Skip to content

Commit

Permalink
Replace jsonpickle with json to serialize entity (#275)
Browse files Browse the repository at this point in the history
* Replace jsonpickle with json to serialize entity

* Added workflow to create release tag

* Pinned sqlalchemy and Flask-SQLAlchemy for unit test

* Fixed version

* Changed log to debug level

* Update logging

* Added empty line
  • Loading branch information
lupengamzn authored Mar 22, 2021
1 parent 508f929 commit 266bc82
Show file tree
Hide file tree
Showing 10 changed files with 640 additions and 61 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/Release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Release X-Ray Python SDK

on:
workflow_dispatch:
inputs:
version:
description: The version to tag the release with, e.g., 1.2.0, 1.3.0
required: true

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout master branch
uses: actions/checkout@v2

- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: '${{ github.event.inputs.version }}'
release_name: '${{ github.event.inputs.version }} Release'
body: 'See details in [CHANGELOG](https://github.com/aws/aws-xray-sdk-python/blob/master/CHANGELOG.rst)'
draft: true
prerelease: false
57 changes: 32 additions & 25 deletions aws_xray_sdk/core/models/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
import time
import string

import jsonpickle
import json

from ..utils.compat import annotation_value_types, string_types
from ..utils.conversion import metadata_to_dict
from .throwable import Throwable
from . import http
from ..exceptions.exceptions import AlreadyEndedException
Expand Down Expand Up @@ -256,36 +257,42 @@ def get_origin_trace_header(self):
def serialize(self):
"""
Serialize to JSON document that can be accepted by the
X-Ray backend service. It uses jsonpickle to perform
serialization.
X-Ray backend service. It uses json to perform serialization.
"""
try:
return jsonpickle.encode(self, unpicklable=False)
return json.dumps(self.to_dict(), default=str)
except Exception:
log.exception("got an exception during serialization")
log.exception("Failed to serialize %s", self.name)

def _delete_empty_properties(self, properties):
def to_dict(self):
"""
Delete empty properties before serialization to avoid
extra keys with empty values in the output json.
Convert Entity(Segment/Subsegment) object to dict
with required properties that have non-empty values.
"""
if not self.parent_id:
del properties['parent_id']
if not self.subsegments:
del properties['subsegments']
if not self.aws:
del properties['aws']
if not self.http:
del properties['http']
if not self.cause:
del properties['cause']
if not self.annotations:
del properties['annotations']
if not self.metadata:
del properties['metadata']
properties.pop(ORIGIN_TRACE_HEADER_ATTR_KEY, None)

del properties['sampled']
entity_dict = {}

for key, value in vars(self).items():
if isinstance(value, bool) or value:
if key == 'subsegments':
# child subsegments are stored as List
subsegments = []
for subsegment in value:
subsegments.append(subsegment.to_dict())
entity_dict[key] = subsegments
elif key == 'cause':
entity_dict[key] = {}
entity_dict[key]['working_directory'] = self.cause['working_directory']
# exceptions are stored as List
throwables = []
for throwable in value['exceptions']:
throwables.append(throwable.to_dict())
entity_dict[key]['exceptions'] = throwables
elif key == 'metadata':
entity_dict[key] = metadata_to_dict(value)
elif key != 'sampled' and key != ORIGIN_TRACE_HEADER_ATTR_KEY:
entity_dict[key] = value

return entity_dict

def _check_ended(self):
if not self.in_progress:
Expand Down
22 changes: 11 additions & 11 deletions aws_xray_sdk/core/models/segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,14 @@ def set_rule_name(self, rule_name):
self.aws['xray'] = {}
self.aws['xray']['sampling_rule_name'] = rule_name

def __getstate__(self):
"""
Used by jsonpikle to remove unwanted fields.
"""
properties = copy.copy(self.__dict__)
super(Segment, self)._delete_empty_properties(properties)
if not self.user:
del properties['user']
del properties['ref_counter']
del properties['_subsegments_counter']
return properties
def to_dict(self):
"""
Convert Segment object to dict with required properties
that have non-empty values.
"""
segment_dict = super(Segment, self).to_dict()
del segment_dict['ref_counter']
del segment_dict['_subsegments_counter']

return segment_dict
19 changes: 10 additions & 9 deletions aws_xray_sdk/core/models/subsegment.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,13 @@ def set_sql(self, sql):
"""
self.sql = sql

def __getstate__(self):

properties = copy.copy(self.__dict__)
super(Subsegment, self)._delete_empty_properties(properties)

del properties['parent_segment']
if not self.sql:
del properties['sql']
return properties
def to_dict(self):
"""
Convert Subsegment object to dict with required properties
that have non-empty values.
"""
subsegment_dict = super(Subsegment, self).to_dict()

del subsegment_dict['parent_segment']

return subsegment_dict
21 changes: 13 additions & 8 deletions aws_xray_sdk/core/models/throwable.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ def __init__(self, exception, stack, remote=False):
if exception:
setattr(exception, '_recorded', True)
setattr(exception, '_cause_id', self.id)

def to_dict(self):
"""
Convert Throwable object to dict with required properties that
have non-empty values.
"""
throwable_dict = {}

for key, value in vars(self).items():
if isinstance(value, bool) or value:
throwable_dict[key] = value

return throwable_dict

def _normalize_stack_trace(self, stack):
if stack is None:
Expand All @@ -66,11 +79,3 @@ def _normalize_stack_trace(self, stack):
normalized['label'] = label.strip()

self.stack.append(normalized)

def __getstate__(self):
properties = copy.copy(self.__dict__)

if not self.stack:
del properties['stack']

return properties
35 changes: 35 additions & 0 deletions aws_xray_sdk/core/utils/conversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import logging

log = logging.getLogger(__name__)

def metadata_to_dict(obj):
"""
Convert object to dict with all serializable properties like:
dict, list, set, tuple, str, bool, int, float, type, object, etc.
"""
try:
if isinstance(obj, dict):
metadata = {}
for key, value in obj.items():
metadata[key] = metadata_to_dict(value)
return metadata
elif isinstance(obj, type):
return str(obj)
elif hasattr(obj, "_ast"):
return metadata_to_dict(obj._ast())
elif hasattr(obj, "__iter__") and not isinstance(obj, str):
metadata = []
for item in obj:
metadata.append(metadata_to_dict(item))
return metadata
elif hasattr(obj, "__dict__"):
metadata = {}
for key, value in vars(obj).items():
if not callable(value) and not key.startswith('_'):
metadata[key] = metadata_to_dict(value)
return metadata
else:
return obj
except Exception:
log.exception("Failed to convert {} to dict".format(str(obj)))
return {}
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
],

install_requires=[
'jsonpickle',
'enum34;python_version<"3.4"',
'wrapt',
'future',
Expand Down
Loading

0 comments on commit 266bc82

Please sign in to comment.