-
Notifications
You must be signed in to change notification settings - Fork 777
LogRecord Resource Serialization #3346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
|
|
||
| from opentelemetry.attributes import BoundedAttributes | ||
| from opentelemetry.sdk._logs import LogLimits, LogRecord | ||
| from opentelemetry.sdk.resources import Resource | ||
|
|
||
|
|
||
| class TestLogRecord(unittest.TestCase): | ||
|
|
@@ -32,7 +33,7 @@ def test_log_record_to_json(self): | |
| "trace_id": "", | ||
| "span_id": "", | ||
| "trace_flags": None, | ||
| "resource": "", | ||
| "resource": None, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be nice to add a test to verify that a non-empty Resource also converts to json correctly.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, certainly agree that coverage was missing. As a first time contributor here I was trying to things slim, but I'm happy to add it: |
||
| }, | ||
| indent=4, | ||
| ) | ||
|
|
@@ -42,6 +43,30 @@ def test_log_record_to_json(self): | |
| ).to_json() | ||
| self.assertEqual(expected, actual) | ||
|
|
||
| def test_log_record_to_json_with_resource(self): | ||
| """Should JSON serialize/deserialize Resource objects within log records.""" | ||
| expected = json.dumps( | ||
| { | ||
| "body": "a log line", | ||
| "severity_number": "None", | ||
| "severity_text": None, | ||
| "attributes": None, | ||
| "timestamp": "1970-01-01T00:00:00.000000Z", | ||
| "trace_id": "", | ||
| "span_id": "", | ||
| "trace_flags": None, | ||
| "resource": {"attributes": {"foo": "bar"}, "schema_url": ""}, | ||
| }, | ||
| indent=4, | ||
| ) | ||
|
|
||
| actual = LogRecord( | ||
| timestamp=0, | ||
| body="a log line", | ||
| resource=Resource(attributes={"foo": "bar"}), | ||
| ).to_json() | ||
| self.assertEqual(expected, actual) | ||
|
|
||
| def test_log_record_bounded_attributes(self): | ||
| attr = {"key": "value"} | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please pass in the indent to
to_json()There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lzchen The serialized JSON is immediately deserialized before being reserialized again as part of the larger data structure. The indent should only matter in terms of consistency for that final serialization process, right?
See comparable
ReadableSpanthat behaves identically:opentelemetry-python/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
Line 493 in 6b9f389
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To add some context, this
to_jsonwas originally added for debugging purposes to be used inConsoleExporter. That's why there is a repr.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lzchen 🎗️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @sernst, what do you think about making a change to -- instead of converting the
Resourceto JSON then immediately converting that JSON to an object -- refactor the code a bit to add a method toResource, e.g.to_dictionarythat returns a dictionary representation that bothto_jsoncan use and that can be used here, e.g.Totally understand that your code here is consistent with the rest of the codebase, and no worries if you feel my suggestion would be out of scope -- we can definitely make these changes in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pmcollins Yeah, I agree that avoiding the conversion to JSON and back with a direct dictionary serialization is a much better way to go. I would suggest that this should go beyond just the resource object. I noticed the pattern heavily used in within the sub-objects of
ReadableSpan,MetricsDataandLogRecordconversion. I think it would make sense to make all of the objects involved in the serialization process capable of dictionary serialization without intermediate JSON serialization.I've just added a second commit to this PR, which does just that:
64946b4
These new changes introduce
to_dictto the objects included in the existing JSON serialization process forReadableSpan,MetricsData,LogRecord, andResourceobjects. This includes addingto_dictto objects that are included within the serialized data structures of these objects. In places whererepr()serialization was used, it has been replaced by a JSON-compatible serialization instead. Inconsistencies between null and empty string values were preserved, but in cases where attributes are optional, an empty dictionary is provided as well to be more consistent with cases where attributes are not optional and an empty dictionary represents no attributes were specified on the containing object.These changes also included:
to_dictmethods for clarity in subsequent usage.DataTandDataPointTwere did not include the exponential histogram types in point.py, and so those were added with newto_jsonandto_dictmethods as well for consistency. It appears that the exponential types were added later and including them in the types might have been overlooked. Please let me know if that is a misunderstanding on my part.to_jsonfunctionality given its redundancy for Python 3.7+ compatibility. I was assuming this was legacy code for previous compatibility, but please let me know if that's not the case as well.to_dictwas added to objects likeSpanContext,Link, andEventthat were previously being serialized by static methods within theReadableSpanclass and accessing private/protected members. This simplified the serialization in theReadableSpanclass and those methods were removed. However, once again, let me know if there was a larger purpose to those I could not find.Finally, I used
to_dictas the method names here to be consistent with other related usages. For example,dataclasses.asdict(). But, mostly because that was by far the most popular usage within the larger community:328k files found on GitHub that define
to_dictfunctions, which include some of the most popular Python libraries to date:https://github.com/search?q=%22def+to_dict%28%22+language%3APython&type=code&p=1&l=Python
versus
3.3k files found on GitHub that define
to_dictionaryfunctions:https://github.com/search?q=%22def+to_dictionary%28%22+language%3APython&type=code&l=Python
However, if there is a preference for this library to use
to_dictionaryinstead let me know and I will adjust.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow awesome solution @sernst! Yes, it appears that
to_dictis a better choice 😄Do you think your second commit warrants its own PR? (I'm not sure what the convention is in this repo). Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Yeah, I'm not sure what the preference would be here either. I'm happy to separate it out into another PR if that is preferred.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes from the second commit discussed here have been moved into a separate PR:
#3365
tracking against #3364
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@srikanthccv In response to yesterday's comment, please see previous on this thread for those topics already being discussed and actions taken. Would have been helpful to have feedback in context rather than a bifurcated thread. Thanks!