Implement custom protoc plugin to generate OTLP JSON class definitions#4910
Implement custom protoc plugin to generate OTLP JSON class definitions#4910herin049 wants to merge 6 commits intoopen-telemetry:mainfrom
Conversation
f66380d to
a9d10c7
Compare
|
I am very much looking forward to this landing: |
codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/analyzer.py
Outdated
Show resolved
Hide resolved
|
I think it would be valuable to add a test that validates the generated JSON by feeding it to |
Just to make sure I understand your suggestion properly, currently I have an integration test which runs the plugin to generate the ProtoJSON Python classes and tests the serialization/deserialization. Are you suggesting that we also generate the corresponding Protobuf definitions and then perform the JSON serialization using the ProtoJSON classes, load the JSON using the Protobuf definitions and then compare the attributes on the ProtoJSON and Protobuf classes? |
4d4119c to
4bce86a
Compare
|
@pmcollins Since we mostly care about serialization for the SDK, I added tests to use |
|
Thanks @herin049. My suggestion is to simulate a Collector receiving JSON. In other words, end-to-end tests that create SDK objects typically sent to an exporter for emission (traces, metrics, and logs), serialize those objects into JSON using the functionality in this PR, send that JSON to our simulated Collector ( |
|
@pmcollins I will be adding the |
|
Makes sense @herin049 -- can be in a subsequent PR. |
4bce86a to
05b459f
Compare
|
@aabmass I have rewritten the commit history to separate out the addition of the |
aabmass
left a comment
There was a problem hiding this comment.
It's a bit large LGTM. I will look over the generated code too
| def encode_repeated( | ||
| values: typing.Optional[list[typing.Any]], | ||
| map_fn: typing.Callable[[typing.Any], typing.Any], | ||
| ) -> list[typing.Any]: |
There was a problem hiding this comment.
nit use generic
def encode_repeated(
values: list[T] | None,
map_fn: typing.Callable[[T], typing.Any],
Comment view) -> list[typing.Any]:|
|
||
| @contextmanager | ||
| def code_generation() -> Iterator[ | ||
| Tuple[plugin.CodeGeneratorRequest, plugin.CodeGeneratorResponse], |
| if proto_type in INT64_TYPES: | ||
| return True | ||
| return proto_type in { |
There was a problem hiding this comment.
I think this can be a single bool expression return ... or ...
There was a problem hiding this comment.
This code LGTM but I would be open to using some off-the-shelf strongly typed lib for this too. Up to you, I'm not sure if something like that exists.
codegen/opentelemetry-codegen-json/src/opentelemetry/codegen/json/generator.py
Show resolved
Hide resolved
| ] | ||
| ) | ||
|
|
||
| sys.path.insert(0, str(gen_path.absolute())) |
There was a problem hiding this comment.
this should be cleaned up after the test, maybe use https://docs.pytest.org/en/stable/reference/reference.html#pytest.MonkeyPatch.syspath_prepend
|
|
||
|
|
||
| def test_unknown_fields_ignored( | ||
| test_v1_types: tuple[Type[Any], Type[Any]], |
There was a problem hiding this comment.
Oh, does this runtime module need to be available to the generated code as a dependency? I.e. they need to install opentelemetry-codegen-json?
| def to_json(self) -> builtins.str: | ||
| """ | ||
| Serialize this message to a JSON string. | ||
|
|
||
| Returns: | ||
| JSON string | ||
| """ | ||
| return json.dumps(self.to_dict()) |
There was a problem hiding this comment.
Would it be reasonable to put this in a base class to try to reduce the code size a bit? Maybe there are downsides, lmk
There was a problem hiding this comment.
I've opted to add a @json_serde class decorator
| """ | ||
| return json.dumps(self.to_dict()) | ||
|
|
||
| @builtins.classmethod |
There was a problem hiding this comment.
Since it's not super obvious to me, what's the need of using builtins? To avoid accidental name clashes?
There was a problem hiding this comment.
Yes, I used this to prevent name clashes. It might be overkill, but I figured it wouldn't hurt. This is also the convention that the mypy protobuf library used which I took inspiration from.
| typeCheckingMode = "standard" | ||
| pythonVersion = "3.9" | ||
|
|
||
| include = [ |
There was a problem hiding this comment.
Can we typecheck the plugin code here, and ideally even the generated code as a sanity check?
MikeGoldsmith
left a comment
There was a problem hiding this comment.
Looks good to me outside of what @aabmass raised. Found one minor typo 👍🏻
| Args: | ||
| snake_name: The field name in snake_case. | ||
| Returns: | ||
| The field name converted to camelCase.k |
There was a problem hiding this comment.
| The field name converted to camelCase.k | |
| The field name converted to camelCase |
Description
This PR implements a custom protoc plugin to automatically generate OTLP JSON class definitions. Consensus was reached deciding that a custom protoc plugin would be the best route for adding support for OTLP JSON while minimizing the number of runtime dependencies (for additional context reference #4886). This PR is a refined version of the first part of a set of changes that are in #4902. Please view the draft PR for an overview of how these packages will be used.
Fixes #1003
Type of change
Please delete options that are not relevant.
How Has This Been Tested?
tox run -e $(tox --listenvs | grep opentelemetry-protojson | tr '\n' ',')Does This PR Require a Contrib Repo Change?
Checklist: