From 5dc009306fd3372c3246fdffd7be7f011781f794 Mon Sep 17 00:00:00 2001 From: alrex Date: Mon, 22 Mar 2021 12:36:28 -0700 Subject: [PATCH] split zipkin exporter into json/proto packages (#1699) --- .flake8 | 2 +- CHANGELOG.md | 4 + docs/exporter/zipkin/zipkin.rst | 14 +- .../opentelemetry/exporter/opencensus/util.py | 6 +- .../CHANGELOG.md | 0 .../LICENSE | 201 +++++++ .../MANIFEST.in | 9 + .../README.rst | 25 + .../setup.cfg | 55 ++ .../setup.py | 32 ++ .../exporter/zipkin/encoder/__init__.py | 5 +- .../exporter/zipkin/json}/__init__.py | 27 +- .../exporter/zipkin/json/v1/__init__.py} | 26 +- .../exporter/zipkin/json/v2/__init__.py} | 0 .../exporter/zipkin/json/version.py | 15 + .../exporter/zipkin/node_endpoint.py | 0 .../tests/__init__.py | 0 .../tests/encoder}/__init__.py | 0 .../tests/encoder/common_tests.py | 0 .../tests/encoder/test_v1_json.py | 2 +- .../tests/encoder/test_v2_json.py | 2 +- .../tests/test_zipkin_exporter.py | 188 +++++++ .../CHANGELOG.md} | 0 .../LICENSE | 201 +++++++ .../MANIFEST.in | 9 + .../README.rst | 25 + .../setup.cfg | 57 ++ .../setup.py | 33 ++ .../exporter/zipkin/proto/http/__init__.py | 142 +++++ .../zipkin/proto/http/v2}/__init__.py | 2 +- .../zipkin/proto/http/v2/gen/__init__.py | 0 .../zipkin/proto/http/v2}/gen/zipkin_pb2.py | 0 .../zipkin/proto/http/v2}/gen/zipkin_pb2.pyi | 0 .../exporter/zipkin/proto/http/version.py | 15 + .../tests/__init__.py | 13 + .../tests/encoder/__init__.py | 0 .../tests/encoder/common_tests.py | 505 ++++++++++++++++++ .../tests/encoder/test_v2_protobuf.py | 4 +- .../tests/test_zipkin_exporter.py | 23 +- .../opentelemetry-exporter-zipkin/README.rst | 10 +- .../opentelemetry-exporter-zipkin/setup.cfg | 17 +- .../exporter/zipkin/encoder/v1/__init__.py | 41 -- .../tests/test_zipkin.py | 27 + pyproject.toml | 2 +- tox.ini | 27 +- 45 files changed, 1662 insertions(+), 104 deletions(-) rename exporter/{opentelemetry-exporter-zipkin => opentelemetry-exporter-zipkin-json}/CHANGELOG.md (100%) create mode 100644 exporter/opentelemetry-exporter-zipkin-json/LICENSE create mode 100644 exporter/opentelemetry-exporter-zipkin-json/MANIFEST.in create mode 100644 exporter/opentelemetry-exporter-zipkin-json/README.rst create mode 100644 exporter/opentelemetry-exporter-zipkin-json/setup.cfg create mode 100644 exporter/opentelemetry-exporter-zipkin-json/setup.py rename exporter/{opentelemetry-exporter-zipkin => opentelemetry-exporter-zipkin-json}/src/opentelemetry/exporter/zipkin/encoder/__init__.py (99%) rename exporter/{opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin => opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json}/__init__.py (85%) rename exporter/{opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v1/json.py => opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v1/__init__.py} (73%) rename exporter/{opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/json.py => opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v2/__init__.py} (100%) create mode 100644 exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version.py rename exporter/{opentelemetry-exporter-zipkin => opentelemetry-exporter-zipkin-json}/src/opentelemetry/exporter/zipkin/node_endpoint.py (100%) rename exporter/{opentelemetry-exporter-zipkin => opentelemetry-exporter-zipkin-json}/tests/__init__.py (100%) rename exporter/{opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/gen => opentelemetry-exporter-zipkin-json/tests/encoder}/__init__.py (100%) rename exporter/{opentelemetry-exporter-zipkin => opentelemetry-exporter-zipkin-json}/tests/encoder/common_tests.py (100%) rename exporter/{opentelemetry-exporter-zipkin => opentelemetry-exporter-zipkin-json}/tests/encoder/test_v1_json.py (99%) rename exporter/{opentelemetry-exporter-zipkin => opentelemetry-exporter-zipkin-json}/tests/encoder/test_v2_json.py (99%) create mode 100644 exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py rename exporter/{opentelemetry-exporter-zipkin/tests/encoder/__init__.py => opentelemetry-exporter-zipkin-proto-http/CHANGELOG.md} (100%) create mode 100644 exporter/opentelemetry-exporter-zipkin-proto-http/LICENSE create mode 100644 exporter/opentelemetry-exporter-zipkin-proto-http/MANIFEST.in create mode 100644 exporter/opentelemetry-exporter-zipkin-proto-http/README.rst create mode 100644 exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg create mode 100644 exporter/opentelemetry-exporter-zipkin-proto-http/setup.py create mode 100644 exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py rename exporter/{opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf => opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2}/__init__.py (98%) create mode 100644 exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/gen/__init__.py rename exporter/{opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf => opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2}/gen/zipkin_pb2.py (100%) rename exporter/{opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf => opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2}/gen/zipkin_pb2.pyi (100%) create mode 100644 exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version.py create mode 100644 exporter/opentelemetry-exporter-zipkin-proto-http/tests/__init__.py create mode 100644 exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/__init__.py create mode 100644 exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/common_tests.py rename exporter/{opentelemetry-exporter-zipkin => opentelemetry-exporter-zipkin-proto-http}/tests/encoder/test_v2_protobuf.py (98%) rename exporter/{opentelemetry-exporter-zipkin => opentelemetry-exporter-zipkin-proto-http}/tests/test_zipkin_exporter.py (90%) delete mode 100644 exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v1/__init__.py create mode 100644 exporter/opentelemetry-exporter-zipkin/tests/test_zipkin.py diff --git a/.flake8 b/.flake8 index 33658400ce0..c66d8690768 100644 --- a/.flake8 +++ b/.flake8 @@ -20,7 +20,7 @@ exclude = exporter/opentelemetry-exporter-jaeger-proto-grpc/build/* exporter/opentelemetry-exporter-jaeger-thrift/src/opentelemetry/exporter/jaeger/thrift/gen/ exporter/opentelemetry-exporter-jaeger-thrift/build/* - exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/gen/ + exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/gen/ docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/grpc/gen/ docs/examples/opentelemetry-example-app/build/* opentelemetry-proto/build/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f43369ea9f..7a8e24366d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#1703](https://github.com/open-telemetry/opentelemetry-python/pull/1703)) - Fixed an unset `OTEL_TRACES_EXPORTER` resulting in an error ([#1707](https://github.com/open-telemetry/opentelemetry-python/pull/1707)) +- Split Zipkin exporter into `opentelemetry-exporter-zipkin-json` and + `opentelemetry-exporter-zipkin-proto-http` packages to reduce dependencies. The + `opentelemetry-exporter-zipkin` installs both. + ([#1699](https://github.com/open-telemetry/opentelemetry-python/pull/1699)) ### Removed - Removed unused `get_hexadecimal_trace_id` and `get_hexadecimal_span_id` methods. diff --git a/docs/exporter/zipkin/zipkin.rst b/docs/exporter/zipkin/zipkin.rst index 18042022a45..a33b7f5de1f 100644 --- a/docs/exporter/zipkin/zipkin.rst +++ b/docs/exporter/zipkin/zipkin.rst @@ -1,7 +1,17 @@ -Opentelemetry Zipkin Exporter -============================= +OpenTelemetry Zipkin Exporters +============================== .. automodule:: opentelemetry.exporter.zipkin :members: :undoc-members: :show-inheritance: + +.. automodule:: opentelemetry.exporter.zipkin.json + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: opentelemetry.exporter.zipkin.proto.http + :members: + :undoc-members: + :show-inheritance: diff --git a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/util.py b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/util.py index 9b9e7201906..e08b884c3fa 100644 --- a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/util.py +++ b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/util.py @@ -82,9 +82,9 @@ def add_proto_attribute_value(pb_attributes, key, value): def get_node(service_name, host_name): """Generates Node message from params and system information. - Args: - service_name: Name of Collector service. - host_name: Host name. + Args: + service_name: Name of Collector service. + host_name: Host name. """ return common_pb2.Node( identifier=common_pb2.ProcessIdentifier( diff --git a/exporter/opentelemetry-exporter-zipkin/CHANGELOG.md b/exporter/opentelemetry-exporter-zipkin-json/CHANGELOG.md similarity index 100% rename from exporter/opentelemetry-exporter-zipkin/CHANGELOG.md rename to exporter/opentelemetry-exporter-zipkin-json/CHANGELOG.md diff --git a/exporter/opentelemetry-exporter-zipkin-json/LICENSE b/exporter/opentelemetry-exporter-zipkin-json/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-json/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/exporter/opentelemetry-exporter-zipkin-json/MANIFEST.in b/exporter/opentelemetry-exporter-zipkin-json/MANIFEST.in new file mode 100644 index 00000000000..aed3e33273b --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-json/MANIFEST.in @@ -0,0 +1,9 @@ +graft src +graft tests +global-exclude *.pyc +global-exclude *.pyo +global-exclude __pycache__/* +include CHANGELOG.md +include MANIFEST.in +include README.rst +include LICENSE diff --git a/exporter/opentelemetry-exporter-zipkin-json/README.rst b/exporter/opentelemetry-exporter-zipkin-json/README.rst new file mode 100644 index 00000000000..cfb7b1fa53d --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-json/README.rst @@ -0,0 +1,25 @@ +OpenTelemetry Zipkin JSON Exporter +================================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-exporter-zipkin-json.svg + :target: https://pypi.org/project/opentelemetry-exporter-zipkin-json/ + +This library allows export of tracing data to `Zipkin `_ using JSON +for serialization. + +Installation +------------ + +:: + + pip install opentelemetry-exporter-zipkin-json + + +References +---------- + +* `OpenTelemetry Zipkin Exporter `_ +* `Zipkin `_ +* `OpenTelemetry Project `_ diff --git a/exporter/opentelemetry-exporter-zipkin-json/setup.cfg b/exporter/opentelemetry-exporter-zipkin-json/setup.cfg new file mode 100644 index 00000000000..0f8a7c900ac --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-json/setup.cfg @@ -0,0 +1,55 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +[metadata] +name = opentelemetry-exporter-zipkin-json +description = Zipkin Span JSON Exporter for OpenTelemetry +long_description = file: README.rst +long_description_content_type = text/x-rst +author = OpenTelemetry Authors +author_email = cncf-opentelemetry-contributors@lists.cncf.io +url = https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/opentelemetry-exporter-zipkin-json +platforms = any +license = Apache-2.0 +classifiers = + Development Status :: 4 - Beta + Intended Audience :: Developers + License :: OSI Approved :: Apache Software License + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + +[options] +python_requires = >=3.5 +package_dir= + =src +packages=find_namespace: +install_requires = + requests ~= 2.7 + opentelemetry-api == 1.0.0.dev0 + opentelemetry-sdk == 1.0.0.dev0 + +[options.packages.find] +where = src + +[options.extras_require] +test = + +[options.entry_points] +opentelemetry_exporter = + zipkin_json = opentelemetry.exporter.zipkin.json:ZipkinExporter \ No newline at end of file diff --git a/exporter/opentelemetry-exporter-zipkin-json/setup.py b/exporter/opentelemetry-exporter-zipkin-json/setup.py new file mode 100644 index 00000000000..1e8b53811c5 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-json/setup.py @@ -0,0 +1,32 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os + +import setuptools + +BASE_DIR = os.path.dirname(__file__) +VERSION_FILENAME = os.path.join( + BASE_DIR, + "src", + "opentelemetry", + "exporter", + "zipkin", + "json", + "version.py", +) +PACKAGE_INFO = {} +with open(VERSION_FILENAME) as f: + exec(f.read(), PACKAGE_INFO) + +setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/__init__.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/encoder/__init__.py similarity index 99% rename from exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/__init__.py rename to exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/encoder/__init__.py index 8705a7bc372..3390c19a467 100644 --- a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/__init__.py +++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/encoder/__init__.py @@ -49,9 +49,8 @@ class Protocol(Enum): OS environ var OTEL_EXPORTER_ZIPKIN_PROTOCOL (reserved for future usage). """ - V1_JSON = "v1_json" - V2_JSON = "v2_json" - V2_PROTOBUF = "v2_protobuf" + V1 = "v1" + V2 = "v2" # pylint: disable=W0223 diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py similarity index 85% rename from exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py rename to exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py index afb29b239f0..55e3a565ec0 100644 --- a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py +++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py @@ -13,15 +13,17 @@ # limitations under the License. """ +OpenTelemetry Zipkin JSON Exporter +---------------------------------- + This library allows to export tracing data to `Zipkin `_. Usage ----- -The **OpenTelemetry Zipkin Exporter** allows exporting of `OpenTelemetry`_ +The **OpenTelemetry Zipkin JSON Exporter** allows exporting of `OpenTelemetry`_ traces to `Zipkin`_. This exporter sends traces to the configured Zipkin -collector endpoint using HTTP and supports multiple protocols (v1 json, -v2 json, v2 protobuf). +collector endpoint using JSON over HTTP and supports multiple versions (v1, v2). .. _Zipkin: https://zipkin.io/ .. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/ @@ -30,7 +32,7 @@ .. code:: python from opentelemetry import trace - from opentelemetry.exporter import zipkin + from opentelemetry.exporter.zipkin.json import ZipkinExporter from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor @@ -38,8 +40,8 @@ tracer = trace.get_tracer(__name__) # create a ZipkinExporter - zipkin_exporter = zipkin.ZipkinExporter( - # protocol=Protocol.V2_PROTOBUF + zipkin_exporter = ZipkinExporter( + # version=Protocol.V2 # optional: # endpoint="http://localhost:9411/api/v2/spans", # local_node_ipv4="192.168.0.1", @@ -76,9 +78,8 @@ Encoder, Protocol, ) -from opentelemetry.exporter.zipkin.encoder.v1.json import JsonV1Encoder -from opentelemetry.exporter.zipkin.encoder.v2.json import JsonV2Encoder -from opentelemetry.exporter.zipkin.encoder.v2.protobuf import ProtobufEncoder +from opentelemetry.exporter.zipkin.json.v1 import JsonV1Encoder +from opentelemetry.exporter.zipkin.json.v2 import JsonV2Encoder from opentelemetry.exporter.zipkin.node_endpoint import IpInput, NodeEndpoint from opentelemetry.sdk.environment_variables import ( OTEL_EXPORTER_ZIPKIN_ENDPOINT, @@ -96,7 +97,7 @@ class ZipkinExporter(SpanExporter): def __init__( self, - protocol: Protocol, + version: Protocol = Protocol.V2, endpoint: Optional[str] = None, local_node_ipv4: IpInput = None, local_node_ipv6: IpInput = None, @@ -113,12 +114,10 @@ def __init__( ) self.endpoint = endpoint - if protocol == Protocol.V1_JSON: + if version == Protocol.V1: self.encoder = JsonV1Encoder(max_tag_value_length) - elif protocol == Protocol.V2_JSON: + elif version == Protocol.V2: self.encoder = JsonV2Encoder(max_tag_value_length) - elif protocol == Protocol.V2_PROTOBUF: - self.encoder = ProtobufEncoder(max_tag_value_length) def export(self, spans: Sequence[Span]) -> SpanExportResult: # Populate service_name from first span diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v1/json.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v1/__init__.py similarity index 73% rename from exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v1/json.py rename to exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v1/__init__.py index 966657311bb..2fcb4ab1680 100644 --- a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v1/json.py +++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v1/__init__.py @@ -14,13 +14,33 @@ """Zipkin Export Encoders for JSON formats """ -from typing import Dict -from opentelemetry.exporter.zipkin.encoder import JsonEncoder -from opentelemetry.exporter.zipkin.encoder.v1 import V1Encoder +import abc +from typing import Dict, List + +from opentelemetry.exporter.zipkin.encoder import Encoder, JsonEncoder from opentelemetry.trace import Span +# pylint: disable=W0223 +class V1Encoder(Encoder): + def _extract_binary_annotations( + self, span: Span, encoded_local_endpoint: Dict + ) -> List[Dict]: + binary_annotations = [] + for tag_key, tag_value in self._extract_tags_from_span(span).items(): + if isinstance(tag_value, str) and self.max_tag_value_length > 0: + tag_value = tag_value[: self.max_tag_value_length] + binary_annotations.append( + { + "key": tag_key, + "value": tag_value, + "endpoint": encoded_local_endpoint, + } + ) + return binary_annotations + + class JsonV1Encoder(JsonEncoder, V1Encoder): """Zipkin Export Encoder for JSON v1 API diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/json.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v2/__init__.py similarity index 100% rename from exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/json.py rename to exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/v2/__init__.py diff --git a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version.py new file mode 100644 index 00000000000..e4529dffc64 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version.py @@ -0,0 +1,15 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__version__ = "1.0.0.dev0" diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/node_endpoint.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/node_endpoint.py similarity index 100% rename from exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/node_endpoint.py rename to exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/node_endpoint.py diff --git a/exporter/opentelemetry-exporter-zipkin/tests/__init__.py b/exporter/opentelemetry-exporter-zipkin-json/tests/__init__.py similarity index 100% rename from exporter/opentelemetry-exporter-zipkin/tests/__init__.py rename to exporter/opentelemetry-exporter-zipkin-json/tests/__init__.py diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/gen/__init__.py b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/__init__.py similarity index 100% rename from exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/gen/__init__.py rename to exporter/opentelemetry-exporter-zipkin-json/tests/encoder/__init__.py diff --git a/exporter/opentelemetry-exporter-zipkin/tests/encoder/common_tests.py b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/common_tests.py similarity index 100% rename from exporter/opentelemetry-exporter-zipkin/tests/encoder/common_tests.py rename to exporter/opentelemetry-exporter-zipkin-json/tests/encoder/common_tests.py diff --git a/exporter/opentelemetry-exporter-zipkin/tests/encoder/test_v1_json.py b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v1_json.py similarity index 99% rename from exporter/opentelemetry-exporter-zipkin/tests/encoder/test_v1_json.py rename to exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v1_json.py index c8cdb0edac6..c1956500bcf 100644 --- a/exporter/opentelemetry-exporter-zipkin/tests/encoder/test_v1_json.py +++ b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v1_json.py @@ -15,7 +15,7 @@ from opentelemetry import trace as trace_api from opentelemetry.exporter.zipkin.encoder import NAME_KEY, VERSION_KEY -from opentelemetry.exporter.zipkin.encoder.v1.json import JsonV1Encoder +from opentelemetry.exporter.zipkin.json.v1 import JsonV1Encoder from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint from opentelemetry.sdk import trace from opentelemetry.trace import TraceFlags, format_span_id, format_trace_id diff --git a/exporter/opentelemetry-exporter-zipkin/tests/encoder/test_v2_json.py b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v2_json.py similarity index 99% rename from exporter/opentelemetry-exporter-zipkin/tests/encoder/test_v2_json.py rename to exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v2_json.py index 369eef68952..eb0ad6000aa 100644 --- a/exporter/opentelemetry-exporter-zipkin/tests/encoder/test_v2_json.py +++ b/exporter/opentelemetry-exporter-zipkin-json/tests/encoder/test_v2_json.py @@ -15,7 +15,7 @@ from opentelemetry import trace as trace_api from opentelemetry.exporter.zipkin.encoder import NAME_KEY, VERSION_KEY -from opentelemetry.exporter.zipkin.encoder.v2.json import JsonV2Encoder +from opentelemetry.exporter.zipkin.json.v2 import JsonV2Encoder from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint from opentelemetry.sdk import trace from opentelemetry.trace import SpanKind, TraceFlags diff --git a/exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py b/exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py new file mode 100644 index 00000000000..aa2ad9e6255 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py @@ -0,0 +1,188 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import ipaddress +import os +import unittest +from unittest.mock import patch + +from opentelemetry import trace +from opentelemetry.exporter.zipkin.encoder import Protocol +from opentelemetry.exporter.zipkin.json import DEFAULT_ENDPOINT, ZipkinExporter +from opentelemetry.exporter.zipkin.json.v2 import JsonV2Encoder +from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint +from opentelemetry.sdk.environment_variables import ( + OTEL_EXPORTER_ZIPKIN_ENDPOINT, +) +from opentelemetry.sdk.resources import SERVICE_NAME, Resource +from opentelemetry.sdk.trace import TracerProvider, _Span +from opentelemetry.sdk.trace.export import SpanExportResult + +TEST_SERVICE_NAME = "test_service" + + +class MockResponse: + def __init__(self, status_code): + self.status_code = status_code + self.text = status_code + + +class TestZipkinExporter(unittest.TestCase): + @classmethod + def setUpClass(cls): + trace.set_tracer_provider( + TracerProvider( + resource=Resource({SERVICE_NAME: TEST_SERVICE_NAME}) + ) + ) + + def tearDown(self): + if OTEL_EXPORTER_ZIPKIN_ENDPOINT in os.environ: + del os.environ[OTEL_EXPORTER_ZIPKIN_ENDPOINT] + + def test_constructor_default(self): + exporter = ZipkinExporter() + self.assertIsInstance(exporter.encoder, JsonV2Encoder) + self.assertEqual(exporter.endpoint, DEFAULT_ENDPOINT) + self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) + self.assertEqual(exporter.local_node.ipv4, None) + self.assertEqual(exporter.local_node.ipv6, None) + self.assertEqual(exporter.local_node.port, None) + + def test_constructor_env_vars(self): + os_endpoint = "https://foo:9911/path" + os.environ[OTEL_EXPORTER_ZIPKIN_ENDPOINT] = os_endpoint + + exporter = ZipkinExporter() + + self.assertEqual(exporter.endpoint, os_endpoint) + self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) + self.assertEqual(exporter.local_node.ipv4, None) + self.assertEqual(exporter.local_node.ipv6, None) + self.assertEqual(exporter.local_node.port, None) + + def test_constructor_protocol_endpoint(self): + """Test the constructor for the common usage of providing the + protocol and endpoint arguments.""" + endpoint = "https://opentelemetry.io:15875/myapi/traces?format=zipkin" + + exporter = ZipkinExporter(endpoint=endpoint) + + self.assertIsInstance(exporter.encoder, JsonV2Encoder) + self.assertEqual(exporter.endpoint, endpoint) + self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) + self.assertEqual(exporter.local_node.ipv4, None) + self.assertEqual(exporter.local_node.ipv6, None) + self.assertEqual(exporter.local_node.port, None) + + def test_constructor_all_params_and_env_vars(self): + """Test the scenario where all params are provided and all OS env + vars are set. Explicit params should take precedence. + """ + os_endpoint = "https://os.env.param:9911/path" + os.environ[OTEL_EXPORTER_ZIPKIN_ENDPOINT] = os_endpoint + + constructor_param_version = Protocol.V2 + constructor_param_endpoint = "https://constructor.param:9911/path" + local_node_ipv4 = "192.168.0.1" + local_node_ipv6 = "2001:db8::1000" + local_node_port = 30301 + max_tag_value_length = 56 + + exporter = ZipkinExporter( + constructor_param_version, + constructor_param_endpoint, + local_node_ipv4, + local_node_ipv6, + local_node_port, + max_tag_value_length, + ) + + self.assertIsInstance(exporter.encoder, JsonV2Encoder) + self.assertEqual(exporter.endpoint, constructor_param_endpoint) + self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) + self.assertEqual( + exporter.local_node.ipv4, ipaddress.IPv4Address(local_node_ipv4) + ) + self.assertEqual( + exporter.local_node.ipv6, ipaddress.IPv6Address(local_node_ipv6) + ) + self.assertEqual(exporter.local_node.port, local_node_port) + + @patch("requests.post") + def test_export_success(self, mock_post): + mock_post.return_value = MockResponse(200) + spans = [] + exporter = ZipkinExporter() + status = exporter.export(spans) + self.assertEqual(SpanExportResult.SUCCESS, status) + + @patch("requests.post") + def test_export_invalid_response(self, mock_post): + mock_post.return_value = MockResponse(404) + spans = [] + exporter = ZipkinExporter() + status = exporter.export(spans) + self.assertEqual(SpanExportResult.FAILURE, status) + + @patch("requests.post") + def test_export_span_service_name(self, mock_post): + mock_post.return_value = MockResponse(200) + resource = Resource.create({SERVICE_NAME: "test"}) + context = trace.SpanContext( + trace_id=0x000000000000000000000000DEADBEEF, + span_id=0x00000000DEADBEF0, + is_remote=False, + ) + span = _Span("test_span", context=context, resource=resource) + span.start() + span.end() + exporter = ZipkinExporter() + exporter.export([span]) + self.assertEqual(exporter.local_node.service_name, "test") + + +class TestZipkinNodeEndpoint(unittest.TestCase): + def test_constructor_default(self): + node_endpoint = NodeEndpoint() + self.assertEqual(node_endpoint.ipv4, None) + self.assertEqual(node_endpoint.ipv6, None) + self.assertEqual(node_endpoint.port, None) + self.assertEqual(node_endpoint.service_name, TEST_SERVICE_NAME) + + def test_constructor_explicits(self): + ipv4 = "192.168.0.1" + ipv6 = "2001:db8::c001" + port = 414120 + node_endpoint = NodeEndpoint(ipv4, ipv6, port) + self.assertEqual(node_endpoint.ipv4, ipaddress.IPv4Address(ipv4)) + self.assertEqual(node_endpoint.ipv6, ipaddress.IPv6Address(ipv6)) + self.assertEqual(node_endpoint.port, port) + self.assertEqual(node_endpoint.service_name, TEST_SERVICE_NAME) + + def test_ipv4_invalid_raises_error(self): + with self.assertRaises(ValueError): + NodeEndpoint(ipv4="invalid-ipv4-address") + + def test_ipv4_passed_ipv6_raises_error(self): + with self.assertRaises(ValueError): + NodeEndpoint(ipv4="2001:db8::c001") + + def test_ipv6_invalid_raises_error(self): + with self.assertRaises(ValueError): + NodeEndpoint(ipv6="invalid-ipv6-address") + + def test_ipv6_passed_ipv4_raises_error(self): + with self.assertRaises(ValueError): + NodeEndpoint(ipv6="192.168.0.1") diff --git a/exporter/opentelemetry-exporter-zipkin/tests/encoder/__init__.py b/exporter/opentelemetry-exporter-zipkin-proto-http/CHANGELOG.md similarity index 100% rename from exporter/opentelemetry-exporter-zipkin/tests/encoder/__init__.py rename to exporter/opentelemetry-exporter-zipkin-proto-http/CHANGELOG.md diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/LICENSE b/exporter/opentelemetry-exporter-zipkin-proto-http/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/MANIFEST.in b/exporter/opentelemetry-exporter-zipkin-proto-http/MANIFEST.in new file mode 100644 index 00000000000..aed3e33273b --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/MANIFEST.in @@ -0,0 +1,9 @@ +graft src +graft tests +global-exclude *.pyc +global-exclude *.pyo +global-exclude __pycache__/* +include CHANGELOG.md +include MANIFEST.in +include README.rst +include LICENSE diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/README.rst b/exporter/opentelemetry-exporter-zipkin-proto-http/README.rst new file mode 100644 index 00000000000..12801dbf377 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/README.rst @@ -0,0 +1,25 @@ +OpenTelemetry Zipkin Protobuf Exporter +====================================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-exporter-zipkin-proto-http.svg + :target: https://pypi.org/project/opentelemetry-exporter-zipkin-proto-http/ + +This library allows export of tracing data to `Zipkin `_ using Protobuf +for serialization. + +Installation +------------ + +:: + + pip install opentelemetry-exporter-zipkin-proto-http + + +References +---------- + +* `OpenTelemetry Zipkin Exporter `_ +* `Zipkin `_ +* `OpenTelemetry Project `_ diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg b/exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg new file mode 100644 index 00000000000..96e67653a76 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg @@ -0,0 +1,57 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +[metadata] +name = opentelemetry-exporter-zipkin-proto-http +description = Zipkin Span Protobuf Exporter for OpenTelemetry +long_description = file: README.rst +long_description_content_type = text/x-rst +author = OpenTelemetry Authors +author_email = cncf-opentelemetry-contributors@lists.cncf.io +url = https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/opentelemetry-exporter-zipkin-proto-http +platforms = any +license = Apache-2.0 +classifiers = + Development Status :: 4 - Beta + Intended Audience :: Developers + License :: OSI Approved :: Apache Software License + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + +[options] +python_requires = >=3.5 +package_dir= + =src +packages=find_namespace: +install_requires = + protobuf >= 3.12 + requests ~= 2.7 + opentelemetry-api == 1.0.0.dev0 + opentelemetry-sdk == 1.0.0.dev0 + opentelemetry-exporter-zipkin-json == 1.0.0.dev0 + +[options.packages.find] +where = src + +[options.extras_require] +test = + +[options.entry_points] +opentelemetry_exporter = + zipkin_proto = opentelemetry.exporter.zipkin.proto.http:ZipkinExporter \ No newline at end of file diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/setup.py b/exporter/opentelemetry-exporter-zipkin-proto-http/setup.py new file mode 100644 index 00000000000..707e6c1a121 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/setup.py @@ -0,0 +1,33 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os + +import setuptools + +BASE_DIR = os.path.dirname(__file__) +VERSION_FILENAME = os.path.join( + BASE_DIR, + "src", + "opentelemetry", + "exporter", + "zipkin", + "proto", + "http", + "version.py", +) +PACKAGE_INFO = {} +with open(VERSION_FILENAME) as f: + exec(f.read(), PACKAGE_INFO) + +setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py new file mode 100644 index 00000000000..fbc8cc03cf3 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py @@ -0,0 +1,142 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +OpenTelemetry Zipkin Protobuf Exporter +-------------------------------------- + +This library allows to export tracing data to `Zipkin `_. + +Usage +----- + +The **OpenTelemetry Zipkin Exporter** allows exporting of `OpenTelemetry`_ +traces to `Zipkin`_. This exporter sends traces to the configured Zipkin +collector endpoint using HTTP and supports v2 protobuf. + +.. _Zipkin: https://zipkin.io/ +.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/ +.. _Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#zipkin-exporter + +.. code:: python + + from opentelemetry import trace + from opentelemetry.exporter.zipkin.proto.http import ZipkinExporter + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import BatchSpanProcessor + + trace.set_tracer_provider(TracerProvider()) + tracer = trace.get_tracer(__name__) + + # create a ZipkinExporter + zipkin_exporter = ZipkinExporter( + # optional: + # endpoint="http://localhost:9411/api/v2/spans", + # local_node_ipv4="192.168.0.1", + # local_node_ipv6="2001:db8::c001", + # local_node_port=31313, + # max_tag_value_length=256 + ) + + # Create a BatchSpanProcessor and add the exporter to it + span_processor = BatchSpanProcessor(zipkin_exporter) + + # add to the tracer + trace.get_tracer_provider().add_span_processor(span_processor) + + with tracer.start_as_current_span("foo"): + print("Hello world!") + +The exporter supports the following environment variable for configuration: + +- :envvar:`OTEL_EXPORTER_ZIPKIN_ENDPOINT` + +API +--- +""" + +import logging +from os import environ +from typing import Optional, Sequence + +import requests + +from opentelemetry.exporter.zipkin.encoder import ( + DEFAULT_MAX_TAG_VALUE_LENGTH, + Encoder, + Protocol, +) +from opentelemetry.exporter.zipkin.proto.http.v2 import ProtobufEncoder +from opentelemetry.exporter.zipkin.node_endpoint import IpInput, NodeEndpoint +from opentelemetry.sdk.environment_variables import ( + OTEL_EXPORTER_ZIPKIN_ENDPOINT, +) +from opentelemetry.sdk.resources import SERVICE_NAME +from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult +from opentelemetry.trace import Span + +DEFAULT_ENDPOINT = "http://localhost:9411/api/v2/spans" +REQUESTS_SUCCESS_STATUS_CODES = (200, 202) + +logger = logging.getLogger(__name__) + + +class ZipkinExporter(SpanExporter): + def __init__( + self, + endpoint: Optional[str] = None, + local_node_ipv4: IpInput = None, + local_node_ipv6: IpInput = None, + local_node_port: Optional[int] = None, + max_tag_value_length: Optional[int] = None, + ): + self.local_node = NodeEndpoint( + local_node_ipv4, local_node_ipv6, local_node_port + ) + + if endpoint is None: + endpoint = ( + environ.get(OTEL_EXPORTER_ZIPKIN_ENDPOINT) or DEFAULT_ENDPOINT + ) + self.endpoint = endpoint + + self.encoder = ProtobufEncoder(max_tag_value_length) + + def export(self, spans: Sequence[Span]) -> SpanExportResult: + # Populate service_name from first span + # We restrict any SpanProcessor to be only associated with a single + # TracerProvider, so it is safe to assume that all Spans in a single + # batch all originate from one TracerProvider (and in turn have all + # the same service.name) + if spans: + service_name = spans[0].resource.attributes.get(SERVICE_NAME) + if service_name: + self.local_node.service_name = service_name + result = requests.post( + url=self.endpoint, + data=self.encoder.serialize(spans, self.local_node), + headers={"Content-Type": self.encoder.content_type()}, + ) + + if result.status_code not in REQUESTS_SUCCESS_STATUS_CODES: + logger.error( + "Traces cannot be uploaded; status code: %s, message %s", + result.status_code, + result.text, + ) + return SpanExportResult.FAILURE + return SpanExportResult.SUCCESS + + def shutdown(self) -> None: + pass diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/__init__.py b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/__init__.py similarity index 98% rename from exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/__init__.py rename to exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/__init__.py index f0cdb65b37c..b54761e166c 100644 --- a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/__init__.py +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/__init__.py @@ -19,7 +19,7 @@ from typing import List, Optional, Sequence from opentelemetry.exporter.zipkin.encoder import Encoder -from opentelemetry.exporter.zipkin.encoder.v2.protobuf.gen import zipkin_pb2 +from opentelemetry.exporter.zipkin.proto.http.v2.gen import zipkin_pb2 from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint from opentelemetry.sdk.trace import Event from opentelemetry.trace import Span, SpanContext, SpanKind diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/gen/__init__.py b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/gen/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/gen/zipkin_pb2.py b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/gen/zipkin_pb2.py similarity index 100% rename from exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/gen/zipkin_pb2.py rename to exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/gen/zipkin_pb2.py diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/gen/zipkin_pb2.pyi b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/gen/zipkin_pb2.pyi similarity index 100% rename from exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/gen/zipkin_pb2.pyi rename to exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/gen/zipkin_pb2.pyi diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version.py b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version.py new file mode 100644 index 00000000000..e4529dffc64 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version.py @@ -0,0 +1,15 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__version__ = "1.0.0.dev0" diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/__init__.py b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/__init__.py new file mode 100644 index 00000000000..b0a6f428417 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/__init__.py b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/common_tests.py b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/common_tests.py new file mode 100644 index 00000000000..a04148b6ea4 --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/common_tests.py @@ -0,0 +1,505 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import abc +import json +import sys +import unittest +from typing import Dict, List + +from opentelemetry import trace as trace_api +from opentelemetry.exporter.zipkin.encoder import ( + DEFAULT_MAX_TAG_VALUE_LENGTH, + Encoder, +) +from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint +from opentelemetry.sdk import trace +from opentelemetry.sdk.util.instrumentation import InstrumentationInfo +from opentelemetry.trace import TraceFlags +from opentelemetry.trace.status import Status, StatusCode + +TEST_SERVICE_NAME = "test_service" + + +# pylint: disable=protected-access +class CommonEncoderTestCases: + class CommonEncoderTest(unittest.TestCase): + @staticmethod + @abc.abstractmethod + def get_encoder(*args, **kwargs) -> Encoder: + pass + + @classmethod + def get_encoder_default(cls) -> Encoder: + return cls.get_encoder() + + @abc.abstractmethod + def test_encode_trace_id(self): + pass + + @abc.abstractmethod + def test_encode_span_id(self): + pass + + @abc.abstractmethod + def test_encode_local_endpoint_default(self): + pass + + @abc.abstractmethod + def test_encode_local_endpoint_explicits(self): + pass + + @abc.abstractmethod + def _test_encode_max_tag_length(self, max_tag_value_length: int): + pass + + def test_encode_max_tag_length_2(self): + self._test_encode_max_tag_length(2) + + def test_encode_max_tag_length_5(self): + self._test_encode_max_tag_length(5) + + def test_encode_max_tag_length_9(self): + self._test_encode_max_tag_length(9) + + def test_encode_max_tag_length_10(self): + self._test_encode_max_tag_length(10) + + def test_encode_max_tag_length_11(self): + self._test_encode_max_tag_length(11) + + def test_encode_max_tag_length_128(self): + self._test_encode_max_tag_length(128) + + def test_constructor_default(self): + encoder = self.get_encoder() + + self.assertEqual( + DEFAULT_MAX_TAG_VALUE_LENGTH, encoder.max_tag_value_length + ) + + def test_constructor_max_tag_value_length(self): + max_tag_value_length = 123456 + encoder = self.get_encoder(max_tag_value_length) + self.assertEqual( + max_tag_value_length, encoder.max_tag_value_length + ) + + def test_nsec_to_usec_round(self): + base_time_nsec = 683647322 * 10 ** 9 + for nsec in ( + base_time_nsec, + base_time_nsec + 150 * 10 ** 6, + base_time_nsec + 300 * 10 ** 6, + base_time_nsec + 400 * 10 ** 6, + ): + self.assertEqual( + (nsec + 500) // 10 ** 3, + self.get_encoder_default()._nsec_to_usec_round(nsec), + ) + + def test_encode_debug(self): + self.assertFalse( + self.get_encoder_default()._encode_debug( + trace_api.SpanContext( + trace_id=0x000000000000000000000000DEADBEEF, + span_id=0x00000000DEADBEF0, + is_remote=False, + trace_flags=TraceFlags(TraceFlags.DEFAULT), + ) + ) + ) + self.assertTrue( + self.get_encoder_default()._encode_debug( + trace_api.SpanContext( + trace_id=0x000000000000000000000000DEADBEEF, + span_id=0x00000000DEADBEF0, + is_remote=False, + trace_flags=TraceFlags(TraceFlags.SAMPLED), + ) + ) + ) + + def test_get_parent_id_from_span(self): + parent_id = 0x00000000DEADBEF0 + self.assertEqual( + parent_id, + self.get_encoder_default()._get_parent_id( + trace._Span( + name="test-span", + context=trace_api.SpanContext( + 0x000000000000000000000000DEADBEEF, + 0x04BF92DEEFC58C92, + is_remote=False, + ), + parent=trace_api.SpanContext( + 0x0000000000000000000000AADEADBEEF, + parent_id, + is_remote=False, + ), + ) + ), + ) + + def test_get_parent_id_from_span_context(self): + parent_id = 0x00000000DEADBEF0 + self.assertEqual( + parent_id, + self.get_encoder_default()._get_parent_id( + trace_api.SpanContext( + trace_id=0x000000000000000000000000DEADBEEF, + span_id=parent_id, + is_remote=False, + ), + ), + ) + + @staticmethod + def get_data_for_max_tag_length_test( + max_tag_length: int, + ) -> (trace._Span, Dict): + start_time = 683647322 * 10 ** 9 # in ns + duration = 50 * 10 ** 6 + end_time = start_time + duration + + span = trace._Span( + name=TEST_SERVICE_NAME, + context=trace_api.SpanContext( + 0x0E0C63257DE34C926F9EFCD03927272E, + 0x04BF92DEEFC58C92, + is_remote=False, + trace_flags=TraceFlags(TraceFlags.SAMPLED), + ), + resource=trace.Resource({}), + ) + span.start(start_time=start_time) + span.set_attribute("string1", "v" * 500) + span.set_attribute("string2", "v" * 50) + span.set_attribute("list1", ["a"] * 25) + span.set_attribute("list2", ["a"] * 10) + span.set_attribute("list3", [2] * 25) + span.set_attribute("list4", [2] * 10) + span.set_attribute("list5", [True] * 25) + span.set_attribute("list6", [True] * 10) + span.set_attribute("tuple1", ("a",) * 25) + span.set_attribute("tuple2", ("a",) * 10) + span.set_attribute("tuple3", (2,) * 25) + span.set_attribute("tuple4", (2,) * 10) + span.set_attribute("tuple5", (True,) * 25) + span.set_attribute("tuple6", (True,) * 10) + span.set_attribute("range1", range(0, 25)) + span.set_attribute("range2", range(0, 10)) + span.set_attribute("empty_list", []) + span.set_attribute("none_list", ["hello", None, "world"]) + span.end(end_time=end_time) + + expected_outputs = { + 2: { + "string1": "vv", + "string2": "vv", + "list1": "[]", + "list2": "[]", + "list3": "[]", + "list4": "[]", + "list5": "[]", + "list6": "[]", + "tuple1": "[]", + "tuple2": "[]", + "tuple3": "[]", + "tuple4": "[]", + "tuple5": "[]", + "tuple6": "[]", + "range1": "[]", + "range2": "[]", + "empty_list": "[]", + "none_list": "[]", + }, + 5: { + "string1": "vvvvv", + "string2": "vvvvv", + "list1": '["a"]', + "list2": '["a"]', + "list3": '["2"]', + "list4": '["2"]', + "list5": "[]", + "list6": "[]", + "tuple1": '["a"]', + "tuple2": '["a"]', + "tuple3": '["2"]', + "tuple4": '["2"]', + "tuple5": "[]", + "tuple6": "[]", + "range1": '["0"]', + "range2": '["0"]', + "empty_list": "[]", + "none_list": "[]", + }, + 9: { + "string1": "vvvvvvvvv", + "string2": "vvvvvvvvv", + "list1": '["a","a"]', + "list2": '["a","a"]', + "list3": '["2","2"]', + "list4": '["2","2"]', + "list5": '["true"]', + "list6": '["true"]', + "tuple1": '["a","a"]', + "tuple2": '["a","a"]', + "tuple3": '["2","2"]', + "tuple4": '["2","2"]', + "tuple5": '["true"]', + "tuple6": '["true"]', + "range1": '["0","1"]', + "range2": '["0","1"]', + "empty_list": "[]", + "none_list": '["hello"]', + }, + 10: { + "string1": "vvvvvvvvvv", + "string2": "vvvvvvvvvv", + "list1": '["a","a"]', + "list2": '["a","a"]', + "list3": '["2","2"]', + "list4": '["2","2"]', + "list5": '["true"]', + "list6": '["true"]', + "tuple1": '["a","a"]', + "tuple2": '["a","a"]', + "tuple3": '["2","2"]', + "tuple4": '["2","2"]', + "tuple5": '["true"]', + "tuple6": '["true"]', + "range1": '["0","1"]', + "range2": '["0","1"]', + "empty_list": "[]", + "none_list": '["hello"]', + }, + 11: { + "string1": "vvvvvvvvvvv", + "string2": "vvvvvvvvvvv", + "list1": '["a","a"]', + "list2": '["a","a"]', + "list3": '["2","2"]', + "list4": '["2","2"]', + "list5": '["true"]', + "list6": '["true"]', + "tuple1": '["a","a"]', + "tuple2": '["a","a"]', + "tuple3": '["2","2"]', + "tuple4": '["2","2"]', + "tuple5": '["true"]', + "tuple6": '["true"]', + "range1": '["0","1"]', + "range2": '["0","1"]', + "empty_list": "[]", + "none_list": '["hello"]', + }, + 128: { + "string1": "v" * 128, + "string2": "v" * 50, + "list1": '["a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"]', + "list2": '["a","a","a","a","a","a","a","a","a","a"]', + "list3": '["2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2"]', + "list4": '["2","2","2","2","2","2","2","2","2","2"]', + "list5": '["true","true","true","true","true","true","true","true","true","true","true","true","true","true","true","true","true","true"]', + "list6": '["true","true","true","true","true","true","true","true","true","true"]', + "tuple1": '["a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"]', + "tuple2": '["a","a","a","a","a","a","a","a","a","a"]', + "tuple3": '["2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2","2"]', + "tuple4": '["2","2","2","2","2","2","2","2","2","2"]', + "tuple5": '["true","true","true","true","true","true","true","true","true","true","true","true","true","true","true","true","true","true"]', + "tuple6": '["true","true","true","true","true","true","true","true","true","true"]', + "range1": '["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24"]', + "range2": '["0","1","2","3","4","5","6","7","8","9"]', + "empty_list": "[]", + "none_list": '["hello",null,"world"]', + }, + } + + return span, expected_outputs[max_tag_length] + + @staticmethod + def get_exhaustive_otel_span_list() -> List[trace._Span]: + trace_id = 0x6E0C63257DE34C926F9EFCD03927272E + + base_time = 683647322 * 10 ** 9 # in ns + start_times = ( + base_time, + base_time + 150 * 10 ** 6, + base_time + 300 * 10 ** 6, + base_time + 400 * 10 ** 6, + ) + end_times = ( + start_times[0] + (50 * 10 ** 6), + start_times[1] + (100 * 10 ** 6), + start_times[2] + (200 * 10 ** 6), + start_times[3] + (300 * 10 ** 6), + ) + + parent_span_context = trace_api.SpanContext( + trace_id, 0x1111111111111111, is_remote=False + ) + + other_context = trace_api.SpanContext( + trace_id, 0x2222222222222222, is_remote=False + ) + + span1 = trace._Span( + name="test-span-1", + context=trace_api.SpanContext( + trace_id, + 0x34BF92DEEFC58C92, + is_remote=False, + trace_flags=TraceFlags(TraceFlags.SAMPLED), + ), + parent=parent_span_context, + events=( + trace.Event( + name="event0", + timestamp=base_time + 50 * 10 ** 6, + attributes={ + "annotation_bool": True, + "annotation_string": "annotation_test", + "key_float": 0.3, + }, + ), + ), + links=( + trace_api.Link( + context=other_context, attributes={"key_bool": True} + ), + ), + resource=trace.Resource({}), + ) + span1.start(start_time=start_times[0]) + span1.set_attribute("key_bool", False) + span1.set_attribute("key_string", "hello_world") + span1.set_attribute("key_float", 111.22) + span1.set_status(Status(StatusCode.OK)) + span1.end(end_time=end_times[0]) + + span2 = trace._Span( + name="test-span-2", + context=parent_span_context, + parent=None, + resource=trace.Resource( + attributes={"key_resource": "some_resource"} + ), + ) + span2.start(start_time=start_times[1]) + span2.set_status(Status(StatusCode.ERROR, "Example description")) + span2.end(end_time=end_times[1]) + + span3 = trace._Span( + name="test-span-3", + context=other_context, + parent=None, + resource=trace.Resource( + attributes={"key_resource": "some_resource"} + ), + ) + span3.start(start_time=start_times[2]) + span3.set_attribute("key_string", "hello_world") + span3.end(end_time=end_times[2]) + + span4 = trace._Span( + name="test-span-3", + context=other_context, + parent=None, + resource=trace.Resource({}), + instrumentation_info=InstrumentationInfo( + name="name", version="version" + ), + ) + span4.start(start_time=start_times[3]) + span4.end(end_time=end_times[3]) + + return [span1, span2, span3, span4] + + # pylint: disable=W0223 + class CommonJsonEncoderTest(CommonEncoderTest, abc.ABC): + def test_encode_trace_id(self): + for trace_id in (1, 1024, 2 ** 32, 2 ** 64, 2 ** 65): + self.assertEqual( + format(trace_id, "032x"), + self.get_encoder_default()._encode_trace_id(trace_id), + ) + + def test_encode_span_id(self): + for span_id in (1, 1024, 2 ** 8, 2 ** 16, 2 ** 32, 2 ** 64): + self.assertEqual( + format(span_id, "016x"), + self.get_encoder_default()._encode_span_id(span_id), + ) + + def test_encode_local_endpoint_default(self): + self.assertEqual( + self.get_encoder_default()._encode_local_endpoint( + NodeEndpoint() + ), + {"serviceName": TEST_SERVICE_NAME}, + ) + + def test_encode_local_endpoint_explicits(self): + ipv4 = "192.168.0.1" + ipv6 = "2001:db8::c001" + port = 414120 + self.assertEqual( + self.get_encoder_default()._encode_local_endpoint( + NodeEndpoint(ipv4, ipv6, port) + ), + { + "serviceName": TEST_SERVICE_NAME, + "ipv4": ipv4, + "ipv6": ipv6, + "port": port, + }, + ) + + @staticmethod + def pop_and_sort(source_list, source_index, sort_key): + """ + Convenience method that will pop a specified index from a list, + sort it by a given key and then return it. + """ + popped_item = source_list.pop(source_index, None) + if popped_item is not None: + popped_item = sorted(popped_item, key=lambda x: x[sort_key]) + return popped_item + + def assert_equal_encoded_spans(self, expected_spans, actual_spans): + if sys.version_info.major == 3 and sys.version_info.minor <= 5: + expected_spans = json.loads(expected_spans) + actual_spans = json.loads(actual_spans) + for expected_span, actual_span in zip( + expected_spans, actual_spans + ): + actual_annotations = self.pop_and_sort( + actual_span, "annotations", "timestamp" + ) + expected_annotations = self.pop_and_sort( + expected_span, "annotations", "timestamp" + ) + expected_binary_annotations = self.pop_and_sort( + expected_span, "binaryAnnotations", "key" + ) + actual_binary_annotations = self.pop_and_sort( + actual_span, "binaryAnnotations", "key" + ) + self.assertEqual(actual_span, expected_span) + self.assertEqual(actual_annotations, expected_annotations) + self.assertEqual( + actual_binary_annotations, expected_binary_annotations + ) + else: + self.assertEqual(expected_spans, actual_spans) diff --git a/exporter/opentelemetry-exporter-zipkin/tests/encoder/test_v2_protobuf.py b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/test_v2_protobuf.py similarity index 98% rename from exporter/opentelemetry-exporter-zipkin/tests/encoder/test_v2_protobuf.py rename to exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/test_v2_protobuf.py index 092a8a303ca..8cf9f6abf5f 100644 --- a/exporter/opentelemetry-exporter-zipkin/tests/encoder/test_v2_protobuf.py +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/encoder/test_v2_protobuf.py @@ -15,9 +15,9 @@ import json from opentelemetry.exporter.zipkin.encoder import NAME_KEY, VERSION_KEY -from opentelemetry.exporter.zipkin.encoder.v2.protobuf import ProtobufEncoder -from opentelemetry.exporter.zipkin.encoder.v2.protobuf.gen import zipkin_pb2 from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint +from opentelemetry.exporter.zipkin.proto.http.v2 import ProtobufEncoder +from opentelemetry.exporter.zipkin.proto.http.v2.gen import zipkin_pb2 from opentelemetry.trace import SpanKind from .common_tests import TEST_SERVICE_NAME, CommonEncoderTestCases diff --git a/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py similarity index 90% rename from exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py rename to exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py index 23da474730b..5537ec8d1a7 100644 --- a/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py @@ -18,10 +18,12 @@ from unittest.mock import patch from opentelemetry import trace -from opentelemetry.exporter.zipkin import DEFAULT_ENDPOINT, ZipkinExporter -from opentelemetry.exporter.zipkin.encoder import Protocol -from opentelemetry.exporter.zipkin.encoder.v2.protobuf import ProtobufEncoder from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint +from opentelemetry.exporter.zipkin.proto.http import ( + DEFAULT_ENDPOINT, + ZipkinExporter, +) +from opentelemetry.exporter.zipkin.proto.http.v2 import ProtobufEncoder from opentelemetry.sdk.environment_variables import ( OTEL_EXPORTER_ZIPKIN_ENDPOINT, ) @@ -52,7 +54,7 @@ def tearDown(self): del os.environ[OTEL_EXPORTER_ZIPKIN_ENDPOINT] def test_constructor_default(self): - exporter = ZipkinExporter(Protocol.V2_PROTOBUF) + exporter = ZipkinExporter() self.assertIsInstance(exporter.encoder, ProtobufEncoder) self.assertEqual(exporter.endpoint, DEFAULT_ENDPOINT) self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) @@ -64,7 +66,7 @@ def test_constructor_env_vars(self): os_endpoint = "https://foo:9911/path" os.environ[OTEL_EXPORTER_ZIPKIN_ENDPOINT] = os_endpoint - exporter = ZipkinExporter(Protocol.V2_PROTOBUF) + exporter = ZipkinExporter() self.assertEqual(exporter.endpoint, os_endpoint) self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) @@ -75,10 +77,9 @@ def test_constructor_env_vars(self): def test_constructor_protocol_endpoint(self): """Test the constructor for the common usage of providing the protocol and endpoint arguments.""" - protocol = Protocol.V2_PROTOBUF endpoint = "https://opentelemetry.io:15875/myapi/traces?format=zipkin" - exporter = ZipkinExporter(protocol, endpoint) + exporter = ZipkinExporter(endpoint) self.assertIsInstance(exporter.encoder, ProtobufEncoder) self.assertEqual(exporter.endpoint, endpoint) @@ -94,7 +95,6 @@ def test_constructor_all_params_and_env_vars(self): os_endpoint = "https://os.env.param:9911/path" os.environ[OTEL_EXPORTER_ZIPKIN_ENDPOINT] = os_endpoint - constructor_param_protocol = Protocol.V2_PROTOBUF constructor_param_endpoint = "https://constructor.param:9911/path" local_node_ipv4 = "192.168.0.1" local_node_ipv6 = "2001:db8::1000" @@ -102,7 +102,6 @@ def test_constructor_all_params_and_env_vars(self): max_tag_value_length = 56 exporter = ZipkinExporter( - constructor_param_protocol, constructor_param_endpoint, local_node_ipv4, local_node_ipv6, @@ -125,7 +124,7 @@ def test_constructor_all_params_and_env_vars(self): def test_export_success(self, mock_post): mock_post.return_value = MockResponse(200) spans = [] - exporter = ZipkinExporter(Protocol.V2_PROTOBUF) + exporter = ZipkinExporter() status = exporter.export(spans) self.assertEqual(SpanExportResult.SUCCESS, status) @@ -133,7 +132,7 @@ def test_export_success(self, mock_post): def test_export_invalid_response(self, mock_post): mock_post.return_value = MockResponse(404) spans = [] - exporter = ZipkinExporter(Protocol.V2_PROTOBUF) + exporter = ZipkinExporter() status = exporter.export(spans) self.assertEqual(SpanExportResult.FAILURE, status) @@ -149,7 +148,7 @@ def test_export_span_service_name(self, mock_post): span = _Span("test_span", context=context, resource=resource) span.start() span.end() - exporter = ZipkinExporter(Protocol.V2_PROTOBUF) + exporter = ZipkinExporter() exporter.export([span]) self.assertEqual(exporter.local_node.service_name, "test") diff --git a/exporter/opentelemetry-exporter-zipkin/README.rst b/exporter/opentelemetry-exporter-zipkin/README.rst index a23df5eeecc..2445ca879b7 100644 --- a/exporter/opentelemetry-exporter-zipkin/README.rst +++ b/exporter/opentelemetry-exporter-zipkin/README.rst @@ -6,7 +6,15 @@ OpenTelemetry Zipkin Exporter .. |pypi| image:: https://badge.fury.io/py/opentelemetry-exporter-zipkin.svg :target: https://pypi.org/project/opentelemetry-exporter-zipkin/ -This library allows to export tracing data to `Zipkin `_. +This library is provided as a convenience to install all supported OpenTelemetry Zipkin Exporters. Currently it installs: +* opentelemetry-exporter-zipkin-json +* opentelemetry-exporter-zipkin-proto-http + +In the future, additional packages may be available: +* opentelemetry-exporter-zipkin-thrift + +To avoid unnecessary dependencies, users should install the specific package once they've determined their +preferred serialization method. Installation ------------ diff --git a/exporter/opentelemetry-exporter-zipkin/setup.cfg b/exporter/opentelemetry-exporter-zipkin/setup.cfg index 11cc221589a..2e636b13b3c 100644 --- a/exporter/opentelemetry-exporter-zipkin/setup.cfg +++ b/exporter/opentelemetry-exporter-zipkin/setup.cfg @@ -14,7 +14,7 @@ # [metadata] name = opentelemetry-exporter-zipkin -description = Zipkin Span Exporter for OpenTelemetry +description = Zipkin Span Exporters for OpenTelemetry long_description = file: README.rst long_description_content_type = text/x-rst author = OpenTelemetry Authors @@ -35,21 +35,10 @@ classifiers = [options] python_requires = >=3.6 -package_dir= - =src packages=find_namespace: install_requires = - protobuf >= 3.12 - requests ~= 2.7 - opentelemetry-api == 1.0.0.dev0 - opentelemetry-sdk == 1.0.0.dev0 - -[options.packages.find] -where = src + opentelemetry-exporter-zipkin-json == 1.0.0.dev0 + opentelemetry-exporter-zipkin-proto-http == 1.0.0.dev0 [options.extras_require] test = - -[options.entry_points] -opentelemetry_exporter = - zipkin = opentelemetry.exporter.zipkin:ZipkinExporter \ No newline at end of file diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v1/__init__.py b/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v1/__init__.py deleted file mode 100644 index b892a67bef3..00000000000 --- a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v1/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Zipkin Export Encoders for JSON formats -""" - -import abc -from typing import Dict, List - -from opentelemetry.exporter.zipkin.encoder import Encoder -from opentelemetry.trace import Span, SpanContext, SpanKind - - -# pylint: disable=W0223 -class V1Encoder(Encoder): - def _extract_binary_annotations( - self, span: Span, encoded_local_endpoint: Dict - ) -> List[Dict]: - binary_annotations = [] - for tag_key, tag_value in self._extract_tags_from_span(span).items(): - if isinstance(tag_value, str) and self.max_tag_value_length > 0: - tag_value = tag_value[: self.max_tag_value_length] - binary_annotations.append( - { - "key": tag_key, - "value": tag_value, - "endpoint": encoded_local_endpoint, - } - ) - return binary_annotations diff --git a/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin.py b/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin.py new file mode 100644 index 00000000000..fa9c3ecf48f --- /dev/null +++ b/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin.py @@ -0,0 +1,27 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from opentelemetry.exporter.zipkin import json +from opentelemetry.exporter.zipkin.proto import http + + +class TestZipkinExporter(unittest.TestCase): + def test_constructors(self): + try: + json.ZipkinExporter() + http.ZipkinExporter() + except Exception as exc: # pylint: disable=broad-except + self.assertIsNone(exc) diff --git a/pyproject.toml b/pyproject.toml index f94565946af..e59980b2cf5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ exclude = ''' /( # generated files exporter/opentelemetry-exporter-jaeger-proto-grpc/src/opentelemetry/exporter/jaeger/proto/grpc/gen| exporter/opentelemetry-exporter-jaeger-thrift/src/opentelemetry/exporter/jaeger/thrift/gen| - exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/encoder/v2/protobuf/gen| + exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/v2/gen| opentelemetry-proto/src/opentelemetry/proto/collector| opentelemetry-proto/src/opentelemetry/proto/common| opentelemetry-proto/src/opentelemetry/proto/metrics| diff --git a/tox.ini b/tox.ini index 2be66c60145..af932e009e0 100644 --- a/tox.ini +++ b/tox.ini @@ -50,8 +50,16 @@ envlist = ; intentionally excluded from pypy3 ; opentelemetry-exporter-zipkin - py3{6,7,8,9}-test-exporter-zipkin - pypy3-test-exporter-zipkin + py3{6,7,8,9}-test-exporter-zipkin-combined + pypy3-test-exporter-zipkin-combined + + ; opentelemetry-exporter-zipkin-proto-http + py3{6,7,8,9}-test-exporter-zipkin-proto-http + pypy3-test-exporter-zipkin-proto-http + + ; opentelemetry-exporter-zipkin-json + py3{6,7,8,9}-test-exporter-zipkin-json + pypy3-test-exporter-zipkin-json ; opentelemetry-opentracing-shim py3{6,7,8,9}-test-core-opentracing-shim @@ -97,7 +105,9 @@ changedir = test-exporter-opencensus: exporter/opentelemetry-exporter-opencensus/tests test-exporter-otlp-combined: exporter/opentelemetry-exporter-otlp/tests test-exporter-otlp-proto-grpc: exporter/opentelemetry-exporter-otlp-proto-grpc/tests - test-exporter-zipkin: exporter/opentelemetry-exporter-zipkin/tests + test-exporter-zipkin-combined: exporter/opentelemetry-exporter-zipkin/tests + test-exporter-zipkin-proto-http: exporter/opentelemetry-exporter-zipkin-proto-http/tests + test-exporter-zipkin-json: exporter/opentelemetry-exporter-zipkin-json/tests test-propagator-b3: propagator/opentelemetry-propagator-b3/tests test-propagator-jaeger: propagator/opentelemetry-propagator-jaeger/tests @@ -131,7 +141,14 @@ commands_pre = opentracing-shim: pip install {toxinidir}/opentelemetry-sdk opentracing-shim: pip install {toxinidir}/shim/opentelemetry-opentracing-shim - zipkin: pip install {toxinidir}/exporter/opentelemetry-exporter-zipkin + exporter-zipkin-combined: pip install {toxinidir}/exporter/opentelemetry-exporter-zipkin-json + exporter-zipkin-combined: pip install {toxinidir}/exporter/opentelemetry-exporter-zipkin-proto-http + exporter-zipkin-combined: pip install {toxinidir}/exporter/opentelemetry-exporter-zipkin + + exporter-zipkin-proto-http: pip install {toxinidir}/exporter/opentelemetry-exporter-zipkin-json + exporter-zipkin-proto-http: pip install {toxinidir}/exporter/opentelemetry-exporter-zipkin-proto-http + + exporter-zipkin-json: pip install {toxinidir}/exporter/opentelemetry-exporter-zipkin-json b3: pip install {toxinidir}/propagator/opentelemetry-propagator-b3 @@ -184,6 +201,8 @@ commands_pre = python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-opencensus[test] python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-grpc[test] python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-otlp[test] + python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-zipkin-json[test] + python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-zipkin-proto-http[test] python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-zipkin[test] python -m pip install -e {toxinidir}/propagator/opentelemetry-propagator-b3[test] python -m pip install -e {toxinidir}/propagator/opentelemetry-propagator-jaeger[test]