From fe42a0331aed911920e454cad12e52d0666cd32f Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Sun, 23 May 2021 12:13:22 +0530 Subject: [PATCH 01/15] WIP: OTLP Exporter Proto over HTPP --- eachdist.ini | 2 + .../LICENSE | 201 +++++++++ .../MANIFEST.in | 9 + .../README.rst | 25 ++ .../setup.cfg | 57 +++ .../setup.py | 33 ++ .../exporter/otlp/proto/http/__init__.py | 78 ++++ .../exporter/otlp/proto/http/py.typed | 0 .../proto/http/trace_exporter/__init__.py | 183 ++++++++ .../http/trace_exporter/encoder/__init__.py | 306 ++++++++++++++ .../exporter/otlp/proto/http/version.py | 15 + .../tests/test_proto_span_exporter.py | 149 +++++++ .../tests/test_protobuf_encoder.py | 396 ++++++++++++++++++ .../opentelemetry-exporter-otlp/README.rst | 2 +- .../opentelemetry-exporter-otlp/setup.cfg | 1 + tox.ini | 11 + 16 files changed, 1467 insertions(+), 1 deletion(-) create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/LICENSE create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/MANIFEST.in create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/README.rst create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/setup.py create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/py.typed create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/tests/test_protobuf_encoder.py diff --git a/eachdist.ini b/eachdist.ini index 121ae93dab..b7f998c4a7 100644 --- a/eachdist.ini +++ b/eachdist.ini @@ -24,6 +24,7 @@ packages= exporter/opentelemetry-exporter-zipkin-json exporter/opentelemetry-exporter-zipkin exporter/opentelemetry-exporter-otlp-proto-grpc + exporter/opentelemetry-exporter-otlp-proto-http exporter/opentelemetry-exporter-otlp exporter/opentelemetry-exporter-jaeger-thrift exporter/opentelemetry-exporter-jaeger-proto-grpc @@ -51,6 +52,7 @@ packages= opentelemetry-api opentelemetry-sdk exporter/opentelemetry-exporter-otlp-proto-grpc + exporter/opentelemetry-exporter-otlp-proto-http exporter/opentelemetry-exporter-otlp [lintroots] diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/LICENSE b/exporter/opentelemetry-exporter-otlp-proto-http/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-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-otlp-proto-http/MANIFEST.in b/exporter/opentelemetry-exporter-otlp-proto-http/MANIFEST.in new file mode 100644 index 0000000000..aed3e33273 --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-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-otlp-proto-http/README.rst b/exporter/opentelemetry-exporter-otlp-proto-http/README.rst new file mode 100644 index 0000000000..394b4cf5e5 --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-proto-http/README.rst @@ -0,0 +1,25 @@ +OpenTelemetry Collector Protobuf over HTTP Exporter +=================================================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-exporter-otlp-proto-http.svg + :target: https://pypi.org/project/opentelemetry-exporter-otlp-proto-http/ + +This library allows to export data to the OpenTelemetry Collector using the OpenTelemetry Protocol using Protobuf over HTTP. + +Installation +------------ + +:: + + pip install opentelemetry-exporter-otlp-proto-http + + +References +---------- + +* `OpenTelemetry Collector Exporter `_ +* `OpenTelemetry Collector `_ +* `OpenTelemetry `_ +* `OpenTelemetry Protocol Specification `_ diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg b/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg new file mode 100644 index 0000000000..67e54a4b91 --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-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-otlp-proto-http +description = OpenTelemetry Collector Protobuf over HTTP Exporter +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-otlp-proto-http +platforms = any +license = Apache-2.0 +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + License :: OSI Approved :: Apache Software License + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + +[options] +python_requires = >=3.6 +package_dir= + =src +packages=find_namespace: +install_requires = + requests ~= 2.7 + googleapis-common-protos ~= 1.52 + opentelemetry-api == 1.3.0.dev0 + opentelemetry-sdk == 1.3.0.dev0 + opentelemetry-proto == 1.3.0.dev0 + backoff ~= 1.10.0 + +[options.extras_require] +test = + +[options.packages.find] +where = src + +[options.entry_points] +opentelemetry_exporter = + otlp_proto_http_span = opentelemetry.exporter.otlp.proto.http.trace_exporter:OTLPSpanExporter diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/setup.py b/exporter/opentelemetry-exporter-otlp-proto-http/setup.py new file mode 100644 index 0000000000..510eceba6c --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-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", + "otlp", + "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-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py new file mode 100644 index 0000000000..c3be0f2c21 --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py @@ -0,0 +1,78 @@ +# 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. + + +""" +This library allows to export tracing data to an OTLP collector. + +Usage +----- + +The **OTLP Span Exporter** allows to export `OpenTelemetry`_ traces to the +`OTLP`_ collector. + +You can configure the exporter with the following environment variables: + +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_HEADERS` +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` +- :envvar:`OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` +- :envvar:`OTEL_EXPORTER_OTLP_TIMEOUT` +- :envvar:`OTEL_EXPORTER_OTLP_PROTOCOL` +- :envvar:`OTEL_EXPORTER_OTLP_HEADERS` +- :envvar:`OTEL_EXPORTER_OTLP_ENDPOINT` +- :envvar:`OTEL_EXPORTER_OTLP_COMPRESSION` +- :envvar:`OTEL_EXPORTER_OTLP_CERTIFICATE` + +.. _OTLP: https://github.com/open-telemetry/opentelemetry-collector/ +.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/ + +.. code:: python + + from opentelemetry import trace + from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter + from opentelemetry.sdk.resources import Resource + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import BatchSpanProcessor + + # Resource can be required for some backends, e.g. Jaeger + # If resource wouldn't be set - traces wouldn't appears in Jaeger + resource = Resource(attributes={ + "service.name": "service" + }) + + trace.set_tracer_provider(TracerProvider(resource=resource)) + tracer = trace.get_tracer(__name__) + + otlp_exporter = OTLPSpanExporter(endpoint="http://localhost:4317", insecure=True) + + span_processor = BatchSpanProcessor(otlp_exporter) + + trace.get_tracer_provider().add_span_processor(span_processor) + + with tracer.start_as_current_span("foo"): + print("Hello world!") + +API +--- +""" +import enum + + +class Compression(enum.Enum): + NoCompression = "none" + Deflate = "deflate" + Gzip = "gzip" diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/py.typed b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py new file mode 100644 index 0000000000..7120047eb3 --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py @@ -0,0 +1,183 @@ +# 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 gzip +import logging +import zlib +from io import BytesIO +from os import environ +from typing import Dict, Optional +from time import sleep + +import requests +from backoff import expo + +from opentelemetry.sdk.environment_variables import ( + OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE, + OTEL_EXPORTER_OTLP_TRACES_COMPRESSION, + OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, + OTEL_EXPORTER_OTLP_TRACES_HEADERS, + OTEL_EXPORTER_OTLP_TRACES_TIMEOUT, + OTEL_EXPORTER_OTLP_CERTIFICATE, + OTEL_EXPORTER_OTLP_COMPRESSION, + OTEL_EXPORTER_OTLP_ENDPOINT, + OTEL_EXPORTER_OTLP_HEADERS, + OTEL_EXPORTER_OTLP_TIMEOUT, +) +from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult +from opentelemetry.exporter.otlp.proto.http import Compression +from opentelemetry.exporter.otlp.proto.http.trace_exporter.encoder import ( + ProtobufEncoder, +) + + +_logger = logging.getLogger(__name__) + + +DEFAULT_COMPRESSION = Compression.NoCompression +DEFAULT_ENDPOINT = "https://localhost:4317" +DEFAULT_TIMEOUT = 10 # in seconds + + +class OTLPSpanExporter(SpanExporter): + def __init__( + self, + endpoint: Optional[str] = None, + certificate_file: Optional[str] = None, + headers: Optional[Dict[str, str]] = None, + timeout: Optional[int] = None, + compression: Optional[Compression] = None, + ): + self._endpoint = endpoint or environ.get( + OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, + environ.get(OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_ENDPOINT), + ) + self._certificate_file = certificate_file or environ.get( + OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE, + environ.get(OTEL_EXPORTER_OTLP_CERTIFICATE, False), + ) + self._headers = headers or _headers_from_env() + self._timeout = timeout or int( + environ.get( + OTEL_EXPORTER_OTLP_TRACES_TIMEOUT, + environ.get(OTEL_EXPORTER_OTLP_TIMEOUT, DEFAULT_TIMEOUT), + ) + ) + self._compression = compression or _compression_from_env() + self._session = requests.Session() + self._session.headers.update(self._headers) + self._session.headers.update( + {"Content-Type": ProtobufEncoder._CONTENT_TYPE} + ) + if self._compression is not Compression.NoCompression: + self._session.headers.update( + {"Content-Encoding": self._compression.value} + ) + self._closed = False + + def _export(self, serialized_data: str): + data = serialized_data + if self._compression == Compression.Gzip: + gzip_data = BytesIO() + with gzip.GzipFile(fileobj=gzip_data, mode="w") as gzip_stream: + gzip_stream.write(serialized_data) + data = gzip_data.getvalue() + elif self._compression == Compression.Deflate: + data = zlib.compress(bytes(serialized_data)) + + return self._session.post( + url=self._endpoint, + data=data, + verify=self._certificate_file, + timeout=self._timeout, + ) + + @staticmethod + def _retryable(resp: requests.Response) -> bool: + if resp.status_code == 408: + return True + if resp.status_code >= 500 and resp.status_code <= 599: + return True + return False + + def export(self, spans) -> SpanExportResult: + # After the call to Shutdown subsequent calls to Export are + # not allowed and should return a Failure result. + if self._closed: + _logger.warning("Exporter already shutdown, ignoring batch") + return SpanExportResult.FAILURE + + serialized_data = ProtobufEncoder.serialize(spans) + max_value = 64 + + for delay in expo(max_value=max_value): + + if delay == max_value: + return SpanExportResult.FAILURE + + resp = self._export(serialized_data) + # pylint: disable=no-else-return + if resp.status_code in (200, 202): + return SpanExportResult.SUCCESS + elif self._retryable(resp): + _logger.debug( + "Waiting %ss before retrying export of span", delay + ) + sleep(delay) + continue + else: + _logger.warning( + "Failed to export batch code: %s, reason: %s", + resp.status_code, + resp.text, + ) + return SpanExportResult.FAILURE + return SpanExportResult.FAILURE + + def shutdown(self): + if self._closed: + _logger.warning("Exporter already shutdown, ignoring call") + return + self._session.close() + self._closed = True + + +def _headers_from_env() -> Optional[Dict[str, str]]: + headers_str = environ.get( + OTEL_EXPORTER_OTLP_TRACES_HEADERS, + environ.get(OTEL_EXPORTER_OTLP_HEADERS), + ) + headers = {} + if headers_str: + for header in headers_str.split(","): + try: + header_name, header_value = header.split("=") + headers[header_name.strip()] = header_value.strip() + except ValueError: + _logger.warning( + "Skipped invalid OTLP exporter header: %r", header + ) + return headers + + +def _compression_from_env() -> Compression: + compression = ( + environ.get( + OTEL_EXPORTER_OTLP_TRACES_COMPRESSION, + environ.get(OTEL_EXPORTER_OTLP_COMPRESSION, "none"), + ) + .lower() + .strip() + ) + return Compression(compression) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py new file mode 100644 index 0000000000..80b59ea9c7 --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py @@ -0,0 +1,306 @@ +# 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 logging +from collections import abc +from typing import Any, List, Optional, Sequence, Text + +from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ( + ExportTraceServiceRequest as PB2ExportTraceServiceRequest, +) +from opentelemetry.proto.common.v1.common_pb2 import AnyValue as PB2AnyValue +from opentelemetry.proto.common.v1.common_pb2 import ( + ArrayValue as PB2ArrayValue, +) +from opentelemetry.proto.common.v1.common_pb2 import ( + InstrumentationLibrary as PB2InstrumentationLibrary, +) +from opentelemetry.proto.common.v1.common_pb2 import KeyValue as PB2KeyValue +from opentelemetry.proto.resource.v1.resource_pb2 import ( + Resource as PB2Resource, +) +from opentelemetry.proto.trace.v1.trace_pb2 import ( + InstrumentationLibrarySpans as PB2InstrumentationLibrarySpans, +) +from opentelemetry.proto.trace.v1.trace_pb2 import ( + ResourceSpans as PB2ResourceSpans, +) +from opentelemetry.proto.trace.v1.trace_pb2 import Span as PB2SPan +from opentelemetry.proto.trace.v1.trace_pb2 import Status as PB2Status +from opentelemetry.sdk.trace import Event as SDKEvent +from opentelemetry.sdk.trace import ( + InstrumentationInfo as SDKInstrumentationInfo, +) +from opentelemetry.sdk.trace import Resource as SDKResource +from opentelemetry.sdk.trace import Span as SDKSpan +from opentelemetry.sdk.trace import SpanContext as SDKSpanContext +from opentelemetry.trace import Link as SDKLink +from opentelemetry.trace import SpanKind as SDKSpanKind +from opentelemetry.trace import TraceState as SDKTraceState +from opentelemetry.trace.status import Status as SDKStatus +from opentelemetry.trace.status import StatusCode as SDKStatusCode +from opentelemetry.util.types import Attributes as SDKAttributes + +# pylint: disable=E1101 +_SPAN_KIND_MAP = { + SDKSpanKind.INTERNAL: PB2SPan.SpanKind.SPAN_KIND_INTERNAL, + SDKSpanKind.SERVER: PB2SPan.SpanKind.SPAN_KIND_SERVER, + SDKSpanKind.CLIENT: PB2SPan.SpanKind.SPAN_KIND_CLIENT, + SDKSpanKind.PRODUCER: PB2SPan.SpanKind.SPAN_KIND_PRODUCER, + SDKSpanKind.CONSUMER: PB2SPan.SpanKind.SPAN_KIND_CONSUMER, +} + +_logger = logging.getLogger(__name__) + + +class ProtobufEncoder: + _CONTENT_TYPE = "application/x-protobuf" + + @classmethod + def serialize(cls, sdk_spans: Sequence[SDKSpan]) -> str: + return cls.encode(sdk_spans).SerializeToString() + + @staticmethod + def encode(sdk_spans: Sequence[SDKSpan]) -> PB2ExportTraceServiceRequest: + return PB2ExportTraceServiceRequest( + resource_spans=_encode_resource_spans(sdk_spans) + ) + + +def _encode_resource_spans( + sdk_spans: Sequence[SDKSpan], +) -> List[PB2ResourceSpans]: + # We need to inspect the spans and group + structure them as: + # + # Resource + # Instrumentation Library + # Spans + # + # First loop organizes the SDK spans in this structure. Protobuf messages + # are not hashable so we stick with SDK data in this phase. + # + # Second loop encodes the data into Protobuf format. + # + sdk_resource_spans = {} + + for sdk_span in sdk_spans: + sdk_resource = sdk_span.resource + sdk_instrumentation = sdk_span.instrumentation_info or None + pb2_span = _encode_span(sdk_span) + + if sdk_resource not in sdk_resource_spans.keys(): + sdk_resource_spans[sdk_resource] = { + sdk_instrumentation: [pb2_span] + } + elif ( + sdk_instrumentation not in sdk_resource_spans[sdk_resource].keys() + ): + sdk_resource_spans[sdk_resource][sdk_instrumentation] = [pb2_span] + else: + sdk_resource_spans[sdk_resource][sdk_instrumentation].append( + pb2_span + ) + + pb2_resource_spans = [] + + for sdk_resource, sdk_instrumentations in sdk_resource_spans.items(): + instrumentation_library_spans = [] + for sdk_instrumentation, pb2_spans in sdk_instrumentations.items(): + instrumentation_library_spans.append( + PB2InstrumentationLibrarySpans( + instrumentation_library=( + _encode_instrumentation_library(sdk_instrumentation) + ), + spans=pb2_spans, + ) + ) + pb2_resource_spans.append( + PB2ResourceSpans( + resource=_encode_resource(sdk_resource), + instrumentation_library_spans=instrumentation_library_spans, + ) + ) + + return pb2_resource_spans + + +def _encode_span(sdk_span: SDKSpan) -> PB2SPan: + sdk_context = sdk_span.get_span_context() + return PB2SPan( + trace_id=_encode_trace_id(sdk_context.trace_id), + span_id=_encode_span_id(sdk_context.span_id), + trace_state=_encode_trace_state(sdk_context.trace_state), + parent_span_id=_encode_parent_id(sdk_span.parent), + name=sdk_span.name, + kind=_SPAN_KIND_MAP[sdk_span.kind], + start_time_unix_nano=sdk_span.start_time, + end_time_unix_nano=sdk_span.end_time, + attributes=_encode_attributes(sdk_span.attributes), + events=_encode_events(sdk_span.events), + links=_encode_links(sdk_span.links), + status=_encode_status(sdk_span.status), + ) + + +def _encode_events( + sdk_events: Sequence[SDKEvent], +) -> Optional[List[PB2SPan.Event]]: + pb2_events = None + if sdk_events: + pb2_events = [] + for sdk_event in sdk_events: + encoded_event = PB2SPan.Event( + name=sdk_event.name, + time_unix_nano=sdk_event.timestamp, + ) + for key, value in sdk_event.attributes.items(): + try: + encoded_event.attributes.append( + _encode_key_value(key, value) + ) + # pylint: disable=broad-except + except Exception as error: + _logger.exception(error) + pb2_events.append(encoded_event) + return pb2_events + + +def _encode_links(sdk_links: List[SDKLink]) -> List[PB2SPan.Link]: + pb2_links = None + if sdk_links: + pb2_links = [] + for sdk_link in sdk_links: + encoded_link = PB2SPan.Link( + trace_id=_encode_trace_id(sdk_link.context.trace_id), + span_id=_encode_span_id(sdk_link.context.span_id), + ) + for key, value in sdk_link.attributes.items(): + try: + encoded_link.attributes.append( + _encode_key_value(key, value) + ) + # pylint: disable=broad-except + except Exception as error: + _logger.exception(error) + pb2_links.append(encoded_link) + return pb2_links + + +def _encode_status(sdk_status: SDKStatus) -> Optional[PB2Status]: + pb2_status = None + if sdk_status is not None: + deprecated_code = PB2Status.DEPRECATED_STATUS_CODE_OK + if sdk_status.status_code is SDKStatusCode.ERROR: + deprecated_code = PB2Status.DEPRECATED_STATUS_CODE_UNKNOWN_ERROR + pb2_status = PB2Status( + deprecated_code=deprecated_code, + code=sdk_status.status_code.value, + message=sdk_status.description, + ) + return pb2_status + + +def _encode_trace_state(sdk_trace_state: SDKTraceState) -> Optional[str]: + pb2_trace_state = None + if sdk_trace_state is not None: + pb2_trace_state = ",".join( + [ + "{}={}".format(key, value) + for key, value in (sdk_trace_state.items()) + ] + ) + return pb2_trace_state + + +def _encode_parent_id(context: Optional[SDKSpanContext]) -> Optional[bytes]: + if isinstance(context, SDKSpanContext): + encoded_parent_id = _encode_span_id(context.span_id) + else: + encoded_parent_id = None + return encoded_parent_id + + +def _encode_attributes( + sdk_attributes: SDKAttributes, +) -> Optional[List[PB2KeyValue]]: + if sdk_attributes: + attributes = [] + for key, value in sdk_attributes.items(): + try: + attributes.append(_encode_key_value(key, value)) + except Exception as error: # pylint: disable=broad-except + _logger.exception(error) + else: + attributes = None + return attributes + + +def _encode_resource(sdk_resource: SDKResource) -> PB2Resource: + pb2_resource = PB2Resource() + for key, value in sdk_resource.attributes.items(): + try: + # pylint: disable=no-member + pb2_resource.attributes.append(_encode_key_value(key, value)) + except Exception as error: # pylint: disable=broad-except + _logger.exception(error) + return pb2_resource + + +def _encode_instrumentation_library( + sdk_instrumentation_info: SDKInstrumentationInfo, +) -> PB2InstrumentationLibrary: + if sdk_instrumentation_info is None: + pb2_instrumentation_library = PB2InstrumentationLibrary() + else: + pb2_instrumentation_library = PB2InstrumentationLibrary( + name=sdk_instrumentation_info.name, + version=sdk_instrumentation_info.version, + ) + return pb2_instrumentation_library + + +def _encode_value(value: Any) -> PB2AnyValue: + if isinstance(value, bool): + any_value = PB2AnyValue(bool_value=value) + elif isinstance(value, str): + any_value = PB2AnyValue(string_value=value) + elif isinstance(value, int): + any_value = PB2AnyValue(int_value=value) + elif isinstance(value, float): + any_value = PB2AnyValue(double_value=value) + elif isinstance(value, abc.Sequence): + any_value = PB2AnyValue( + array_value=PB2ArrayValue(values=[_encode_value(v) for v in value]) + ) + # tracing specs currently does not support Mapping type attributes. + # elif isinstance(value, abc.Mapping): + # pass + else: + raise Exception( + "Invalid type {} of value {}".format(type(value), value) + ) + return any_value + + +def _encode_key_value(key: Text, value: Any) -> PB2KeyValue: + any_value = _encode_value(value) + return PB2KeyValue(key=key, value=any_value) + + +def _encode_span_id(span_id: int) -> bytes: + return span_id.to_bytes(length=8, byteorder="big", signed=False) + + +def _encode_trace_id(trace_id: int) -> bytes: + return trace_id.to_bytes(length=16, byteorder="big", signed=False) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py new file mode 100644 index 0000000000..f94a1f8a6c --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/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.3.0.dev0" diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py new file mode 100644 index 0000000000..cc19594ddf --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py @@ -0,0 +1,149 @@ +# 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 unittest.mock import patch + +from opentelemetry.exporter.otlp.proto.http import Compression +from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( + DEFAULT_COMPRESSION, + DEFAULT_ENDPOINT, + DEFAULT_TIMEOUT, + OTLPSpanExporter, +) + +# from opentelemetry.exporter.otlp.encoder.protobuf import ProtobufEncoder +from opentelemetry.sdk.environment_variables import ( + OTEL_EXPORTER_OTLP_CERTIFICATE, + OTEL_EXPORTER_OTLP_COMPRESSION, + OTEL_EXPORTER_OTLP_ENDPOINT, + OTEL_EXPORTER_OTLP_HEADERS, + OTEL_EXPORTER_OTLP_TIMEOUT, + OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE, + OTEL_EXPORTER_OTLP_TRACES_COMPRESSION, + OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, + OTEL_EXPORTER_OTLP_TRACES_HEADERS, + OTEL_EXPORTER_OTLP_TRACES_TIMEOUT, +) + +OS_ENV_ENDPOINT = "os.env.base" +OS_ENV_CERTIFICATE = "os/env/base.crt" +OS_ENV_HEADERS = "envHeader1=val1,envHeader2=val2" +OS_ENV_TIMEOUT = "30" + + +# pylint: disable=protected-access +class TestOTLPSpanExporter(unittest.TestCase): + def test_constructor_default(self): + + exporter = OTLPSpanExporter() + + self.assertEqual(exporter._endpoint, DEFAULT_ENDPOINT) + self.assertEqual(exporter._certificate_file, False) + self.assertEqual(exporter._timeout, DEFAULT_TIMEOUT) + self.assertIs(exporter._compression, DEFAULT_COMPRESSION) + self.assertEqual(exporter._headers, {}) + + @patch.dict( + "os.environ", + { + OTEL_EXPORTER_OTLP_CERTIFICATE: OS_ENV_CERTIFICATE, + OTEL_EXPORTER_OTLP_COMPRESSION: Compression.Gzip.value, + OTEL_EXPORTER_OTLP_ENDPOINT: OS_ENV_ENDPOINT, + OTEL_EXPORTER_OTLP_HEADERS: OS_ENV_HEADERS, + OTEL_EXPORTER_OTLP_TIMEOUT: OS_ENV_TIMEOUT, + OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE: "traces/certificate.env", + OTEL_EXPORTER_OTLP_TRACES_COMPRESSION: Compression.Deflate.value, + OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: "https://traces.endpoint.env", + OTEL_EXPORTER_OTLP_TRACES_HEADERS: "tracesEnv1=val1,tracesEnv2=val2", + OTEL_EXPORTER_OTLP_TRACES_TIMEOUT: "40", + }, + ) + def test_exporter_traces_env_take_priority(self): + exporter = OTLPSpanExporter() + + self.assertEqual(exporter._endpoint, "https://traces.endpoint.env") + self.assertEqual(exporter._certificate_file, "traces/certificate.env") + self.assertEqual(exporter._timeout, 40) + self.assertIs(exporter._compression, Compression.Deflate) + self.assertEqual( + exporter._headers, + {"tracesEnv1": "val1", "tracesEnv2": "val2"}, + ) + + @patch.dict( + "os.environ", + { + OTEL_EXPORTER_OTLP_CERTIFICATE: OS_ENV_CERTIFICATE, + OTEL_EXPORTER_OTLP_COMPRESSION: Compression.Gzip.value, + OTEL_EXPORTER_OTLP_ENDPOINT: OS_ENV_ENDPOINT, + OTEL_EXPORTER_OTLP_HEADERS: OS_ENV_HEADERS, + OTEL_EXPORTER_OTLP_TIMEOUT: OS_ENV_TIMEOUT, + }, + ) + def test_exporter_constructor_take_priority(self): + exporter = OTLPSpanExporter( + endpoint="example.com/1234", + certificate_file="path/to/service.crt", + headers={"testHeader1": "value1", "testHeader2": "value2"}, + timeout=20, + compression=Compression.NoCompression, + ) + + self.assertEqual(exporter._endpoint, "example.com/1234") + self.assertEqual(exporter._certificate_file, "path/to/service.crt") + self.assertEqual(exporter._timeout, 20) + self.assertIs(exporter._compression, Compression.NoCompression) + self.assertEqual( + exporter._headers, + {"testHeader1": "value1", "testHeader2": "value2"}, + ) + + @patch.dict( + "os.environ", + { + OTEL_EXPORTER_OTLP_CERTIFICATE: OS_ENV_CERTIFICATE, + OTEL_EXPORTER_OTLP_COMPRESSION: Compression.Gzip.value, + OTEL_EXPORTER_OTLP_ENDPOINT: OS_ENV_ENDPOINT, + OTEL_EXPORTER_OTLP_HEADERS: OS_ENV_HEADERS, + OTEL_EXPORTER_OTLP_TIMEOUT: OS_ENV_TIMEOUT, + }, + ) + def test_exporter_env(self): + + exporter = OTLPSpanExporter() + + self.assertEqual(exporter._endpoint, OS_ENV_ENDPOINT) + self.assertEqual(exporter._certificate_file, OS_ENV_CERTIFICATE) + self.assertEqual(exporter._timeout, int(OS_ENV_TIMEOUT)) + self.assertIs(exporter._compression, Compression.Gzip) + self.assertEqual( + exporter._headers, {"envHeader1": "val1", "envHeader2": "val2"} + ) + + @patch.dict( + "os.environ", + { + OTEL_EXPORTER_OTLP_HEADERS: "envHeader1=val1,envHeader2=val2,missingValue" + }, + ) + def test_headers_parse_from_env(self): + + with self.assertLogs(level="WARNING") as cm: + _ = OTLPSpanExporter() + + self.assertEqual( + cm.records[0].message, + "Skipped invalid OTLP exporter header: 'missingValue'", + ) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_protobuf_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_protobuf_encoder.py new file mode 100644 index 0000000000..d7bf607c82 --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_protobuf_encoder.py @@ -0,0 +1,396 @@ +# 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. + +# pylint: disable=protected-access + +import unittest +from typing import List, Tuple + +from opentelemetry.exporter.otlp.proto.http.trace_exporter.encoder import ( + _SPAN_KIND_MAP, + ProtobufEncoder, + _encode_span_id, + _encode_status, + _encode_trace_id, +) +from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ( + ExportTraceServiceRequest as PB2ExportTraceServiceRequest, +) +from opentelemetry.proto.common.v1.common_pb2 import AnyValue as PB2AnyValue +from opentelemetry.proto.common.v1.common_pb2 import ( + InstrumentationLibrary as PB2InstrumentationLibrary, +) +from opentelemetry.proto.common.v1.common_pb2 import KeyValue as PB2KeyValue +from opentelemetry.proto.resource.v1.resource_pb2 import ( + Resource as PB2Resource, +) +from opentelemetry.proto.trace.v1.trace_pb2 import ( + InstrumentationLibrarySpans as PB2InstrumentationLibrarySpans, +) +from opentelemetry.proto.trace.v1.trace_pb2 import ( + ResourceSpans as PB2ResourceSpans, +) +from opentelemetry.proto.trace.v1.trace_pb2 import Span as PB2SPan +from opentelemetry.proto.trace.v1.trace_pb2 import Status as PB2Status +from opentelemetry.sdk.trace import Event as SDKEvent +from opentelemetry.sdk.trace import Resource as SDKResource +from opentelemetry.sdk.trace import SpanContext as SDKSpanContext +from opentelemetry.sdk.trace import _Span as SDKSpan +from opentelemetry.sdk.util.instrumentation import ( + InstrumentationInfo as SDKInstrumentationInfo, +) +from opentelemetry.trace import Link as SDKLink +from opentelemetry.trace import SpanKind as SDKSpanKind +from opentelemetry.trace import TraceFlags as SDKTraceFlags +from opentelemetry.trace.status import Status as SDKStatus +from opentelemetry.trace.status import StatusCode as SDKStatusCode + + +class TestProtobufEncoder(unittest.TestCase): + def test_encode(self): + otel_spans, expected_encoding = self.get_exhaustive_test_spans() + self.assertEqual( + ProtobufEncoder().encode(otel_spans), expected_encoding + ) + + def test_serialize(self): + otel_spans, expected_encoding = self.get_exhaustive_test_spans() + self.assertEqual( + ProtobufEncoder().serialize(otel_spans), + expected_encoding.SerializeToString(), + ) + + def test_content_type(self): + self.assertEqual( + ProtobufEncoder._CONTENT_TYPE, "application/x-protobuf" + ) + + @staticmethod + def get_exhaustive_otel_span_list() -> List[SDKSpan]: + trace_id = 0x3E0C63257DE34C926F9EFCD03927272E + + 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 = SDKSpanContext( + trace_id, 0x1111111111111111, is_remote=False + ) + + other_context = SDKSpanContext( + trace_id, 0x2222222222222222, is_remote=False + ) + + span1 = SDKSpan( + name="test-span-1", + context=SDKSpanContext( + trace_id, + 0x34BF92DEEFC58C92, + is_remote=False, + trace_flags=SDKTraceFlags(SDKTraceFlags.SAMPLED), + ), + parent=parent_span_context, + events=( + SDKEvent( + name="event0", + timestamp=base_time + 50 * 10 ** 6, + attributes={ + "annotation_bool": True, + "annotation_string": "annotation_test", + "key_float": 0.3, + }, + ), + ), + links=( + SDKLink(context=other_context, attributes={"key_bool": True}), + ), + resource=SDKResource({}), + ) + 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(SDKStatus(SDKStatusCode.ERROR, "Example description")) + span1.end(end_time=end_times[0]) + + span2 = SDKSpan( + name="test-span-2", + context=parent_span_context, + parent=None, + resource=SDKResource(attributes={"key_resource": "some_resource"}), + ) + span2.start(start_time=start_times[1]) + span2.end(end_time=end_times[1]) + + span3 = SDKSpan( + name="test-span-3", + context=other_context, + parent=None, + resource=SDKResource(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 = SDKSpan( + name="test-span-4", + context=other_context, + parent=None, + resource=SDKResource({}), + instrumentation_info=SDKInstrumentationInfo( + name="name", version="version" + ), + ) + span4.start(start_time=start_times[3]) + span4.end(end_time=end_times[3]) + + return [span1, span2, span3, span4] + + def get_exhaustive_test_spans( + self, + ) -> Tuple[List[SDKSpan], PB2ExportTraceServiceRequest]: + otel_spans = self.get_exhaustive_otel_span_list() + trace_id = _encode_trace_id(otel_spans[0].context.trace_id) + span_kind = _SPAN_KIND_MAP[SDKSpanKind.INTERNAL] + + pb2_service_request = PB2ExportTraceServiceRequest( + resource_spans=[ + PB2ResourceSpans( + resource=PB2Resource(), + instrumentation_library_spans=[ + PB2InstrumentationLibrarySpans( + instrumentation_library=PB2InstrumentationLibrary(), + spans=[ + PB2SPan( + trace_id=trace_id, + span_id=_encode_span_id( + otel_spans[0].context.span_id + ), + trace_state=None, + parent_span_id=_encode_span_id( + otel_spans[0].parent.span_id + ), + name=otel_spans[0].name, + kind=span_kind, + start_time_unix_nano=otel_spans[ + 0 + ].start_time, + end_time_unix_nano=otel_spans[0].end_time, + attributes=[ + PB2KeyValue( + key="key_bool", + value=PB2AnyValue( + bool_value=False + ), + ), + PB2KeyValue( + key="key_string", + value=PB2AnyValue( + string_value="hello_world" + ), + ), + PB2KeyValue( + key="key_float", + value=PB2AnyValue( + double_value=111.22 + ), + ), + ], + events=[ + PB2SPan.Event( + name="event0", + time_unix_nano=otel_spans[0] + .events[0] + .timestamp, + attributes=[ + PB2KeyValue( + key="annotation_bool", + value=PB2AnyValue( + bool_value=True + ), + ), + PB2KeyValue( + key="annotation_string", + value=PB2AnyValue( + string_value="annotation_test" + ), + ), + PB2KeyValue( + key="key_float", + value=PB2AnyValue( + double_value=0.3 + ), + ), + ], + ) + ], + links=[ + PB2SPan.Link( + trace_id=_encode_trace_id( + otel_spans[0] + .links[0] + .context.trace_id + ), + span_id=_encode_span_id( + otel_spans[0] + .links[0] + .context.span_id + ), + attributes=[ + PB2KeyValue( + key="key_bool", + value=PB2AnyValue( + bool_value=True + ), + ), + ], + ) + ], + status=PB2Status( + deprecated_code=PB2Status.DEPRECATED_STATUS_CODE_UNKNOWN_ERROR, # pylint: disable=no-member + code=SDKStatusCode.ERROR.value, + message="Example description", + ), + ) + ], + ), + PB2InstrumentationLibrarySpans( + instrumentation_library=PB2InstrumentationLibrary( + name="name", + version="version", + ), + spans=[ + PB2SPan( + trace_id=trace_id, + span_id=_encode_span_id( + otel_spans[3].context.span_id + ), + trace_state=None, + parent_span_id=None, + name=otel_spans[3].name, + kind=span_kind, + start_time_unix_nano=otel_spans[ + 3 + ].start_time, + end_time_unix_nano=otel_spans[3].end_time, + attributes=None, + events=None, + links=None, + status={}, + ) + ], + ), + ], + ), + PB2ResourceSpans( + resource=PB2Resource( + attributes=[ + PB2KeyValue( + key="key_resource", + value=PB2AnyValue( + string_value="some_resource" + ), + ) + ] + ), + instrumentation_library_spans=[ + PB2InstrumentationLibrarySpans( + instrumentation_library=PB2InstrumentationLibrary(), + spans=[ + PB2SPan( + trace_id=trace_id, + span_id=_encode_span_id( + otel_spans[1].context.span_id + ), + trace_state=None, + parent_span_id=None, + name=otel_spans[1].name, + kind=span_kind, + start_time_unix_nano=otel_spans[ + 1 + ].start_time, + end_time_unix_nano=otel_spans[1].end_time, + attributes=None, + events=None, + links=None, + status={}, + ), + PB2SPan( + trace_id=trace_id, + span_id=_encode_span_id( + otel_spans[2].context.span_id + ), + trace_state=None, + parent_span_id=None, + name=otel_spans[2].name, + kind=span_kind, + start_time_unix_nano=otel_spans[ + 2 + ].start_time, + end_time_unix_nano=otel_spans[2].end_time, + attributes=[ + PB2KeyValue( + key="key_string", + value=PB2AnyValue( + string_value="hello_world" + ), + ), + ], + events=None, + links=None, + status={}, + ), + ], + ) + ], + ), + ] + ) + + return otel_spans, pb2_service_request + + def test_encode_status_code_translations(self): + self.assertEqual( + _encode_status(SDKStatus(status_code=SDKStatusCode.UNSET)), + PB2Status( + deprecated_code=PB2Status.DEPRECATED_STATUS_CODE_OK, # pylint: disable=no-member + code=SDKStatusCode.UNSET.value, + ), + ) + + self.assertEqual( + _encode_status(SDKStatus(status_code=SDKStatusCode.OK)), + PB2Status( + deprecated_code=PB2Status.DEPRECATED_STATUS_CODE_OK, # pylint: disable=no-member + code=SDKStatusCode.OK.value, + ), + ) + + self.assertEqual( + _encode_status(SDKStatus(status_code=SDKStatusCode.ERROR)), + PB2Status( + deprecated_code=PB2Status.DEPRECATED_STATUS_CODE_UNKNOWN_ERROR, # pylint: disable=no-member + code=SDKStatusCode.ERROR.value, + ), + ) diff --git a/exporter/opentelemetry-exporter-otlp/README.rst b/exporter/opentelemetry-exporter-otlp/README.rst index 06f45540de..6618dd2e04 100644 --- a/exporter/opentelemetry-exporter-otlp/README.rst +++ b/exporter/opentelemetry-exporter-otlp/README.rst @@ -8,9 +8,9 @@ OpenTelemetry Collector Exporters This library is provided as a convenience to install all supported OpenTelemetry Collector Exporters. Currently it installs: * opentelemetry-exporter-otlp-proto-grpc +* opentelemetry-exporter-otlp-proto-http In the future, additional packages will be available: -* opentelemetry-exporter-otlp-proto-http * opentelemetry-exporter-otlp-json-http To avoid unnecessary dependencies, users should install the specific package once they've determined their diff --git a/exporter/opentelemetry-exporter-otlp/setup.cfg b/exporter/opentelemetry-exporter-otlp/setup.cfg index 5502be5f8e..89fd254da0 100644 --- a/exporter/opentelemetry-exporter-otlp/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp/setup.cfg @@ -39,3 +39,4 @@ python_requires = >=3.6 packages=find_namespace: install_requires = opentelemetry-exporter-otlp-proto-grpc == 1.3.0.dev0 + opentelemetry-exporter-otlp-proto-http == 1.3.0.dev0 diff --git a/tox.ini b/tox.ini index f0ee49e971..9bb9e14805 100644 --- a/tox.ini +++ b/tox.ini @@ -49,6 +49,10 @@ envlist = py3{6,7,8,9}-test-exporter-otlp-proto-grpc ; intentionally excluded from pypy3 + ; opentelemetry-exporter-otlp-proto-http + py3{6,7,8,9}-test-exporter-otlp-proto-http + pypy3-test-exporter-otlp-proto-http + ; opentelemetry-exporter-zipkin py3{6,7,8,9}-test-exporter-zipkin-combined pypy3-test-exporter-zipkin-combined @@ -105,6 +109,7 @@ 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-otlp-proto-http: exporter/opentelemetry-exporter-otlp-proto-http/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 @@ -128,11 +133,15 @@ commands_pre = exporter-otlp-combined: pip install {toxinidir}/opentelemetry-proto exporter-otlp-combined: pip install {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-grpc + exporter-otlp-combined: pip install {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-http exporter-otlp-combined: pip install {toxinidir}/exporter/opentelemetry-exporter-otlp exporter-otlp-proto-grpc: pip install {toxinidir}/opentelemetry-proto exporter-otlp-proto-grpc: pip install {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-grpc + exporter-otlp-proto-http: pip install {toxinidir}/opentelemetry-proto + exporter-otlp-proto-http: pip install {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-http + exporter-jaeger-combined: pip install {toxinidir}/exporter/opentelemetry-exporter-jaeger-proto-grpc {toxinidir}/exporter/opentelemetry-exporter-jaeger-thrift {toxinidir}/exporter/opentelemetry-exporter-jaeger exporter-jaeger-proto-grpc: pip install {toxinidir}/exporter/opentelemetry-exporter-jaeger-proto-grpc exporter-jaeger-thrift: pip install {toxinidir}/exporter/opentelemetry-exporter-jaeger-thrift @@ -199,6 +208,7 @@ commands_pre = python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-jaeger[test] 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-proto-http[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] @@ -262,6 +272,7 @@ commands_pre = -e {toxinidir}/exporter/opentelemetry-exporter-opencensus \ -e {toxinidir}/opentelemetry-proto \ -e {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-grpc \ + -e {toxinidir}/exporter/opentelemetry-exporter-otlp-proto-http \ -e {toxinidir}/exporter/opentelemetry-exporter-otlp docker-compose up -d commands = From 24283181ebd14df9b91628165baadf205fc2150e Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Sun, 23 May 2021 12:23:32 +0530 Subject: [PATCH 02/15] Add class var for retry timeout --- .../exporter/otlp/proto/http/trace_exporter/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py index 7120047eb3..e28c96ccb2 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py @@ -51,6 +51,9 @@ class OTLPSpanExporter(SpanExporter): + + _MAX_RETRY_TIMEOUT = 64 + def __init__( self, endpoint: Optional[str] = None, @@ -119,11 +122,10 @@ def export(self, spans) -> SpanExportResult: return SpanExportResult.FAILURE serialized_data = ProtobufEncoder.serialize(spans) - max_value = 64 - for delay in expo(max_value=max_value): + for delay in expo(max_value=self._MAX_RETRY_TIMEOUT): - if delay == max_value: + if delay == self._MAX_RETRY_TIMEOUT: return SpanExportResult.FAILURE resp = self._export(serialized_data) From 80075179144c20f0a9575efa20426a2e4dd504d8 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Sun, 23 May 2021 15:58:06 +0530 Subject: [PATCH 03/15] Update default endpoint --- .../exporter/otlp/proto/http/trace_exporter/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py index e28c96ccb2..f7d2fde6bc 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py @@ -46,7 +46,7 @@ DEFAULT_COMPRESSION = Compression.NoCompression -DEFAULT_ENDPOINT = "https://localhost:4317" +DEFAULT_ENDPOINT = "http://localhost:55681/v1/traces" DEFAULT_TIMEOUT = 10 # in seconds From 391af282aa7d00d406e32390e33b790087d48654 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Sat, 26 Jun 2021 06:15:00 +0530 Subject: [PATCH 04/15] fix version failures --- eachdist.ini | 28 +++++++++---------- .../setup.cfg | 6 ++-- .../exporter/otlp/proto/http/version.py | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/eachdist.ini b/eachdist.ini index d785393354..13f0ffb68f 100644 --- a/eachdist.ini +++ b/eachdist.ini @@ -18,17 +18,17 @@ version=1.4.0.dev0 packages= opentelemetry-sdk opentelemetry-proto - propagator/opentelemetry-propagator-jaeger - propagator/opentelemetry-propagator-b3 - exporter/opentelemetry-exporter-zipkin-proto-http - exporter/opentelemetry-exporter-zipkin-json - exporter/opentelemetry-exporter-zipkin - exporter/opentelemetry-exporter-otlp-proto-grpc - exporter/opentelemetry-exporter-otlp-proto-http - exporter/opentelemetry-exporter-otlp - exporter/opentelemetry-exporter-jaeger-thrift - exporter/opentelemetry-exporter-jaeger-proto-grpc - exporter/opentelemetry-exporter-jaeger + opentelemetry-propagator-jaeger + opentelemetry-propagator-b3 + opentelemetry-exporter-zipkin-proto-http + opentelemetry-exporter-zipkin-json + opentelemetry-exporter-zipkin + opentelemetry-exporter-otlp-proto-grpc + opentelemetry-exporter-otlp-proto-http + opentelemetry-exporter-otlp + opentelemetry-exporter-jaeger-thrift + opentelemetry-exporter-jaeger-proto-grpc + opentelemetry-exporter-jaeger opentelemetry-api [prerelease] @@ -51,9 +51,9 @@ packages= opentelemetry-exporter-prometheus opentelemetry-api opentelemetry-sdk - exporter/opentelemetry-exporter-otlp-proto-grpc - exporter/opentelemetry-exporter-otlp-proto-http - exporter/opentelemetry-exporter-otlp + opentelemetry-exporter-otlp-proto-grpc + opentelemetry-exporter-otlp-proto-http + opentelemetry-exporter-otlp [lintroots] extraroots=examples/*,scripts/ diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg b/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg index 67e54a4b91..2a5dff59aa 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg @@ -41,9 +41,9 @@ packages=find_namespace: install_requires = requests ~= 2.7 googleapis-common-protos ~= 1.52 - opentelemetry-api == 1.3.0.dev0 - opentelemetry-sdk == 1.3.0.dev0 - opentelemetry-proto == 1.3.0.dev0 + opentelemetry-api == 1.4.0.dev0 + opentelemetry-sdk == 1.4.0.dev0 + opentelemetry-proto == 1.4.0.dev0 backoff ~= 1.10.0 [options.extras_require] diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py index f94a1f8a6c..48c4fb840e 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.3.0.dev0" +__version__ = "1.4.0.dev0" From d1713699f9a664efa82cbaafbd13e85583be5025 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 30 Jun 2021 04:21:30 +0530 Subject: [PATCH 05/15] Add CHANGELOG entry --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 306004537d..e9e75fb3a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.3.0-0.22b0...HEAD) +### Added + +- Add support for OTLP Exporter Protobuf over HTTP + ([#1868](https://github.com/open-telemetry/opentelemetry-python/pull/1868)) + ### Changed - Updated `opentelemetry-opencensus-exporter` to use `service_name` of spans instead of resource ([#1897](https://github.com/open-telemetry/opentelemetry-python/pull/1897)) From 176d60a487c26acaae1c6fdebf33370b532dc8a4 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 30 Jun 2021 04:22:02 +0530 Subject: [PATCH 06/15] Add functional test for OTLP/HTTP exporter --- .../exporter/otlp/proto/http/__init__.py | 2 +- .../tests/docker-compose.yml | 3 +- .../tests/otlpexporter/__init__.py | 48 +++++++++++++++++++ .../test_otlp_grpc_exporter_functional.py | 35 ++++---------- .../test_otlp_http_exporter_functional.py | 37 ++++++++++++++ 5 files changed, 96 insertions(+), 29 deletions(-) create mode 100644 tests/opentelemetry-docker-tests/tests/otlpexporter/__init__.py create mode 100644 tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_http_exporter_functional.py diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py index c3be0f2c21..08b0725835 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py @@ -57,7 +57,7 @@ trace.set_tracer_provider(TracerProvider(resource=resource)) tracer = trace.get_tracer(__name__) - otlp_exporter = OTLPSpanExporter(endpoint="http://localhost:4317", insecure=True) + otlp_exporter = OTLPSpanExporter() span_processor = BatchSpanProcessor(otlp_exporter) diff --git a/tests/opentelemetry-docker-tests/tests/docker-compose.yml b/tests/opentelemetry-docker-tests/tests/docker-compose.yml index c0566a6f00..b1cd2d6e1f 100644 --- a/tests/opentelemetry-docker-tests/tests/docker-compose.yml +++ b/tests/opentelemetry-docker-tests/tests/docker-compose.yml @@ -10,4 +10,5 @@ services: otcollector: image: otel/opentelemetry-collector:0.22.0 ports: - - "4317:4317" \ No newline at end of file + - "4317:4317" + - "55681:55681" diff --git a/tests/opentelemetry-docker-tests/tests/otlpexporter/__init__.py b/tests/opentelemetry-docker-tests/tests/otlpexporter/__init__.py new file mode 100644 index 0000000000..d4340fb910 --- /dev/null +++ b/tests/opentelemetry-docker-tests/tests/otlpexporter/__init__.py @@ -0,0 +1,48 @@ +# 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. + +from abc import ABC, abstractmethod + +from opentelemetry.context import attach, detach, set_value +from opentelemetry.sdk.trace.export import SimpleSpanProcessor + + +class ExportStatusSpanProcessor(SimpleSpanProcessor): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.export_status = [] + + def on_end(self, span): + token = attach(set_value("suppress_instrumentation", True)) + self.export_status.append(self.span_exporter.export((span,))) + detach(token) + + +class BaseTestOTLPExporter(ABC): + @abstractmethod + def get_span_processor(self): + pass + + # pylint: disable=no-member + def test_export(self): + with self.tracer.start_as_current_span("foo"): + with self.tracer.start_as_current_span("bar"): + with self.tracer.start_as_current_span("baz"): + pass + + self.assertTrue(len(self.span_processor.export_status), 3) + + for export_status in self.span_processor.export_status: + self.assertEqual(export_status.name, "SUCCESS") + self.assertEqual(export_status.value, 0) diff --git a/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_grpc_exporter_functional.py b/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_grpc_exporter_functional.py index 789dcc7d10..d48b305396 100644 --- a/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_grpc_exporter_functional.py +++ b/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_grpc_exporter_functional.py @@ -13,46 +13,27 @@ # limitations under the License. from opentelemetry import trace -from opentelemetry.context import attach, detach, set_value from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import ( OTLPSpanExporter, ) from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import SimpleSpanProcessor from opentelemetry.test.test_base import TestBase +from . import BaseTestOTLPExporter, ExportStatusSpanProcessor -class ExportStatusSpanProcessor(SimpleSpanProcessor): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.export_status = [] - - def on_end(self, span): - token = attach(set_value("suppress_instrumentation", True)) - self.export_status.append(self.span_exporter.export((span,))) - detach(token) +class TestOTLPGRPCExporter(BaseTestOTLPExporter, TestBase): + # pylint: disable=no-self-use + def get_span_processor(self): + return ExportStatusSpanProcessor( + OTLPSpanExporter(insecure=True, timeout=1) + ) -class TestOTLPExporter(TestBase): def setUp(self): super().setUp() trace.set_tracer_provider(TracerProvider()) self.tracer = trace.get_tracer(__name__) - self.span_processor = ExportStatusSpanProcessor( - OTLPSpanExporter(insecure=True, timeout=1) - ) + self.span_processor = self.get_span_processor() trace.get_tracer_provider().add_span_processor(self.span_processor) - - def test_export(self): - with self.tracer.start_as_current_span("foo"): - with self.tracer.start_as_current_span("bar"): - with self.tracer.start_as_current_span("baz"): - pass - - self.assertTrue(len(self.span_processor.export_status), 3) - - for export_status in self.span_processor.export_status: - self.assertEqual(export_status.name, "SUCCESS") - self.assertEqual(export_status.value, 0) diff --git a/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_http_exporter_functional.py b/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_http_exporter_functional.py new file mode 100644 index 0000000000..59a333dec6 --- /dev/null +++ b/tests/opentelemetry-docker-tests/tests/otlpexporter/test_otlp_http_exporter_functional.py @@ -0,0 +1,37 @@ +# 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. + +from opentelemetry import trace +from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( + OTLPSpanExporter, +) +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.test.test_base import TestBase + +from . import BaseTestOTLPExporter, ExportStatusSpanProcessor + + +class TestOTLPHTTPExporter(BaseTestOTLPExporter, TestBase): + # pylint: disable=no-self-use + def get_span_processor(self): + return ExportStatusSpanProcessor(OTLPSpanExporter()) + + def setUp(self): + super().setUp() + + trace.set_tracer_provider(TracerProvider()) + self.tracer = trace.get_tracer(__name__) + self.span_processor = self.get_span_processor() + + trace.get_tracer_provider().add_span_processor(self.span_processor) From c252c38c240e7ea198172b284af6ee86ca56dfae Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 1 Jul 2021 20:45:29 +0530 Subject: [PATCH 07/15] Update exporter/opentelemetry-exporter-otlp-proto-http/LICENSE Co-authored-by: Diego Hurtado --- exporter/opentelemetry-exporter-otlp-proto-http/LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/LICENSE b/exporter/opentelemetry-exporter-otlp-proto-http/LICENSE index 261eeb9e9f..1ef7dad2c5 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/LICENSE +++ b/exporter/opentelemetry-exporter-otlp-proto-http/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + 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. From 92e005f4e6944954b47845a354691eb9395f9e67 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 1 Jul 2021 20:49:26 +0530 Subject: [PATCH 08/15] Add review suggestion --- .../exporter/otlp/proto/http/trace_exporter/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py index f7d2fde6bc..ce06e1b96c 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py @@ -87,7 +87,7 @@ def __init__( self._session.headers.update( {"Content-Encoding": self._compression.value} ) - self._closed = False + self._shutdown = False def _export(self, serialized_data: str): data = serialized_data @@ -117,7 +117,7 @@ def _retryable(resp: requests.Response) -> bool: def export(self, spans) -> SpanExportResult: # After the call to Shutdown subsequent calls to Export are # not allowed and should return a Failure result. - if self._closed: + if self._shutdown: _logger.warning("Exporter already shutdown, ignoring batch") return SpanExportResult.FAILURE @@ -148,11 +148,11 @@ def export(self, spans) -> SpanExportResult: return SpanExportResult.FAILURE def shutdown(self): - if self._closed: + if self._shutdown: _logger.warning("Exporter already shutdown, ignoring call") return self._session.close() - self._closed = True + self._shutdown = True def _headers_from_env() -> Optional[Dict[str, str]]: From f66811aac8c4ec4b87580f35cd21b62fc090b152 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Sat, 3 Jul 2021 02:42:45 +0530 Subject: [PATCH 09/15] Make proto encoder private --- .../exporter/otlp/proto/http/trace_exporter/__init__.py | 6 +++--- .../otlp/proto/http/trace_exporter/encoder/__init__.py | 2 +- .../tests/test_proto_span_exporter.py | 2 -- .../tests/test_protobuf_encoder.py | 8 ++++---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py index ce06e1b96c..c88ba80068 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py @@ -38,7 +38,7 @@ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult from opentelemetry.exporter.otlp.proto.http import Compression from opentelemetry.exporter.otlp.proto.http.trace_exporter.encoder import ( - ProtobufEncoder, + _ProtobufEncoder, ) @@ -81,7 +81,7 @@ def __init__( self._session = requests.Session() self._session.headers.update(self._headers) self._session.headers.update( - {"Content-Type": ProtobufEncoder._CONTENT_TYPE} + {"Content-Type": _ProtobufEncoder._CONTENT_TYPE} ) if self._compression is not Compression.NoCompression: self._session.headers.update( @@ -121,7 +121,7 @@ def export(self, spans) -> SpanExportResult: _logger.warning("Exporter already shutdown, ignoring batch") return SpanExportResult.FAILURE - serialized_data = ProtobufEncoder.serialize(spans) + serialized_data = _ProtobufEncoder.serialize(spans) for delay in expo(max_value=self._MAX_RETRY_TIMEOUT): diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py index 80b59ea9c7..41acd8987f 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py @@ -64,7 +64,7 @@ _logger = logging.getLogger(__name__) -class ProtobufEncoder: +class _ProtobufEncoder: _CONTENT_TYPE = "application/x-protobuf" @classmethod diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py index cc19594ddf..f2dd7aa66d 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py @@ -22,8 +22,6 @@ DEFAULT_TIMEOUT, OTLPSpanExporter, ) - -# from opentelemetry.exporter.otlp.encoder.protobuf import ProtobufEncoder from opentelemetry.sdk.environment_variables import ( OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_COMPRESSION, diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_protobuf_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_protobuf_encoder.py index d7bf607c82..676b5f7b7a 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_protobuf_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_protobuf_encoder.py @@ -19,10 +19,10 @@ from opentelemetry.exporter.otlp.proto.http.trace_exporter.encoder import ( _SPAN_KIND_MAP, - ProtobufEncoder, _encode_span_id, _encode_status, _encode_trace_id, + _ProtobufEncoder, ) from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ( ExportTraceServiceRequest as PB2ExportTraceServiceRequest, @@ -61,19 +61,19 @@ class TestProtobufEncoder(unittest.TestCase): def test_encode(self): otel_spans, expected_encoding = self.get_exhaustive_test_spans() self.assertEqual( - ProtobufEncoder().encode(otel_spans), expected_encoding + _ProtobufEncoder().encode(otel_spans), expected_encoding ) def test_serialize(self): otel_spans, expected_encoding = self.get_exhaustive_test_spans() self.assertEqual( - ProtobufEncoder().serialize(otel_spans), + _ProtobufEncoder().serialize(otel_spans), expected_encoding.SerializeToString(), ) def test_content_type(self): self.assertEqual( - ProtobufEncoder._CONTENT_TYPE, "application/x-protobuf" + _ProtobufEncoder._CONTENT_TYPE, "application/x-protobuf" ) @staticmethod From 8c283f987b1894a53bda0c00bf125328e5ac0041 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Sat, 3 Jul 2021 04:03:57 +0530 Subject: [PATCH 10/15] Update exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py --- .../http/trace_exporter/encoder/__init__.py | 108 +++++++++--------- 1 file changed, 52 insertions(+), 56 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py index 41acd8987f..7a5adad7b3 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/encoder/__init__.py @@ -38,27 +38,23 @@ ) from opentelemetry.proto.trace.v1.trace_pb2 import Span as PB2SPan from opentelemetry.proto.trace.v1.trace_pb2 import Status as PB2Status -from opentelemetry.sdk.trace import Event as SDKEvent -from opentelemetry.sdk.trace import ( - InstrumentationInfo as SDKInstrumentationInfo, -) -from opentelemetry.sdk.trace import Resource as SDKResource +from opentelemetry.sdk.trace import Event +from opentelemetry.sdk.util.instrumentation import InstrumentationInfo +from opentelemetry.sdk.trace import Resource from opentelemetry.sdk.trace import Span as SDKSpan -from opentelemetry.sdk.trace import SpanContext as SDKSpanContext -from opentelemetry.trace import Link as SDKLink -from opentelemetry.trace import SpanKind as SDKSpanKind -from opentelemetry.trace import TraceState as SDKTraceState -from opentelemetry.trace.status import Status as SDKStatus -from opentelemetry.trace.status import StatusCode as SDKStatusCode -from opentelemetry.util.types import Attributes as SDKAttributes +from opentelemetry.trace import Link +from opentelemetry.trace import SpanKind +from opentelemetry.trace.span import SpanContext, TraceState, Status +from opentelemetry.trace.status import StatusCode +from opentelemetry.util.types import Attributes # pylint: disable=E1101 _SPAN_KIND_MAP = { - SDKSpanKind.INTERNAL: PB2SPan.SpanKind.SPAN_KIND_INTERNAL, - SDKSpanKind.SERVER: PB2SPan.SpanKind.SPAN_KIND_SERVER, - SDKSpanKind.CLIENT: PB2SPan.SpanKind.SPAN_KIND_CLIENT, - SDKSpanKind.PRODUCER: PB2SPan.SpanKind.SPAN_KIND_PRODUCER, - SDKSpanKind.CONSUMER: PB2SPan.SpanKind.SPAN_KIND_CONSUMER, + SpanKind.INTERNAL: PB2SPan.SpanKind.SPAN_KIND_INTERNAL, + SpanKind.SERVER: PB2SPan.SpanKind.SPAN_KIND_SERVER, + SpanKind.CLIENT: PB2SPan.SpanKind.SPAN_KIND_CLIENT, + SpanKind.PRODUCER: PB2SPan.SpanKind.SPAN_KIND_PRODUCER, + SpanKind.CONSUMER: PB2SPan.SpanKind.SPAN_KIND_CONSUMER, } _logger = logging.getLogger(__name__) @@ -136,11 +132,11 @@ def _encode_resource_spans( def _encode_span(sdk_span: SDKSpan) -> PB2SPan: - sdk_context = sdk_span.get_span_context() + span_context = sdk_span.get_span_context() return PB2SPan( - trace_id=_encode_trace_id(sdk_context.trace_id), - span_id=_encode_span_id(sdk_context.span_id), - trace_state=_encode_trace_state(sdk_context.trace_state), + trace_id=_encode_trace_id(span_context.trace_id), + span_id=_encode_span_id(span_context.span_id), + trace_state=_encode_trace_state(span_context.trace_state), parent_span_id=_encode_parent_id(sdk_span.parent), name=sdk_span.name, kind=_SPAN_KIND_MAP[sdk_span.kind], @@ -154,17 +150,17 @@ def _encode_span(sdk_span: SDKSpan) -> PB2SPan: def _encode_events( - sdk_events: Sequence[SDKEvent], + events: Sequence[Event], ) -> Optional[List[PB2SPan.Event]]: pb2_events = None - if sdk_events: + if events: pb2_events = [] - for sdk_event in sdk_events: + for event in events: encoded_event = PB2SPan.Event( - name=sdk_event.name, - time_unix_nano=sdk_event.timestamp, + name=event.name, + time_unix_nano=event.timestamp, ) - for key, value in sdk_event.attributes.items(): + for key, value in event.attributes.items(): try: encoded_event.attributes.append( _encode_key_value(key, value) @@ -176,16 +172,16 @@ def _encode_events( return pb2_events -def _encode_links(sdk_links: List[SDKLink]) -> List[PB2SPan.Link]: +def _encode_links(links: List[Link]) -> List[PB2SPan.Link]: pb2_links = None - if sdk_links: + if links: pb2_links = [] - for sdk_link in sdk_links: + for link in links: encoded_link = PB2SPan.Link( - trace_id=_encode_trace_id(sdk_link.context.trace_id), - span_id=_encode_span_id(sdk_link.context.span_id), + trace_id=_encode_trace_id(link.context.trace_id), + span_id=_encode_span_id(link.context.span_id), ) - for key, value in sdk_link.attributes.items(): + for key, value in link.attributes.items(): try: encoded_link.attributes.append( _encode_key_value(key, value) @@ -197,34 +193,34 @@ def _encode_links(sdk_links: List[SDKLink]) -> List[PB2SPan.Link]: return pb2_links -def _encode_status(sdk_status: SDKStatus) -> Optional[PB2Status]: +def _encode_status(status: Status) -> Optional[PB2Status]: pb2_status = None - if sdk_status is not None: + if status is not None: deprecated_code = PB2Status.DEPRECATED_STATUS_CODE_OK - if sdk_status.status_code is SDKStatusCode.ERROR: + if status.status_code is StatusCode.ERROR: deprecated_code = PB2Status.DEPRECATED_STATUS_CODE_UNKNOWN_ERROR pb2_status = PB2Status( deprecated_code=deprecated_code, - code=sdk_status.status_code.value, - message=sdk_status.description, + code=status.status_code.value, + message=status.description, ) return pb2_status -def _encode_trace_state(sdk_trace_state: SDKTraceState) -> Optional[str]: +def _encode_trace_state(trace_state: TraceState) -> Optional[str]: pb2_trace_state = None - if sdk_trace_state is not None: + if trace_state is not None: pb2_trace_state = ",".join( [ "{}={}".format(key, value) - for key, value in (sdk_trace_state.items()) + for key, value in (trace_state.items()) ] ) return pb2_trace_state -def _encode_parent_id(context: Optional[SDKSpanContext]) -> Optional[bytes]: - if isinstance(context, SDKSpanContext): +def _encode_parent_id(context: Optional[SpanContext]) -> Optional[bytes]: + if isinstance(context, SpanContext): encoded_parent_id = _encode_span_id(context.span_id) else: encoded_parent_id = None @@ -232,23 +228,23 @@ def _encode_parent_id(context: Optional[SDKSpanContext]) -> Optional[bytes]: def _encode_attributes( - sdk_attributes: SDKAttributes, + attributes: Attributes, ) -> Optional[List[PB2KeyValue]]: - if sdk_attributes: - attributes = [] - for key, value in sdk_attributes.items(): + if attributes: + pb2_attributes = [] + for key, value in attributes.items(): try: - attributes.append(_encode_key_value(key, value)) + pb2_attributes.append(_encode_key_value(key, value)) except Exception as error: # pylint: disable=broad-except _logger.exception(error) else: - attributes = None - return attributes + pb2_attributes = None + return pb2_attributes -def _encode_resource(sdk_resource: SDKResource) -> PB2Resource: +def _encode_resource(resource: Resource) -> PB2Resource: pb2_resource = PB2Resource() - for key, value in sdk_resource.attributes.items(): + for key, value in resource.attributes.items(): try: # pylint: disable=no-member pb2_resource.attributes.append(_encode_key_value(key, value)) @@ -258,14 +254,14 @@ def _encode_resource(sdk_resource: SDKResource) -> PB2Resource: def _encode_instrumentation_library( - sdk_instrumentation_info: SDKInstrumentationInfo, + instrumentation_info: InstrumentationInfo, ) -> PB2InstrumentationLibrary: - if sdk_instrumentation_info is None: + if instrumentation_info is None: pb2_instrumentation_library = PB2InstrumentationLibrary() else: pb2_instrumentation_library = PB2InstrumentationLibrary( - name=sdk_instrumentation_info.name, - version=sdk_instrumentation_info.version, + name=instrumentation_info.name, + version=instrumentation_info.version, ) return pb2_instrumentation_library From f091c73c812346c02070ecbb482656234e3e3492 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Tue, 13 Jul 2021 21:13:35 +0530 Subject: [PATCH 11/15] Update exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py Co-authored-by: alrex --- .../exporter/otlp/proto/http/trace_exporter/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py index c88ba80068..061484d0e3 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py @@ -68,7 +68,7 @@ def __init__( ) self._certificate_file = certificate_file or environ.get( OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE, - environ.get(OTEL_EXPORTER_OTLP_CERTIFICATE, False), + environ.get(OTEL_EXPORTER_OTLP_CERTIFICATE, True), ) self._headers = headers or _headers_from_env() self._timeout = timeout or int( From 5d8cba12684431dc6a573ff849fa00981e491da0 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Tue, 13 Jul 2021 21:18:49 +0530 Subject: [PATCH 12/15] Update test --- .../tests/test_proto_span_exporter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py index f2dd7aa66d..9b4bb53ad4 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py @@ -48,7 +48,7 @@ def test_constructor_default(self): exporter = OTLPSpanExporter() self.assertEqual(exporter._endpoint, DEFAULT_ENDPOINT) - self.assertEqual(exporter._certificate_file, False) + self.assertEqual(exporter._certificate_file, True) self.assertEqual(exporter._timeout, DEFAULT_TIMEOUT) self.assertIs(exporter._compression, DEFAULT_COMPRESSION) self.assertEqual(exporter._headers, {}) From b5692fff42e8e606acb5ed9590fc6b5d72ebeff1 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Sun, 18 Jul 2021 19:00:41 +0530 Subject: [PATCH 13/15] Update version and status to beta --- exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg | 6 +++--- .../src/opentelemetry/exporter/otlp/proto/http/version.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg b/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg index 2a5dff59aa..9a344f82ac 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg @@ -23,7 +23,7 @@ url = https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/ platforms = any license = Apache-2.0 classifiers = - Development Status :: 5 - Production/Stable + Development Status :: 4 - Beta Intended Audience :: Developers License :: OSI Approved :: Apache Software License Programming Language :: Python @@ -41,8 +41,8 @@ packages=find_namespace: install_requires = requests ~= 2.7 googleapis-common-protos ~= 1.52 - opentelemetry-api == 1.4.0.dev0 - opentelemetry-sdk == 1.4.0.dev0 + opentelemetry-api ~= 1.3 + opentelemetry-sdk ~= 1.3 opentelemetry-proto == 1.4.0.dev0 backoff ~= 1.10.0 diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py index 48c4fb840e..c829b95757 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.4.0.dev0" +__version__ = "0.23.dev0" From 88f84b9bada6b5620e2b16a3572271f23680dfe9 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Sun, 18 Jul 2021 19:15:26 +0530 Subject: [PATCH 14/15] Remove beta proto http from otlp-combined --- exporter/opentelemetry-exporter-otlp/setup.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/exporter/opentelemetry-exporter-otlp/setup.cfg b/exporter/opentelemetry-exporter-otlp/setup.cfg index 0c9f3aa53d..d4a891e173 100644 --- a/exporter/opentelemetry-exporter-otlp/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp/setup.cfg @@ -39,4 +39,3 @@ python_requires = >=3.6 packages=find_namespace: install_requires = opentelemetry-exporter-otlp-proto-grpc == 1.4.0.dev0 - opentelemetry-exporter-otlp-proto-http == 1.4.0.dev0 From 17ab52eff9aff672e78c30f391c95bd7d9489cc2 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 28 Jul 2021 04:04:08 +0530 Subject: [PATCH 15/15] Bump version --- exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg | 2 +- .../src/opentelemetry/exporter/otlp/proto/http/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg b/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg index 9a344f82ac..c99b806e98 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg @@ -43,7 +43,7 @@ install_requires = googleapis-common-protos ~= 1.52 opentelemetry-api ~= 1.3 opentelemetry-sdk ~= 1.3 - opentelemetry-proto == 1.4.0.dev0 + opentelemetry-proto == 1.5.0.dev0 backoff ~= 1.10.0 [options.extras_require] diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py index c829b95757..5aeaec6556 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.23.dev0" +__version__ = "0.24.dev0"