diff --git a/exporter/opentelemetry-exporter-datadog/CHANGELOG.md b/exporter/opentelemetry-exporter-datadog/CHANGELOG.md deleted file mode 100644 index b7308e8e5f9..00000000000 --- a/exporter/opentelemetry-exporter-datadog/CHANGELOG.md +++ /dev/null @@ -1,31 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.15b0 - -Released 2020-11-02 - - - Make `SpanProcessor.on_start` accept parent Context - ([#1251](https://github.com/open-telemetry/opentelemetry-python/pull/1251)) - -## Version 0.14b0 - -Released 2020-10-13 - -- Add support for span resource labels and service name - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-exporter-datadog - ([#953](https://github.com/open-telemetry/opentelemetry-python/pull/953)) - -## 0.8b0 - -Released 2020-05-27 - -- Add exporter to Datadog - ([#572](https://github.com/open-telemetry/opentelemetry-python/pull/572)) - diff --git a/exporter/opentelemetry-exporter-datadog/README.rst b/exporter/opentelemetry-exporter-datadog/README.rst deleted file mode 100644 index cb97e5997fe..00000000000 --- a/exporter/opentelemetry-exporter-datadog/README.rst +++ /dev/null @@ -1,29 +0,0 @@ -OpenTelemetry Datadog Exporter -============================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-exporter-datadog.svg - :target: https://pypi.org/project/opentelemetry-exporter-datadog/ - -This library allows to export tracing data to `Datadog -`_. OpenTelemetry span event and links are not -supported. - -Installation ------------- - -:: - - pip install opentelemetry-exporter-datadog - - -.. _Datadog: https://www.datadoghq.com/ -.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/ - - -References ----------- - -* `Datadog `_ -* `OpenTelemetry Project `_ diff --git a/exporter/opentelemetry-exporter-datadog/setup.cfg b/exporter/opentelemetry-exporter-datadog/setup.cfg deleted file mode 100644 index 45bf200ee45..00000000000 --- a/exporter/opentelemetry-exporter-datadog/setup.cfg +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-exporter-datadog -description = Datadog Span Exporter for OpenTelemetry -long_description = file: README.rst -long_description_content_type = text/x-rst -author = OpenTelemetry Authors -author_email = cncf-opentelemetry-contributors@lists.cncf.io -url = https://github.com/open-telemetry/opentelemetry-python/exporter/opentelemetry-exporter-datadog -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - ddtrace>=0.34.0 - opentelemetry-api == 0.16.dev0 - opentelemetry-sdk == 0.16.dev0 - -[options.packages.find] -where = src - -[options.extras_require] -test = diff --git a/exporter/opentelemetry-exporter-datadog/setup.py b/exporter/opentelemetry-exporter-datadog/setup.py deleted file mode 100644 index 0c3bdf453fd..00000000000 --- a/exporter/opentelemetry-exporter-datadog/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "exporter", "datadog", "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-datadog/src/opentelemetry/exporter/datadog/__init__.py b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/__init__.py deleted file mode 100644 index 3294ba4e4e0..00000000000 --- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/__init__.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -The **OpenTelemetry Datadog Exporter** provides a span exporter from -`OpenTelemetry`_ traces to `Datadog`_ by using the Datadog Agent. - -Installation ------------- - -:: - - pip install opentelemetry-exporter-datadog - - -Usage ------ - -The Datadog exporter provides a span processor that must be added along with the -exporter. In addition, a formatter is provided to handle propagation of trace -context between OpenTelemetry-instrumented and Datadog-instrumented services in -a distributed trace. - -.. code:: python - - from opentelemetry import propagators, trace - from opentelemetry.exporter.datadog import DatadogExportSpanProcessor, DatadogSpanExporter - from opentelemetry.exporter.datadog.propagator import DatadogFormat - from opentelemetry.sdk.trace import TracerProvider - - trace.set_tracer_provider(TracerProvider()) - tracer = trace.get_tracer(__name__) - - exporter = DatadogSpanExporter( - agent_url="http://agent:8126", service="my-helloworld-service" - ) - - span_processor = DatadogExportSpanProcessor(exporter) - trace.get_tracer_provider().add_span_processor(span_processor) - - # Optional: use Datadog format for propagation in distributed traces - propagators.set_global_textmap(DatadogFormat()) - - with tracer.start_as_current_span("foo"): - print("Hello world!") - - -Examples --------- - -The `docs/examples/datadog_exporter`_ includes examples for using the Datadog -exporter with OpenTelemetry instrumented applications. - -API ---- -.. _Datadog: https://www.datadoghq.com/ -.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/ -.. _docs/examples/datadog_exporter: https://github.com/open-telemetry/opentelemetry-python/tree/master/docs/examples/datadog_exporter -""" -# pylint: disable=import-error - -from .exporter import DatadogSpanExporter -from .spanprocessor import DatadogExportSpanProcessor - -__all__ = ["DatadogExportSpanProcessor", "DatadogSpanExporter"] diff --git a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/constants.py b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/constants.py deleted file mode 100644 index 2ae5386e848..00000000000 --- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/constants.py +++ /dev/null @@ -1,9 +0,0 @@ -DD_ORIGIN = "_dd_origin" -AUTO_REJECT = 0 -AUTO_KEEP = 1 -USER_KEEP = 2 -SAMPLE_RATE_METRIC_KEY = "_sample_rate" -SAMPLING_PRIORITY_KEY = "_sampling_priority_v1" -ENV_KEY = "env" -VERSION_KEY = "version" -SERVICE_NAME_TAG = "service.name" diff --git a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py deleted file mode 100644 index 2b1bd900417..00000000000 --- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py +++ /dev/null @@ -1,317 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import os -from urllib.parse import urlparse - -from ddtrace.ext import SpanTypes as DatadogSpanTypes -from ddtrace.internal.writer import AgentWriter -from ddtrace.span import Span as DatadogSpan - -import opentelemetry.trace as trace_api -from opentelemetry.sdk.trace import sampling -from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult - -# pylint:disable=relative-beyond-top-level -from .constants import ( - DD_ORIGIN, - ENV_KEY, - SAMPLE_RATE_METRIC_KEY, - SERVICE_NAME_TAG, - VERSION_KEY, -) - -logger = logging.getLogger(__name__) - - -DEFAULT_AGENT_URL = "http://localhost:8126" -_INSTRUMENTATION_SPAN_TYPES = { - "opentelemetry.instrumentation.aiohttp-client": DatadogSpanTypes.HTTP, - "opentelemetry.instrumentation.asgi": DatadogSpanTypes.WEB, - "opentelemetry.instrumentation.dbapi": DatadogSpanTypes.SQL, - "opentelemetry.instrumentation.django": DatadogSpanTypes.WEB, - "opentelemetry.instrumentation.flask": DatadogSpanTypes.WEB, - "opentelemetry.instrumentation.grpc": DatadogSpanTypes.GRPC, - "opentelemetry.instrumentation.jinja2": DatadogSpanTypes.TEMPLATE, - "opentelemetry.instrumentation.mysql": DatadogSpanTypes.SQL, - "opentelemetry.instrumentation.psycopg2": DatadogSpanTypes.SQL, - "opentelemetry.instrumentation.pymemcache": DatadogSpanTypes.CACHE, - "opentelemetry.instrumentation.pymongo": DatadogSpanTypes.MONGODB, - "opentelemetry.instrumentation.pymysql": DatadogSpanTypes.SQL, - "opentelemetry.instrumentation.redis": DatadogSpanTypes.REDIS, - "opentelemetry.instrumentation.requests": DatadogSpanTypes.HTTP, - "opentelemetry.instrumentation.sqlalchemy": DatadogSpanTypes.SQL, - "opentelemetry.instrumentation.wsgi": DatadogSpanTypes.WEB, -} - - -class DatadogSpanExporter(SpanExporter): - """Datadog span exporter for OpenTelemetry. - - Args: - agent_url: The url of the Datadog Agent or use ``DD_TRACE_AGENT_URL`` environment variable - service: The service name to be used for the application or use ``DD_SERVICE`` environment variable - env: Set the application’s environment or use ``DD_ENV`` environment variable - version: Set the application’s version or use ``DD_VERSION`` environment variable - tags: A list of default tags to be added to every span or use ``DD_TAGS`` environment variable - """ - - def __init__( - self, agent_url=None, service=None, env=None, version=None, tags=None - ): - self.agent_url = ( - agent_url - if agent_url - else os.environ.get("DD_TRACE_AGENT_URL", DEFAULT_AGENT_URL) - ) - self.service = service or os.environ.get("DD_SERVICE") - self.env = env or os.environ.get("DD_ENV") - self.version = version or os.environ.get("DD_VERSION") - self.tags = _parse_tags_str(tags or os.environ.get("DD_TAGS")) - self._agent_writer = None - - @property - def agent_writer(self): - if self._agent_writer is None: - url_parsed = urlparse(self.agent_url) - if url_parsed.scheme in ("http", "https"): - self._agent_writer = AgentWriter( - hostname=url_parsed.hostname, - port=url_parsed.port, - https=url_parsed.scheme == "https", - ) - elif url_parsed.scheme == "unix": - self._agent_writer = AgentWriter(uds_path=url_parsed.path) - else: - raise ValueError( - "Unknown scheme `%s` for agent URL" % url_parsed.scheme - ) - return self._agent_writer - - def export(self, spans): - datadog_spans = self._translate_to_datadog(spans) - - self.agent_writer.write(spans=datadog_spans) - - return SpanExportResult.SUCCESS - - def shutdown(self): - if self.agent_writer.started: - self.agent_writer.stop() - self.agent_writer.join(self.agent_writer.exit_timeout) - - # pylint: disable=too-many-locals - def _translate_to_datadog(self, spans): - datadog_spans = [] - - for span in spans: - trace_id, parent_id, span_id = _get_trace_ids(span) - - # datadog Span is initialized with a reference to the tracer which is - # used to record the span when it is finished. We can skip ignore this - # because we are not calling the finish method and explictly set the - # duration. - tracer = None - - # extract resource attributes to be used as tags as well as potential service name - [ - resource_tags, - resource_service_name, - ] = _extract_tags_from_resource(span.resource) - - datadog_span = DatadogSpan( - tracer, - _get_span_name(span), - service=resource_service_name or self.service, - resource=_get_resource(span), - span_type=_get_span_type(span), - trace_id=trace_id, - span_id=span_id, - parent_id=parent_id, - ) - datadog_span.start_ns = span.start_time - datadog_span.duration_ns = span.end_time - span.start_time - - if not span.status.is_ok: - datadog_span.error = 1 - if span.status.description: - exc_type, exc_val = _get_exc_info(span) - # no mapping for error.stack since traceback not recorded - datadog_span.set_tag("error.msg", exc_val) - datadog_span.set_tag("error.type", exc_type) - - # combine resource attributes and span attributes, don't modify existing span attributes - combined_span_tags = {} - combined_span_tags.update(resource_tags) - combined_span_tags.update(span.attributes) - - datadog_span.set_tags(combined_span_tags) - - # add configured env tag - if self.env is not None: - datadog_span.set_tag(ENV_KEY, self.env) - - # add configured application version tag to only root span - if self.version is not None and parent_id == 0: - datadog_span.set_tag(VERSION_KEY, self.version) - - # add configured global tags - datadog_span.set_tags(self.tags) - - # add origin to root span - origin = _get_origin(span) - if origin and parent_id == 0: - datadog_span.set_tag(DD_ORIGIN, origin) - - sampling_rate = _get_sampling_rate(span) - if sampling_rate is not None: - datadog_span.set_metric(SAMPLE_RATE_METRIC_KEY, sampling_rate) - - # span events and span links are not supported - - datadog_spans.append(datadog_span) - - return datadog_spans - - -def _get_trace_ids(span): - """Extract tracer ids from span""" - ctx = span.get_span_context() - trace_id = ctx.trace_id - span_id = ctx.span_id - - if isinstance(span.parent, trace_api.Span): - parent_id = span.parent.get_span_context().span_id - elif isinstance(span.parent, trace_api.SpanContext): - parent_id = span.parent.span_id - else: - parent_id = 0 - - trace_id = _convert_trace_id_uint64(trace_id) - - return trace_id, parent_id, span_id - - -def _convert_trace_id_uint64(otel_id): - """Convert 128-bit int used for trace_id to 64-bit unsigned int""" - return otel_id & 0xFFFFFFFFFFFFFFFF - - -def _get_span_name(span): - """Get span name by using instrumentation and kind while backing off to - span.name - """ - instrumentation_name = ( - span.instrumentation_info.name if span.instrumentation_info else None - ) - span_kind_name = span.kind.name if span.kind else None - name = ( - "{}.{}".format(instrumentation_name, span_kind_name) - if instrumentation_name and span_kind_name - else span.name - ) - return name - - -def _get_resource(span): - """Get resource name for span""" - if "http.method" in span.attributes: - route = span.attributes.get("http.route") - return ( - span.attributes["http.method"] + " " + route - if route - else span.attributes["http.method"] - ) - - return span.name - - -def _get_span_type(span): - """Get Datadog span type""" - instrumentation_name = ( - span.instrumentation_info.name if span.instrumentation_info else None - ) - span_type = _INSTRUMENTATION_SPAN_TYPES.get(instrumentation_name) - return span_type - - -def _get_exc_info(span): - """Parse span status description for exception type and value""" - exc_type, exc_val = span.status.description.split(":", 1) - return exc_type, exc_val.strip() - - -def _get_origin(span): - ctx = span.get_span_context() - origin = ctx.trace_state.get(DD_ORIGIN) - return origin - - -def _get_sampling_rate(span): - ctx = span.get_span_context() - return ( - span.sampler.rate - if ctx.trace_flags.sampled - and isinstance(span.sampler, sampling.TraceIdRatioBased) - else None - ) - - -def _parse_tags_str(tags_str): - """Parse a string of tags typically provided via environment variables. - - The expected string is of the form:: - "key1:value1,key2:value2" - - :param tags_str: A string of the above form to parse tags from. - :return: A dict containing the tags that were parsed. - """ - parsed_tags = {} - if not tags_str: - return parsed_tags - - for tag in tags_str.split(","): - try: - key, value = tag.split(":", 1) - - # Validate the tag - if key == "" or value == "" or value.endswith(":"): - raise ValueError - except ValueError: - logger.error( - "Malformed tag in tag pair '%s' from tag string '%s'.", - tag, - tags_str, - ) - else: - parsed_tags[key] = value - - return parsed_tags - - -def _extract_tags_from_resource(resource): - """Parse tags from resource.attributes, except service.name which - has special significance within datadog""" - tags = {} - service_name = None - if not (resource and getattr(resource, "attributes", None)): - return [tags, service_name] - - for attribute_key, attribute_value in resource.attributes.items(): - if attribute_key == SERVICE_NAME_TAG: - service_name = attribute_value - else: - tags[attribute_key] = attribute_value - return [tags, service_name] diff --git a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/propagator.py b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/propagator.py deleted file mode 100644 index ab1468c54ab..00000000000 --- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/propagator.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import typing - -from opentelemetry import trace -from opentelemetry.context import Context -from opentelemetry.trace import get_current_span, set_span_in_context -from opentelemetry.trace.propagation.textmap import ( - Getter, - Setter, - TextMapPropagator, - TextMapPropagatorT, -) - -# pylint:disable=relative-beyond-top-level -from . import constants - - -class DatadogFormat(TextMapPropagator): - """Propagator for the Datadog HTTP header format. - """ - - TRACE_ID_KEY = "x-datadog-trace-id" - PARENT_ID_KEY = "x-datadog-parent-id" - SAMPLING_PRIORITY_KEY = "x-datadog-sampling-priority" - ORIGIN_KEY = "x-datadog-origin" - - def extract( - self, - getter: Getter[TextMapPropagatorT], - carrier: TextMapPropagatorT, - context: typing.Optional[Context] = None, - ) -> Context: - trace_id = extract_first_element( - getter.get(carrier, self.TRACE_ID_KEY) - ) - - span_id = extract_first_element( - getter.get(carrier, self.PARENT_ID_KEY) - ) - - sampled = extract_first_element( - getter.get(carrier, self.SAMPLING_PRIORITY_KEY) - ) - - origin = extract_first_element(getter.get(carrier, self.ORIGIN_KEY)) - - trace_flags = trace.TraceFlags() - if sampled and int(sampled) in ( - constants.AUTO_KEEP, - constants.USER_KEEP, - ): - trace_flags |= trace.TraceFlags.SAMPLED - - if trace_id is None or span_id is None: - return set_span_in_context(trace.INVALID_SPAN, context) - - span_context = trace.SpanContext( - trace_id=int(trace_id), - span_id=int(span_id), - is_remote=True, - trace_flags=trace_flags, - trace_state=trace.TraceState({constants.DD_ORIGIN: origin}), - ) - - return set_span_in_context(trace.DefaultSpan(span_context), context) - - def inject( - self, - set_in_carrier: Setter[TextMapPropagatorT], - carrier: TextMapPropagatorT, - context: typing.Optional[Context] = None, - ) -> None: - span = get_current_span(context) - span_context = span.get_span_context() - if span_context == trace.INVALID_SPAN_CONTEXT: - return - sampled = (trace.TraceFlags.SAMPLED & span.context.trace_flags) != 0 - set_in_carrier( - carrier, self.TRACE_ID_KEY, format_trace_id(span.context.trace_id), - ) - set_in_carrier( - carrier, self.PARENT_ID_KEY, format_span_id(span.context.span_id) - ) - set_in_carrier( - carrier, - self.SAMPLING_PRIORITY_KEY, - str(constants.AUTO_KEEP if sampled else constants.AUTO_REJECT), - ) - if constants.DD_ORIGIN in span.context.trace_state: - set_in_carrier( - carrier, - self.ORIGIN_KEY, - span.context.trace_state[constants.DD_ORIGIN], - ) - - -def format_trace_id(trace_id: int) -> str: - """Format the trace id for Datadog.""" - return str(trace_id & 0xFFFFFFFFFFFFFFFF) - - -def format_span_id(span_id: int) -> str: - """Format the span id for Datadog.""" - return str(span_id) - - -def extract_first_element( - items: typing.Iterable[TextMapPropagatorT], -) -> typing.Optional[TextMapPropagatorT]: - if items is None: - return None - return next(iter(items), None) diff --git a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/spanprocessor.py b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/spanprocessor.py deleted file mode 100644 index 3a1188e0bd3..00000000000 --- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/spanprocessor.py +++ /dev/null @@ -1,225 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import collections -import logging -import threading -import typing - -from opentelemetry.context import Context, attach, detach, set_value -from opentelemetry.sdk.trace import Span, SpanProcessor -from opentelemetry.sdk.trace.export import SpanExporter -from opentelemetry.trace import INVALID_TRACE_ID -from opentelemetry.util import time_ns - -logger = logging.getLogger(__name__) - - -class DatadogExportSpanProcessor(SpanProcessor): - """Datadog exporter span processor - - DatadogExportSpanProcessor is an implementation of `SpanProcessor` that - batches all opened spans into a list per trace. When all spans for a trace - are ended, the trace is queues up for export. This is required for exporting - to the Datadog Agent which expects to received list of spans for each trace. - """ - - _FLUSH_TOKEN = INVALID_TRACE_ID - - def __init__( - self, - span_exporter: SpanExporter, - schedule_delay_millis: float = 5000, - max_trace_size: int = 4096, - ): - if max_trace_size <= 0: - raise ValueError("max_queue_size must be a positive integer.") - - if schedule_delay_millis <= 0: - raise ValueError("schedule_delay_millis must be positive.") - - self.span_exporter = span_exporter - - # queue trace_ids for traces with recently ended spans for worker thread to check - # for exporting - self.check_traces_queue = ( - collections.deque() - ) # type: typing.Deque[int] - - self.traces_lock = threading.Lock() - # dictionary of trace_ids to a list of spans where the first span is the - # first opened span for the trace - self.traces = collections.defaultdict(list) - # counter to keep track of the number of spans and ended spans for a - # trace_id - self.traces_spans_count = collections.Counter() - self.traces_spans_ended_count = collections.Counter() - - self.worker_thread = threading.Thread(target=self.worker, daemon=True) - - # threading conditions used for flushing and shutdown - self.condition = threading.Condition(threading.Lock()) - self.flush_condition = threading.Condition(threading.Lock()) - - # flag to indicate that there is a flush operation on progress - self._flushing = False - - self.max_trace_size = max_trace_size - self._spans_dropped = False - self.schedule_delay_millis = schedule_delay_millis - self.done = False - self.worker_thread.start() - - def on_start( - self, span: Span, parent_context: typing.Optional[Context] = None - ) -> None: - ctx = span.get_span_context() - trace_id = ctx.trace_id - - with self.traces_lock: - # check upper bound on number of spans for trace before adding new - # span - if self.traces_spans_count[trace_id] == self.max_trace_size: - logger.warning("Max spans for trace, spans will be dropped.") - self._spans_dropped = True - return - - # add span to end of list for a trace and update the counter - self.traces[trace_id].append(span) - self.traces_spans_count[trace_id] += 1 - - def on_end(self, span: Span) -> None: - if self.done: - logger.warning("Already shutdown, dropping span.") - return - - ctx = span.get_span_context() - trace_id = ctx.trace_id - - with self.traces_lock: - self.traces_spans_ended_count[trace_id] += 1 - if self.is_trace_exportable(trace_id): - self.check_traces_queue.appendleft(trace_id) - - def worker(self): - timeout = self.schedule_delay_millis / 1e3 - while not self.done: - if not self._flushing: - with self.condition: - self.condition.wait(timeout) - if not self.check_traces_queue: - # spurious notification, let's wait again, reset timeout - timeout = self.schedule_delay_millis / 1e3 - continue - if self.done: - # missing spans will be sent when calling flush - break - - # substract the duration of this export call to the next timeout - start = time_ns() - self.export() - end = time_ns() - duration = (end - start) / 1e9 - timeout = self.schedule_delay_millis / 1e3 - duration - - # be sure that all spans are sent - self._drain_queue() - - def is_trace_exportable(self, trace_id): - return ( - self.traces_spans_count[trace_id] - - self.traces_spans_ended_count[trace_id] - <= 0 - ) - - def export(self) -> None: - """Exports traces with finished spans.""" - notify_flush = False - export_trace_ids = [] - - while self.check_traces_queue: - trace_id = self.check_traces_queue.pop() - if trace_id is self._FLUSH_TOKEN: - notify_flush = True - else: - with self.traces_lock: - # check whether trace is exportable again in case that new - # spans were started since we last concluded trace was - # exportable - if self.is_trace_exportable(trace_id): - export_trace_ids.append(trace_id) - del self.traces_spans_count[trace_id] - del self.traces_spans_ended_count[trace_id] - - if len(export_trace_ids) > 0: - token = attach(set_value("suppress_instrumentation", True)) - - for trace_id in export_trace_ids: - with self.traces_lock: - try: - # Ignore type b/c the Optional[None]+slicing is too "clever" - # for mypy - self.span_exporter.export(self.traces[trace_id]) # type: ignore - # pylint: disable=broad-except - except Exception: - logger.exception( - "Exception while exporting Span batch." - ) - finally: - del self.traces[trace_id] - - detach(token) - - if notify_flush: - with self.flush_condition: - self.flush_condition.notify() - - def _drain_queue(self): - """"Export all elements until queue is empty. - - Can only be called from the worker thread context because it invokes - `export` that is not thread safe. - """ - while self.check_traces_queue: - self.export() - - def force_flush(self, timeout_millis: int = 30000) -> bool: - if self.done: - logger.warning("Already shutdown, ignoring call to force_flush().") - return True - - self._flushing = True - self.check_traces_queue.appendleft(self._FLUSH_TOKEN) - - # wake up worker thread - with self.condition: - self.condition.notify_all() - - # wait for token to be processed - with self.flush_condition: - ret = self.flush_condition.wait(timeout_millis / 1e3) - - self._flushing = False - - if not ret: - logger.warning("Timeout was exceeded in force_flush().") - return ret - - def shutdown(self) -> None: - # signal the worker thread to finish and then wait for it - self.done = True - with self.condition: - self.condition.notify_all() - self.worker_thread.join() - self.span_exporter.shutdown() diff --git a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/version.py b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/exporter/opentelemetry-exporter-datadog/tests/__init__.py b/exporter/opentelemetry-exporter-datadog/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py b/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py deleted file mode 100644 index 7b94704a573..00000000000 --- a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py +++ /dev/null @@ -1,604 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import itertools -import logging -import time -import unittest -from unittest import mock - -from ddtrace.internal.writer import AgentWriter - -from opentelemetry import trace as trace_api -from opentelemetry.context import Context -from opentelemetry.exporter import datadog -from opentelemetry.sdk import trace -from opentelemetry.sdk.trace import Resource, sampling -from opentelemetry.sdk.util.instrumentation import InstrumentationInfo - - -class MockDatadogSpanExporter(datadog.DatadogSpanExporter): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - agent_writer_mock = mock.Mock(spec=AgentWriter) - agent_writer_mock.started = True - agent_writer_mock.exit_timeout = 1 - self._agent_writer = agent_writer_mock - - -def get_spans(tracer, exporter, shutdown=True): - if shutdown: - tracer.span_processor.shutdown() - - spans = [ - call_args[-1]["spans"] - for call_args in exporter.agent_writer.write.call_args_list - ] - - return [span.to_dict() for span in itertools.chain.from_iterable(spans)] - - -class TestDatadogSpanExporter(unittest.TestCase): - def setUp(self): - self.exporter = MockDatadogSpanExporter() - self.span_processor = datadog.DatadogExportSpanProcessor(self.exporter) - tracer_provider = trace.TracerProvider() - tracer_provider.add_span_processor(self.span_processor) - self.tracer_provider = tracer_provider - self.tracer = tracer_provider.get_tracer(__name__) - - def tearDown(self): - self.tracer_provider.shutdown() - - def test_constructor_default(self): - """Test the default values assigned by constructor.""" - exporter = datadog.DatadogSpanExporter() - - self.assertEqual(exporter.agent_url, "http://localhost:8126") - self.assertIsNone(exporter.service) - self.assertIsNotNone(exporter.agent_writer) - - def test_constructor_explicit(self): - """Test the constructor passing all the options.""" - agent_url = "http://localhost:8126" - exporter = datadog.DatadogSpanExporter( - agent_url=agent_url, service="explicit", - ) - - self.assertEqual(exporter.agent_url, agent_url) - self.assertEqual(exporter.service, "explicit") - self.assertIsNone(exporter.env) - self.assertIsNone(exporter.version) - self.assertEqual(exporter.tags, {}) - - exporter = datadog.DatadogSpanExporter( - agent_url=agent_url, - service="explicit", - env="test", - version="0.0.1", - tags="", - ) - - self.assertEqual(exporter.agent_url, agent_url) - self.assertEqual(exporter.service, "explicit") - self.assertEqual(exporter.env, "test") - self.assertEqual(exporter.version, "0.0.1") - self.assertEqual(exporter.tags, {}) - - exporter = datadog.DatadogSpanExporter( - agent_url=agent_url, - service="explicit", - env="test", - version="0.0.1", - tags="team:testers,layer:app", - ) - - self.assertEqual(exporter.agent_url, agent_url) - self.assertEqual(exporter.service, "explicit") - self.assertEqual(exporter.env, "test") - self.assertEqual(exporter.version, "0.0.1") - self.assertEqual(exporter.tags, {"team": "testers", "layer": "app"}) - - @mock.patch.dict( - "os.environ", - { - "DD_TRACE_AGENT_URL": "http://agent:8126", - "DD_SERVICE": "test-service", - "DD_ENV": "test", - "DD_VERSION": "0.0.1", - "DD_TAGS": "team:testers", - }, - ) - def test_constructor_environ(self): - exporter = datadog.DatadogSpanExporter() - - self.assertEqual(exporter.agent_url, "http://agent:8126") - self.assertEqual(exporter.service, "test-service") - self.assertEqual(exporter.env, "test") - self.assertEqual(exporter.version, "0.0.1") - self.assertEqual(exporter.tags, {"team": "testers"}) - self.assertIsNotNone(exporter.agent_writer) - - # pylint: disable=too-many-locals - @mock.patch.dict( - "os.environ", - { - "DD_SERVICE": "test-service", - "DD_ENV": "test", - "DD_VERSION": "0.0.1", - "DD_TAGS": "team:testers", - }, - ) - def test_translate_to_datadog(self): - # pylint: disable=invalid-name - self.maxDiff = None - - resource = Resource( - attributes={ - "key_resource": "some_resource", - "service.name": "resource_service_name", - } - ) - - resource_without_service = Resource( - attributes={"conflicting_key": "conflicting_value"} - ) - - span_names = ("test1", "test2", "test3") - trace_id = 0x6E0C63257DE34C926F9EFCD03927272E - trace_id_low = 0x6F9EFCD03927272E - span_id = 0x34BF92DEEFC58C92 - parent_id = 0x1111111111111111 - other_id = 0x2222222222222222 - - base_time = 683647322 * 10 ** 9 # in ns - start_times = ( - base_time, - base_time + 150 * 10 ** 6, - base_time + 300 * 10 ** 6, - ) - durations = (50 * 10 ** 6, 100 * 10 ** 6, 200 * 10 ** 6) - end_times = ( - start_times[0] + durations[0], - start_times[1] + durations[1], - start_times[2] + durations[2], - ) - - span_context = trace_api.SpanContext( - trace_id, span_id, is_remote=False - ) - parent_span_context = trace_api.SpanContext( - trace_id, parent_id, is_remote=False - ) - other_context = trace_api.SpanContext( - trace_id, other_id, is_remote=False - ) - - instrumentation_info = InstrumentationInfo(__name__, "0") - - otel_spans = [ - trace._Span( - name=span_names[0], - context=span_context, - parent=parent_span_context, - kind=trace_api.SpanKind.CLIENT, - instrumentation_info=instrumentation_info, - resource=Resource({}), - ), - trace._Span( - name=span_names[1], - context=parent_span_context, - parent=None, - instrumentation_info=instrumentation_info, - resource=resource_without_service, - ), - trace._Span( - name=span_names[2], - context=other_context, - parent=None, - resource=resource, - ), - ] - - otel_spans[1].set_attribute("conflicting_key", "original_value") - - otel_spans[0].start(start_time=start_times[0]) - otel_spans[0].end(end_time=end_times[0]) - - otel_spans[1].start(start_time=start_times[1]) - otel_spans[1].end(end_time=end_times[1]) - - otel_spans[2].start(start_time=start_times[2]) - otel_spans[2].end(end_time=end_times[2]) - - # pylint: disable=protected-access - exporter = datadog.DatadogSpanExporter() - datadog_spans = [ - span.to_dict() - for span in exporter._translate_to_datadog(otel_spans) - ] - - expected_spans = [ - dict( - trace_id=trace_id_low, - parent_id=parent_id, - span_id=span_id, - name="tests.test_datadog_exporter.CLIENT", - resource=span_names[0], - start=start_times[0], - duration=durations[0], - error=0, - service="test-service", - meta={"env": "test", "team": "testers"}, - ), - dict( - trace_id=trace_id_low, - parent_id=0, - span_id=parent_id, - name="tests.test_datadog_exporter.INTERNAL", - resource=span_names[1], - start=start_times[1], - duration=durations[1], - error=0, - service="test-service", - meta={ - "env": "test", - "team": "testers", - "version": "0.0.1", - "conflicting_key": "original_value", - }, - ), - dict( - trace_id=trace_id_low, - parent_id=0, - span_id=other_id, - name=span_names[2], - resource=span_names[2], - start=start_times[2], - duration=durations[2], - error=0, - service="resource_service_name", - meta={ - "env": "test", - "team": "testers", - "version": "0.0.1", - "key_resource": "some_resource", - }, - ), - ] - - self.assertEqual(datadog_spans, expected_spans) - - def test_export(self): - """Test that agent and/or collector are invoked""" - # create and save span to be used in tests - context = trace_api.SpanContext( - trace_id=0x000000000000000000000000DEADBEEF, - span_id=0x00000000DEADBEF0, - is_remote=False, - ) - - test_span = trace._Span("test_span", context=context) - test_span.start() - test_span.end() - - self.exporter.export((test_span,)) - - self.assertEqual(self.exporter.agent_writer.write.call_count, 1) - - def test_resources(self): - test_attributes = [ - {}, - {"http.method": "GET", "http.route": "/foo/"}, - {"http.method": "GET", "http.target": "/foo/200"}, - ] - - for index, test in enumerate(test_attributes): - with self.tracer.start_span(str(index), attributes=test): - pass - - datadog_spans = get_spans(self.tracer, self.exporter) - - self.assertEqual(len(datadog_spans), 3) - - actual = [span["resource"] for span in datadog_spans] - expected = ["0", "GET /foo/", "GET"] - - self.assertEqual(actual, expected) - - def test_span_types(self): - test_instrumentations = [ - "opentelemetry.instrumentation.aiohttp-client", - "opentelemetry.instrumentation.dbapi", - "opentelemetry.instrumentation.django", - "opentelemetry.instrumentation.flask", - "opentelemetry.instrumentation.grpc", - "opentelemetry.instrumentation.jinja2", - "opentelemetry.instrumentation.mysql", - "opentelemetry.instrumentation.psycopg2", - "opentelemetry.instrumentation.pymongo", - "opentelemetry.instrumentation.pymysql", - "opentelemetry.instrumentation.redis", - "opentelemetry.instrumentation.requests", - "opentelemetry.instrumentation.sqlalchemy", - "opentelemetry.instrumentation.wsgi", - ] - - for index, instrumentation in enumerate(test_instrumentations): - # change tracer's instrumentation info before starting span - self.tracer.instrumentation_info = InstrumentationInfo( - instrumentation, "0" - ) - with self.tracer.start_span(str(index)): - pass - - datadog_spans = get_spans(self.tracer, self.exporter) - - self.assertEqual(len(datadog_spans), 14) - - actual = [span.get("type") for span in datadog_spans] - expected = [ - "http", - "sql", - "web", - "web", - "grpc", - "template", - "sql", - "sql", - "mongodb", - "sql", - "redis", - "http", - "sql", - "web", - ] - self.assertEqual(actual, expected) - - def test_errors(self): - with self.assertRaises(ValueError): - with self.tracer.start_span("foo"): - raise ValueError("bar") - - datadog_spans = get_spans(self.tracer, self.exporter) - - self.assertEqual(len(datadog_spans), 1) - - span = datadog_spans[0] - self.assertEqual(span["error"], 1) - self.assertEqual(span["meta"]["error.msg"], "bar") - self.assertEqual(span["meta"]["error.type"], "ValueError") - - def test_shutdown(self): - span_names = ["xxx", "bar", "foo"] - - for name in span_names: - with self.tracer.start_span(name): - pass - - self.span_processor.shutdown() - - # check that spans are exported without an explicitly call to - # force_flush() - datadog_spans = get_spans(self.tracer, self.exporter) - actual = [span.get("resource") for span in datadog_spans] - self.assertListEqual(span_names, actual) - - def test_flush(self): - span_names0 = ["xxx", "bar", "foo"] - span_names1 = ["yyy", "baz", "fox"] - - for name in span_names0: - with self.tracer.start_span(name): - pass - - self.assertTrue(self.span_processor.force_flush()) - datadog_spans = get_spans(self.tracer, self.exporter, shutdown=False) - actual0 = [span.get("resource") for span in datadog_spans] - self.assertListEqual(span_names0, actual0) - - # create some more spans to check that span processor still works - for name in span_names1: - with self.tracer.start_span(name): - pass - - self.assertTrue(self.span_processor.force_flush()) - datadog_spans = get_spans(self.tracer, self.exporter) - actual1 = [span.get("resource") for span in datadog_spans] - self.assertListEqual(span_names0 + span_names1, actual1) - - def test_span_processor_lossless(self): - """Test that no spans are lost when sending max_trace_size spans""" - span_processor = datadog.DatadogExportSpanProcessor( - self.exporter, max_trace_size=128 - ) - tracer_provider = trace.TracerProvider() - tracer_provider.add_span_processor(span_processor) - tracer = tracer_provider.get_tracer(__name__) - - with tracer.start_as_current_span("root"): - for _ in range(127): - with tracer.start_span("foo"): - pass - - self.assertTrue(span_processor.force_flush()) - datadog_spans = get_spans(tracer, self.exporter) - self.assertEqual(len(datadog_spans), 128) - tracer_provider.shutdown() - - def test_span_processor_dropped_spans(self): - """Test that spans are lost when exceeding max_trace_size spans""" - span_processor = datadog.DatadogExportSpanProcessor( - self.exporter, max_trace_size=128 - ) - tracer_provider = trace.TracerProvider() - tracer_provider.add_span_processor(span_processor) - tracer = tracer_provider.get_tracer(__name__) - - with tracer.start_as_current_span("root"): - for _ in range(127): - with tracer.start_span("foo"): - pass - with self.assertLogs(level=logging.WARNING): - with tracer.start_span("one-too-many"): - pass - - self.assertTrue(span_processor.force_flush()) - datadog_spans = get_spans(tracer, self.exporter) - self.assertEqual(len(datadog_spans), 128) - tracer_provider.shutdown() - - def test_span_processor_scheduled_delay(self): - """Test that spans are exported each schedule_delay_millis""" - delay = 300 - span_processor = datadog.DatadogExportSpanProcessor( - self.exporter, schedule_delay_millis=delay - ) - tracer_provider = trace.TracerProvider() - tracer_provider.add_span_processor(span_processor) - tracer = tracer_provider.get_tracer(__name__) - - with tracer.start_span("foo"): - pass - - time.sleep(delay / (1e3 * 2)) - datadog_spans = get_spans(tracer, self.exporter, shutdown=False) - self.assertEqual(len(datadog_spans), 0) - - time.sleep(delay / (1e3 * 2) + 0.01) - datadog_spans = get_spans(tracer, self.exporter, shutdown=False) - self.assertEqual(len(datadog_spans), 1) - - tracer_provider.shutdown() - - def test_batch_span_processor_reset_timeout(self): - """Test that the scheduled timeout is reset on cycles without spans""" - delay = 50 - # pylint: disable=protected-access - exporter = MockDatadogSpanExporter() - exporter._agent_writer.write.side_effect = lambda spans: time.sleep( - 0.05 - ) - span_processor = datadog.DatadogExportSpanProcessor( - exporter, schedule_delay_millis=delay - ) - tracer_provider = trace.TracerProvider() - tracer_provider.add_span_processor(span_processor) - tracer = tracer_provider.get_tracer(__name__) - with mock.patch.object(span_processor.condition, "wait") as mock_wait: - with tracer.start_span("foo"): - pass - - # give some time for exporter to loop - # since wait is mocked it should return immediately - time.sleep(0.1) - mock_wait_calls = list(mock_wait.mock_calls) - - # find the index of the call that processed the singular span - for idx, wait_call in enumerate(mock_wait_calls): - _, args, __ = wait_call - if args[0] <= 0: - after_calls = mock_wait_calls[idx + 1 :] - break - - self.assertTrue( - all(args[0] >= 0.05 for _, args, __ in after_calls) - ) - - span_processor.shutdown() - - def test_span_processor_accepts_parent_context(self): - span_processor = mock.Mock( - wraps=datadog.DatadogExportSpanProcessor(self.exporter) - ) - tracer_provider = trace.TracerProvider() - tracer_provider.add_span_processor(span_processor) - tracer = tracer_provider.get_tracer(__name__) - - context = Context() - span = tracer.start_span("foo", context=context) - - span_processor.on_start.assert_called_once_with( - span, parent_context=context - ) - - def test_origin(self): - context = trace_api.SpanContext( - trace_id=0x000000000000000000000000DEADBEEF, - span_id=trace_api.INVALID_SPAN, - is_remote=True, - trace_state=trace_api.TraceState( - {datadog.constants.DD_ORIGIN: "origin-service"} - ), - ) - - root_span = trace._Span(name="root", context=context, parent=None) - child_span = trace._Span( - name="child", context=context, parent=root_span - ) - root_span.start() - child_span.start() - child_span.end() - root_span.end() - - # pylint: disable=protected-access - exporter = datadog.DatadogSpanExporter() - datadog_spans = [ - span.to_dict() - for span in exporter._translate_to_datadog([root_span, child_span]) - ] - - self.assertEqual(len(datadog_spans), 2) - - actual = [ - span["meta"].get(datadog.constants.DD_ORIGIN) - if "meta" in span - else None - for span in datadog_spans - ] - expected = ["origin-service", None] - self.assertListEqual(actual, expected) - - def test_sampling_rate(self): - context = trace_api.SpanContext( - trace_id=0x000000000000000000000000DEADBEEF, - span_id=0x34BF92DEEFC58C92, - is_remote=False, - trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), - ) - sampler = sampling.TraceIdRatioBased(0.5) - - span = trace._Span( - name="sampled", context=context, parent=None, sampler=sampler - ) - span.start() - span.end() - - # pylint: disable=protected-access - exporter = datadog.DatadogSpanExporter() - datadog_spans = [ - span.to_dict() for span in exporter._translate_to_datadog([span]) - ] - - self.assertEqual(len(datadog_spans), 1) - - actual = [ - span["metrics"].get(datadog.constants.SAMPLE_RATE_METRIC_KEY) - if "metrics" in span - else None - for span in datadog_spans - ] - expected = [0.5] - self.assertListEqual(actual, expected) diff --git a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_format.py b/exporter/opentelemetry-exporter-datadog/tests/test_datadog_format.py deleted file mode 100644 index bb41fef49b1..00000000000 --- a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_format.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest - -from opentelemetry import trace as trace_api -from opentelemetry.exporter.datadog import constants, propagator -from opentelemetry.sdk import trace -from opentelemetry.trace import get_current_span, set_span_in_context -from opentelemetry.trace.propagation.textmap import DictGetter - -FORMAT = propagator.DatadogFormat() - -carrier_getter = DictGetter() - - -class TestDatadogFormat(unittest.TestCase): - @classmethod - def setUpClass(cls): - ids_generator = trace_api.RandomIdsGenerator() - cls.serialized_trace_id = propagator.format_trace_id( - ids_generator.generate_trace_id() - ) - cls.serialized_parent_id = propagator.format_span_id( - ids_generator.generate_span_id() - ) - cls.serialized_origin = "origin-service" - - def test_malformed_headers(self): - """Test with no Datadog headers""" - malformed_trace_id_key = FORMAT.TRACE_ID_KEY + "-x" - malformed_parent_id_key = FORMAT.PARENT_ID_KEY + "-x" - context = get_current_span( - FORMAT.extract( - carrier_getter, - { - malformed_trace_id_key: self.serialized_trace_id, - malformed_parent_id_key: self.serialized_parent_id, - }, - ) - ).get_span_context() - - self.assertNotEqual(context.trace_id, int(self.serialized_trace_id)) - self.assertNotEqual(context.span_id, int(self.serialized_parent_id)) - self.assertFalse(context.is_remote) - - def test_missing_trace_id(self): - """If a trace id is missing, populate an invalid trace id.""" - carrier = { - FORMAT.PARENT_ID_KEY: self.serialized_parent_id, - } - - ctx = FORMAT.extract(carrier_getter, carrier) - span_context = get_current_span(ctx).get_span_context() - self.assertEqual(span_context.trace_id, trace_api.INVALID_TRACE_ID) - - def test_missing_parent_id(self): - """If a parent id is missing, populate an invalid trace id.""" - carrier = { - FORMAT.TRACE_ID_KEY: self.serialized_trace_id, - } - - ctx = FORMAT.extract(carrier_getter, carrier) - span_context = get_current_span(ctx).get_span_context() - self.assertEqual(span_context.span_id, trace_api.INVALID_SPAN_ID) - - def test_context_propagation(self): - """Test the propagation of Datadog headers.""" - parent_span_context = get_current_span( - FORMAT.extract( - carrier_getter, - { - FORMAT.TRACE_ID_KEY: self.serialized_trace_id, - FORMAT.PARENT_ID_KEY: self.serialized_parent_id, - FORMAT.SAMPLING_PRIORITY_KEY: str(constants.AUTO_KEEP), - FORMAT.ORIGIN_KEY: self.serialized_origin, - }, - ) - ).get_span_context() - - self.assertEqual( - parent_span_context.trace_id, int(self.serialized_trace_id) - ) - self.assertEqual( - parent_span_context.span_id, int(self.serialized_parent_id) - ) - self.assertEqual(parent_span_context.trace_flags, constants.AUTO_KEEP) - self.assertEqual( - parent_span_context.trace_state.get(constants.DD_ORIGIN), - self.serialized_origin, - ) - self.assertTrue(parent_span_context.is_remote) - - child = trace._Span( - "child", - trace_api.SpanContext( - parent_span_context.trace_id, - trace_api.RandomIdsGenerator().generate_span_id(), - is_remote=False, - trace_flags=parent_span_context.trace_flags, - trace_state=parent_span_context.trace_state, - ), - parent=parent_span_context, - ) - - child_carrier = {} - child_context = set_span_in_context(child) - FORMAT.inject(dict.__setitem__, child_carrier, context=child_context) - - self.assertEqual( - child_carrier[FORMAT.TRACE_ID_KEY], self.serialized_trace_id - ) - self.assertEqual( - child_carrier[FORMAT.PARENT_ID_KEY], str(child.context.span_id) - ) - self.assertEqual( - child_carrier[FORMAT.SAMPLING_PRIORITY_KEY], - str(constants.AUTO_KEEP), - ) - self.assertEqual( - child_carrier.get(FORMAT.ORIGIN_KEY), self.serialized_origin - ) - - def test_sampling_priority_auto_reject(self): - """Test sampling priority rejected.""" - parent_span_context = get_current_span( - FORMAT.extract( - carrier_getter, - { - FORMAT.TRACE_ID_KEY: self.serialized_trace_id, - FORMAT.PARENT_ID_KEY: self.serialized_parent_id, - FORMAT.SAMPLING_PRIORITY_KEY: str(constants.AUTO_REJECT), - }, - ) - ).get_span_context() - - self.assertEqual( - parent_span_context.trace_flags, constants.AUTO_REJECT - ) - - child = trace._Span( - "child", - trace_api.SpanContext( - parent_span_context.trace_id, - trace_api.RandomIdsGenerator().generate_span_id(), - is_remote=False, - trace_flags=parent_span_context.trace_flags, - trace_state=parent_span_context.trace_state, - ), - parent=parent_span_context, - ) - - child_carrier = {} - child_context = set_span_in_context(child) - FORMAT.inject(dict.__setitem__, child_carrier, context=child_context) - - self.assertEqual( - child_carrier[FORMAT.SAMPLING_PRIORITY_KEY], - str(constants.AUTO_REJECT), - ) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-aiohttp-client/CHANGELOG.md deleted file mode 100644 index 8b1d3ee2c1b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/CHANGELOG.md +++ /dev/null @@ -1,25 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Updating span name to match semantic conventions - ([#972](https://github.com/open-telemetry/opentelemetry-python/pull/972)) -- Add instrumentor and auto instrumentation support for aiohttp - ([#1075](https://github.com/open-telemetry/opentelemetry-python/pull/1075)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-aiohttp-client - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) - -## 0.7b1 - -Released 2020-05-12 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/LICENSE b/instrumentation/opentelemetry-instrumentation-aiohttp-client/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-aiohttp-client/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-aiohttp-client/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-aiohttp-client/README.rst b/instrumentation/opentelemetry-instrumentation-aiohttp-client/README.rst deleted file mode 100644 index bc44e0e2627..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/README.rst +++ /dev/null @@ -1,24 +0,0 @@ -OpenTelemetry aiohttp client Integration -======================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-aiohttp-client.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-aiohttp-client/ - -This library allows tracing HTTP requests made by the -`aiohttp client `_ library. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-aiohttp-client - - -References ----------- - -* `OpenTelemetry Project `_ -* `aiohttp client Tracing `_ diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/setup.cfg b/instrumentation/opentelemetry-instrumentation-aiohttp-client/setup.cfg deleted file mode 100644 index e0fcfbf4b0c..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/setup.cfg +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2020, 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-instrumentation-aiohttp-client -description = OpenTelemetry aiohttp client instrumentation -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/instrumentation/opentelemetry-instrumentation-aiohttp-client -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5.3 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - aiohttp ~= 3.0 - wrapt >= 1.0.0, < 2.0.0 - -[options.packages.find] -where = src - -[options.extras_require] -test = - -[options.entry_points] -opentelemetry_instrumentor = - aiohttp-client = opentelemetry.instrumentation.aiohttp_client:AioHttpClientInstrumentor \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/setup.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/setup.py deleted file mode 100644 index fe74e232353..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2020, 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", - "instrumentation", - "aiohttp_client", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py deleted file mode 100644 index c708802a926..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py +++ /dev/null @@ -1,315 +0,0 @@ -# Copyright 2020, 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. - -""" -The opentelemetry-instrumentation-aiohttp-client package allows tracing HTTP -requests made by the aiohttp client library. - -Usage ------ -Explicitly instrumenting a single client session: - -.. code:: python - - import aiohttp - from opentelemetry.instrumentation.aiohttp_client import ( - create_trace_config, - url_path_span_name - ) - import yarl - - def strip_query_params(url: yarl.URL) -> str: - return str(url.with_query(None)) - - async with aiohttp.ClientSession(trace_configs=[create_trace_config( - # Remove all query params from the URL attribute on the span. - url_filter=strip_query_params, - # Use the URL's path as the span name. - span_name=url_path_span_name - )]) as session: - async with session.get(url) as response: - await response.text() - -Instrumenting all client sessions: - -.. code:: python - - import aiohttp - from opentelemetry.instrumentation.aiohttp_client import ( - AioHttpClientInstrumentor - ) - - # Enable instrumentation - AioHttpClientInstrumentor().instrument() - - # Create a session and make an HTTP get request - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - await response.text() - -API ---- -""" - -import socket -import types -import typing - -import aiohttp -import wrapt - -from opentelemetry import context as context_api -from opentelemetry import propagators, trace -from opentelemetry.instrumentation.aiohttp_client.version import __version__ -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.utils import ( - http_status_to_status_code, - unwrap, -) -from opentelemetry.trace import SpanKind, TracerProvider, get_tracer -from opentelemetry.trace.status import Status, StatusCode - -_UrlFilterT = typing.Optional[typing.Callable[[str], str]] -_SpanNameT = typing.Optional[ - typing.Union[typing.Callable[[aiohttp.TraceRequestStartParams], str], str] -] - - -def url_path_span_name(params: aiohttp.TraceRequestStartParams) -> str: - """Extract a span name from the request URL path. - - A simple callable to extract the path portion of the requested URL - for use as the span name. - - :param aiohttp.TraceRequestStartParams params: Parameters describing - the traced request. - - :return: The URL path. - :rtype: str - """ - return params.url.path - - -def create_trace_config( - url_filter: _UrlFilterT = None, - span_name: _SpanNameT = None, - tracer_provider: TracerProvider = None, -) -> aiohttp.TraceConfig: - """Create an aiohttp-compatible trace configuration. - - One span is created for the entire HTTP request, including initial - TCP/TLS setup if the connection doesn't exist. - - By default the span name is set to the HTTP request method. - - Example usage: - - .. code:: python - - import aiohttp - from opentelemetry.instrumentation.aiohttp_client import create_trace_config - - async with aiohttp.ClientSession(trace_configs=[create_trace_config()]) as session: - async with session.get(url) as response: - await response.text() - - - :param url_filter: A callback to process the requested URL prior to adding - it as a span attribute. This can be useful to remove sensitive data - such as API keys or user personal information. - - :param str span_name: Override the default span name. - :param tracer_provider: optional TracerProvider from which to get a Tracer - - :return: An object suitable for use with :py:class:`aiohttp.ClientSession`. - :rtype: :py:class:`aiohttp.TraceConfig` - """ - # `aiohttp.TraceRequestStartParams` resolves to `aiohttp.tracing.TraceRequestStartParams` - # which doesn't exist in the aiottp intersphinx inventory. - # Explicitly specify the type for the `span_name` param and rtype to work - # around this issue. - - tracer = get_tracer(__name__, __version__, tracer_provider) - - def _end_trace(trace_config_ctx: types.SimpleNamespace): - context_api.detach(trace_config_ctx.token) - trace_config_ctx.span.end() - - async def on_request_start( - unused_session: aiohttp.ClientSession, - trace_config_ctx: types.SimpleNamespace, - params: aiohttp.TraceRequestStartParams, - ): - if context_api.get_value("suppress_instrumentation"): - trace_config_ctx.span = None - return - - http_method = params.method.upper() - if trace_config_ctx.span_name is None: - request_span_name = "HTTP {}".format(http_method) - elif callable(trace_config_ctx.span_name): - request_span_name = str(trace_config_ctx.span_name(params)) - else: - request_span_name = str(trace_config_ctx.span_name) - - trace_config_ctx.span = trace_config_ctx.tracer.start_span( - request_span_name, kind=SpanKind.CLIENT, - ) - - if trace_config_ctx.span.is_recording(): - attributes = { - "component": "http", - "http.method": http_method, - "http.url": trace_config_ctx.url_filter(params.url) - if callable(trace_config_ctx.url_filter) - else str(params.url), - } - for key, value in attributes.items(): - trace_config_ctx.span.set_attribute(key, value) - - trace_config_ctx.token = context_api.attach( - trace.set_span_in_context(trace_config_ctx.span) - ) - - propagators.inject(type(params.headers).__setitem__, params.headers) - - async def on_request_end( - unused_session: aiohttp.ClientSession, - trace_config_ctx: types.SimpleNamespace, - params: aiohttp.TraceRequestEndParams, - ): - if trace_config_ctx.span is None: - return - - if trace_config_ctx.span.is_recording(): - trace_config_ctx.span.set_status( - Status(http_status_to_status_code(int(params.response.status))) - ) - trace_config_ctx.span.set_attribute( - "http.status_code", params.response.status - ) - trace_config_ctx.span.set_attribute( - "http.status_text", params.response.reason - ) - _end_trace(trace_config_ctx) - - async def on_request_exception( - unused_session: aiohttp.ClientSession, - trace_config_ctx: types.SimpleNamespace, - params: aiohttp.TraceRequestExceptionParams, - ): - if trace_config_ctx.span is None: - return - - if trace_config_ctx.span.is_recording() and params.exception: - trace_config_ctx.span.set_status(Status(StatusCode.ERROR)) - trace_config_ctx.span.record_exception(params.exception) - _end_trace(trace_config_ctx) - - def _trace_config_ctx_factory(**kwargs): - kwargs.setdefault("trace_request_ctx", {}) - return types.SimpleNamespace( - span_name=span_name, tracer=tracer, url_filter=url_filter, **kwargs - ) - - trace_config = aiohttp.TraceConfig( - trace_config_ctx_factory=_trace_config_ctx_factory - ) - - trace_config.on_request_start.append(on_request_start) - trace_config.on_request_end.append(on_request_end) - trace_config.on_request_exception.append(on_request_exception) - - return trace_config - - -def _instrument( - tracer_provider: TracerProvider = None, - url_filter: _UrlFilterT = None, - span_name: _SpanNameT = None, -): - """Enables tracing of all ClientSessions - - When a ClientSession gets created a TraceConfig is automatically added to - the session's trace_configs. - """ - # pylint:disable=unused-argument - def instrumented_init(wrapped, instance, args, kwargs): - if context_api.get_value("suppress_instrumentation"): - return wrapped(*args, **kwargs) - - trace_configs = list(kwargs.get("trace_configs") or ()) - - trace_config = create_trace_config( - url_filter=url_filter, - span_name=span_name, - tracer_provider=tracer_provider, - ) - trace_config.opentelemetry_aiohttp_instrumented = True - trace_configs.append(trace_config) - - kwargs["trace_configs"] = trace_configs - return wrapped(*args, **kwargs) - - wrapt.wrap_function_wrapper( - aiohttp.ClientSession, "__init__", instrumented_init - ) - - -def _uninstrument(): - """Disables instrumenting for all newly created ClientSessions""" - unwrap(aiohttp.ClientSession, "__init__") - - -def _uninstrument_session(client_session: aiohttp.ClientSession): - """Disables instrumentation for the given ClientSession""" - # pylint: disable=protected-access - trace_configs = client_session._trace_configs - client_session._trace_configs = [ - trace_config - for trace_config in trace_configs - if not hasattr(trace_config, "opentelemetry_aiohttp_instrumented") - ] - - -class AioHttpClientInstrumentor(BaseInstrumentor): - """An instrumentor for aiohttp client sessions - - See `BaseInstrumentor` - """ - - def _instrument(self, **kwargs): - """Instruments aiohttp ClientSession - - Args: - **kwargs: Optional arguments - ``tracer_provider``: a TracerProvider, defaults to global - ``url_filter``: A callback to process the requested URL prior to adding - it as a span attribute. This can be useful to remove sensitive data - such as API keys or user personal information. - ``span_name``: Override the default span name. - """ - _instrument( - tracer_provider=kwargs.get("tracer_provider"), - url_filter=kwargs.get("url_filter"), - span_name=kwargs.get("span_name"), - ) - - def _uninstrument(self, **kwargs): - _uninstrument() - - @staticmethod - def uninstrument_session(client_session: aiohttp.ClientSession): - """Disables instrumentation for the given session""" - _uninstrument_session(client_session) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/version.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/version.py deleted file mode 100644 index bb32120c79b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2020, 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__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py deleted file mode 100644 index f0734653488..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py +++ /dev/null @@ -1,500 +0,0 @@ -# Copyright 2020, 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 asyncio -import contextlib -import typing -import unittest -import urllib.parse -from http import HTTPStatus -from unittest import mock - -import aiohttp -import aiohttp.test_utils -import yarl -from pkg_resources import iter_entry_points - -from opentelemetry import context -from opentelemetry.instrumentation import aiohttp_client -from opentelemetry.instrumentation.aiohttp_client import ( - AioHttpClientInstrumentor, -) -from opentelemetry.test.test_base import TestBase -from opentelemetry.trace.status import StatusCode - - -def run_with_test_server( - runnable: typing.Callable, url: str, handler: typing.Callable -) -> typing.Tuple[str, int]: - async def do_request(): - app = aiohttp.web.Application() - parsed_url = urllib.parse.urlparse(url) - app.add_routes([aiohttp.web.get(parsed_url.path, handler)]) - app.add_routes([aiohttp.web.post(parsed_url.path, handler)]) - app.add_routes([aiohttp.web.patch(parsed_url.path, handler)]) - - with contextlib.suppress(aiohttp.ClientError): - async with aiohttp.test_utils.TestServer(app) as server: - netloc = (server.host, server.port) - await server.start_server() - await runnable(server) - return netloc - - loop = asyncio.get_event_loop() - return loop.run_until_complete(do_request()) - - -class TestAioHttpIntegration(TestBase): - def assert_spans(self, spans): - self.assertEqual( - [ - ( - span.name, - (span.status.status_code, span.status.description), - dict(span.attributes), - ) - for span in self.memory_exporter.get_finished_spans() - ], - spans, - ) - - def test_url_path_span_name(self): - for url, expected in ( - ( - yarl.URL("http://hostname.local:1234/some/path?query=params"), - "/some/path", - ), - (yarl.URL("http://hostname.local:1234"), "/"), - ): - with self.subTest(url=url): - params = aiohttp.TraceRequestStartParams("METHOD", url, {}) - actual = aiohttp_client.url_path_span_name(params) - self.assertEqual(actual, expected) - self.assertIsInstance(actual, str) - - @staticmethod - def _http_request( - trace_config, - url: str, - method: str = "GET", - status_code: int = HTTPStatus.OK, - request_handler: typing.Callable = None, - **kwargs - ) -> typing.Tuple[str, int]: - """Helper to start an aiohttp test server and send an actual HTTP request to it.""" - - async def default_handler(request): - assert "traceparent" in request.headers - return aiohttp.web.Response(status=int(status_code)) - - async def client_request(server: aiohttp.test_utils.TestServer): - async with aiohttp.test_utils.TestClient( - server, trace_configs=[trace_config] - ) as client: - await client.request( - method, url, trace_request_ctx={}, **kwargs - ) - - handler = request_handler or default_handler - return run_with_test_server(client_request, url, handler) - - def test_status_codes(self): - for status_code, span_status in ( - (HTTPStatus.OK, StatusCode.UNSET), - (HTTPStatus.TEMPORARY_REDIRECT, StatusCode.UNSET), - (HTTPStatus.SERVICE_UNAVAILABLE, StatusCode.ERROR), - (HTTPStatus.GATEWAY_TIMEOUT, StatusCode.ERROR,), - ): - with self.subTest(status_code=status_code): - host, port = self._http_request( - trace_config=aiohttp_client.create_trace_config(), - url="/test-path?query=param#foobar", - status_code=status_code, - ) - - self.assert_spans( - [ - ( - "HTTP GET", - (span_status, None), - { - "component": "http", - "http.method": "GET", - "http.url": "http://{}:{}/test-path?query=param#foobar".format( - host, port - ), - "http.status_code": int(status_code), - "http.status_text": status_code.phrase, - }, - ) - ] - ) - - self.memory_exporter.clear() - - def test_not_recording(self): - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - with mock.patch("opentelemetry.trace.get_tracer"): - # pylint: disable=W0612 - host, port = self._http_request( - trace_config=aiohttp_client.create_trace_config(), - url="/test-path?query=param#foobar", - ) - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_span_name_option(self): - for span_name, method, path, expected in ( - ("static", "POST", "/static-span-name", "static"), - ( - lambda params: "{} - {}".format( - params.method, params.url.path - ), - "PATCH", - "/some/path", - "PATCH - /some/path", - ), - ): - with self.subTest(span_name=span_name, method=method, path=path): - host, port = self._http_request( - trace_config=aiohttp_client.create_trace_config( - span_name=span_name - ), - method=method, - url=path, - status_code=HTTPStatus.OK, - ) - - self.assert_spans( - [ - ( - expected, - (StatusCode.UNSET, None), - { - "component": "http", - "http.method": method, - "http.url": "http://{}:{}{}".format( - host, port, path - ), - "http.status_code": int(HTTPStatus.OK), - "http.status_text": HTTPStatus.OK.phrase, - }, - ) - ] - ) - self.memory_exporter.clear() - - def test_url_filter_option(self): - # Strips all query params from URL before adding as a span attribute. - def strip_query_params(url: yarl.URL) -> str: - return str(url.with_query(None)) - - host, port = self._http_request( - trace_config=aiohttp_client.create_trace_config( - url_filter=strip_query_params - ), - url="/some/path?query=param&other=param2", - status_code=HTTPStatus.OK, - ) - - self.assert_spans( - [ - ( - "HTTP GET", - (StatusCode.UNSET, None), - { - "component": "http", - "http.method": "GET", - "http.url": "http://{}:{}/some/path".format( - host, port - ), - "http.status_code": int(HTTPStatus.OK), - "http.status_text": HTTPStatus.OK.phrase, - }, - ) - ] - ) - - def test_connection_errors(self): - trace_configs = [aiohttp_client.create_trace_config()] - - for url, expected_status in ( - ("http://this-is-unknown.local/", StatusCode.ERROR), - ("http://127.0.0.1:1/", StatusCode.ERROR), - ): - with self.subTest(expected_status=expected_status): - - async def do_request(url): - async with aiohttp.ClientSession( - trace_configs=trace_configs, - ) as session: - async with session.get(url): - pass - - loop = asyncio.get_event_loop() - with self.assertRaises(aiohttp.ClientConnectorError): - loop.run_until_complete(do_request(url)) - - self.assert_spans( - [ - ( - "HTTP GET", - (expected_status, None), - { - "component": "http", - "http.method": "GET", - "http.url": url, - }, - ) - ] - ) - self.memory_exporter.clear() - - def test_timeout(self): - async def request_handler(request): - await asyncio.sleep(1) - assert "traceparent" in request.headers - return aiohttp.web.Response() - - host, port = self._http_request( - trace_config=aiohttp_client.create_trace_config(), - url="/test_timeout", - request_handler=request_handler, - timeout=aiohttp.ClientTimeout(sock_read=0.01), - ) - - self.assert_spans( - [ - ( - "HTTP GET", - (StatusCode.ERROR, None), - { - "component": "http", - "http.method": "GET", - "http.url": "http://{}:{}/test_timeout".format( - host, port - ), - }, - ) - ] - ) - - def test_too_many_redirects(self): - async def request_handler(request): - # Create a redirect loop. - location = request.url - assert "traceparent" in request.headers - raise aiohttp.web.HTTPFound(location=location) - - host, port = self._http_request( - trace_config=aiohttp_client.create_trace_config(), - url="/test_too_many_redirects", - request_handler=request_handler, - max_redirects=2, - ) - - self.assert_spans( - [ - ( - "HTTP GET", - (StatusCode.ERROR, None), - { - "component": "http", - "http.method": "GET", - "http.url": "http://{}:{}/test_too_many_redirects".format( - host, port - ), - }, - ) - ] - ) - - -class TestAioHttpClientInstrumentor(TestBase): - URL = "/test-path" - - def setUp(self): - super().setUp() - AioHttpClientInstrumentor().instrument() - - def tearDown(self): - super().tearDown() - AioHttpClientInstrumentor().uninstrument() - - @staticmethod - # pylint:disable=unused-argument - async def default_handler(request): - return aiohttp.web.Response(status=int(200)) - - @staticmethod - def get_default_request(url: str = URL): - async def default_request(server: aiohttp.test_utils.TestServer): - async with aiohttp.test_utils.TestClient(server) as session: - await session.get(url) - - return default_request - - def assert_spans(self, num_spans: int): - finished_spans = self.memory_exporter.get_finished_spans() - self.assertEqual(num_spans, len(finished_spans)) - if num_spans == 0: - return None - if num_spans == 1: - return finished_spans[0] - return finished_spans - - def test_instrument(self): - host, port = run_with_test_server( - self.get_default_request(), self.URL, self.default_handler - ) - span = self.assert_spans(1) - self.assertEqual("http", span.attributes["component"]) - self.assertEqual("GET", span.attributes["http.method"]) - self.assertEqual( - "http://{}:{}/test-path".format(host, port), - span.attributes["http.url"], - ) - self.assertEqual(200, span.attributes["http.status_code"]) - self.assertEqual("OK", span.attributes["http.status_text"]) - - def test_instrument_with_existing_trace_config(self): - trace_config = aiohttp.TraceConfig() - - async def create_session(server: aiohttp.test_utils.TestServer): - async with aiohttp.test_utils.TestClient( - server, trace_configs=[trace_config] - ) as client: - # pylint:disable=protected-access - trace_configs = client.session._trace_configs - self.assertEqual(2, len(trace_configs)) - self.assertTrue(trace_config in trace_configs) - async with client as session: - await session.get(TestAioHttpClientInstrumentor.URL) - - run_with_test_server(create_session, self.URL, self.default_handler) - self.assert_spans(1) - - def test_uninstrument(self): - AioHttpClientInstrumentor().uninstrument() - run_with_test_server( - self.get_default_request(), self.URL, self.default_handler - ) - - self.assert_spans(0) - - AioHttpClientInstrumentor().instrument() - run_with_test_server( - self.get_default_request(), self.URL, self.default_handler - ) - self.assert_spans(1) - - def test_uninstrument_session(self): - async def uninstrument_request(server: aiohttp.test_utils.TestServer): - client = aiohttp.test_utils.TestClient(server) - AioHttpClientInstrumentor().uninstrument_session(client.session) - async with client as session: - await session.get(self.URL) - - run_with_test_server( - uninstrument_request, self.URL, self.default_handler - ) - self.assert_spans(0) - - run_with_test_server( - self.get_default_request(), self.URL, self.default_handler - ) - self.assert_spans(1) - - def test_suppress_instrumentation(self): - token = context.attach( - context.set_value("suppress_instrumentation", True) - ) - try: - run_with_test_server( - self.get_default_request(), self.URL, self.default_handler - ) - finally: - context.detach(token) - self.assert_spans(0) - - @staticmethod - async def suppressed_request(server: aiohttp.test_utils.TestServer): - async with aiohttp.test_utils.TestClient(server) as client: - token = context.attach( - context.set_value("suppress_instrumentation", True) - ) - await client.get(TestAioHttpClientInstrumentor.URL) - context.detach(token) - - def test_suppress_instrumentation_after_creation(self): - run_with_test_server( - self.suppressed_request, self.URL, self.default_handler - ) - self.assert_spans(0) - - def test_suppress_instrumentation_with_server_exception(self): - # pylint:disable=unused-argument - async def raising_handler(request): - raise aiohttp.web.HTTPFound(location=self.URL) - - run_with_test_server( - self.suppressed_request, self.URL, raising_handler - ) - self.assert_spans(0) - - def test_url_filter(self): - def strip_query_params(url: yarl.URL) -> str: - return str(url.with_query(None)) - - AioHttpClientInstrumentor().uninstrument() - AioHttpClientInstrumentor().instrument(url_filter=strip_query_params) - - url = "/test-path?query=params" - host, port = run_with_test_server( - self.get_default_request(url), url, self.default_handler - ) - span = self.assert_spans(1) - self.assertEqual( - "http://{}:{}/test-path".format(host, port), - span.attributes["http.url"], - ) - - def test_span_name(self): - def span_name_callback(params: aiohttp.TraceRequestStartParams) -> str: - return "{} - {}".format(params.method, params.url.path) - - AioHttpClientInstrumentor().uninstrument() - AioHttpClientInstrumentor().instrument(span_name=span_name_callback) - - url = "/test-path" - run_with_test_server( - self.get_default_request(url), url, self.default_handler - ) - span = self.assert_spans(1) - self.assertEqual("GET - /test-path", span.name) - - -class TestLoadingAioHttpInstrumentor(unittest.TestCase): - def test_loading_instrumentor(self): - entry_points = iter_entry_points( - "opentelemetry_instrumentor", "aiohttp-client" - ) - - instrumentor = next(entry_points).load()() - self.assertIsInstance(instrumentor, AioHttpClientInstrumentor) diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-aiopg/CHANGELOG.md deleted file mode 100644 index c62fac06178..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiopg/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.11b0 - -Released 2020-07-28 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/LICENSE b/instrumentation/opentelemetry-instrumentation-aiopg/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiopg/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-aiopg/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-aiopg/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiopg/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-aiopg/README.rst b/instrumentation/opentelemetry-instrumentation-aiopg/README.rst deleted file mode 100644 index f7a66579dfc..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiopg/README.rst +++ /dev/null @@ -1,21 +0,0 @@ -OpenTelemetry aiopg instrumentation -=================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-aiopg.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-aiopg/ - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-aiopg - - -References ----------- - -* `OpenTelemetry aiopg Instrumentation `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/setup.cfg b/instrumentation/opentelemetry-instrumentation-aiopg/setup.cfg deleted file mode 100644 index c903180e98f..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiopg/setup.cfg +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-aiopg -description = OpenTelemetry aiopg instrumentation -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/master/instrumentation/opentelemetry-instrumentation-aiopg -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation-dbapi == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - aiopg >= 0.13.0 - wrapt >= 1.0.0, < 2.0.0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - - -[options.entry_points] -opentelemetry_instrumentor = - aiopg = opentelemetry.instrumentation.aiopg:AiopgInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/setup.py b/instrumentation/opentelemetry-instrumentation-aiopg/setup.py deleted file mode 100644 index dfd463e5abb..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiopg/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "aiopg", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/__init__.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/__init__.py deleted file mode 100644 index 176fc82b40c..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/__init__.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -The integration with PostgreSQL supports the aiopg library, -it can be enabled by using ``AiopgInstrumentor``. - -.. aiopg: https://github.com/aio-libs/aiopg - -Usage ------ - -.. code-block:: python - - import aiopg - from opentelemetry.instrumentation.aiopg import AiopgInstrumentor - - AiopgInstrumentor().instrument() - - cnx = await aiopg.connect(database='Database') - cursor = await cnx.cursor() - await cursor.execute("INSERT INTO test (testField) VALUES (123)") - cursor.close() - cnx.close() - - pool = await aiopg.create_pool(database='Database') - cnx = await pool.acquire() - cursor = await cnx.cursor() - await cursor.execute("INSERT INTO test (testField) VALUES (123)") - cursor.close() - cnx.close() - -API ---- -""" - -from opentelemetry.instrumentation.aiopg import wrappers -from opentelemetry.instrumentation.aiopg.version import __version__ -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor - - -class AiopgInstrumentor(BaseInstrumentor): - _CONNECTION_ATTRIBUTES = { - "database": "info.dbname", - "port": "info.port", - "host": "info.host", - "user": "info.user", - } - - _DATABASE_COMPONENT = "postgresql" - _DATABASE_TYPE = "sql" - - def _instrument(self, **kwargs): - """Integrate with PostgreSQL aiopg library. - aiopg: https://github.com/aio-libs/aiopg - """ - - tracer_provider = kwargs.get("tracer_provider") - - wrappers.wrap_connect( - __name__, - self._DATABASE_COMPONENT, - self._DATABASE_TYPE, - self._CONNECTION_ATTRIBUTES, - version=__version__, - tracer_provider=tracer_provider, - ) - - wrappers.wrap_create_pool( - __name__, - self._DATABASE_COMPONENT, - self._DATABASE_TYPE, - self._CONNECTION_ATTRIBUTES, - version=__version__, - tracer_provider=tracer_provider, - ) - - def _uninstrument(self, **kwargs): - """"Disable aiopg instrumentation""" - wrappers.unwrap_connect() - wrappers.unwrap_create_pool() - - # pylint:disable=no-self-use - def instrument_connection(self, connection): - """Enable instrumentation in a aiopg connection. - - Args: - connection: The connection to instrument. - - Returns: - An instrumented connection. - """ - return wrappers.instrument_connection( - __name__, - connection, - self._DATABASE_COMPONENT, - self._DATABASE_TYPE, - self._CONNECTION_ATTRIBUTES, - ) - - def uninstrument_connection(self, connection): - """Disable instrumentation in a aiopg connection. - - Args: - connection: The connection to uninstrument. - - Returns: - An uninstrumented connection. - """ - return wrappers.uninstrument_connection(connection) diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py deleted file mode 100644 index 14f986da065..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py +++ /dev/null @@ -1,146 +0,0 @@ -import typing - -import wrapt -from aiopg.utils import _ContextManager, _PoolAcquireContextManager - -from opentelemetry.instrumentation.dbapi import ( - DatabaseApiIntegration, - TracedCursor, -) -from opentelemetry.trace import SpanKind -from opentelemetry.trace.status import Status, StatusCode - - -# pylint: disable=abstract-method -class AsyncProxyObject(wrapt.ObjectProxy): - def __aiter__(self): - return self.__wrapped__.__aiter__() - - async def __anext__(self): - result = await self.__wrapped__.__anext__() - return result - - async def __aenter__(self): - return await self.__wrapped__.__aenter__() - - async def __aexit__(self, exc_type, exc_val, exc_tb): - return await self.__wrapped__.__aexit__(exc_type, exc_val, exc_tb) - - def __await__(self): - return self.__wrapped__.__await__() - - -class AiopgIntegration(DatabaseApiIntegration): - async def wrapped_connection( - self, - connect_method: typing.Callable[..., typing.Any], - args: typing.Tuple[typing.Any, typing.Any], - kwargs: typing.Dict[typing.Any, typing.Any], - ): - """Add object proxy to connection object.""" - connection = await connect_method(*args, **kwargs) - # pylint: disable=protected-access - self.get_connection_attributes(connection._conn) - return get_traced_connection_proxy(connection, self) - - async def wrapped_pool(self, create_pool_method, args, kwargs): - pool = await create_pool_method(*args, **kwargs) - async with pool.acquire() as connection: - # pylint: disable=protected-access - self.get_connection_attributes(connection._conn) - return get_traced_pool_proxy(pool, self) - - -def get_traced_connection_proxy( - connection, db_api_integration, *args, **kwargs -): - # pylint: disable=abstract-method - class TracedConnectionProxy(AsyncProxyObject): - # pylint: disable=unused-argument - def __init__(self, connection, *args, **kwargs): - super().__init__(connection) - - def cursor(self, *args, **kwargs): - coro = self._cursor(*args, **kwargs) - return _ContextManager(coro) - - async def _cursor(self, *args, **kwargs): - # pylint: disable=protected-access - cursor = await self.__wrapped__._cursor(*args, **kwargs) - return get_traced_cursor_proxy(cursor, db_api_integration) - - return TracedConnectionProxy(connection, *args, **kwargs) - - -def get_traced_pool_proxy(pool, db_api_integration, *args, **kwargs): - # pylint: disable=abstract-method - class TracedPoolProxy(AsyncProxyObject): - # pylint: disable=unused-argument - def __init__(self, pool, *args, **kwargs): - super().__init__(pool) - - def acquire(self): - """Acquire free connection from the pool.""" - coro = self._acquire() - return _PoolAcquireContextManager(coro, self) - - async def _acquire(self): - # pylint: disable=protected-access - connection = await self.__wrapped__._acquire() - return get_traced_connection_proxy( - connection, db_api_integration, *args, **kwargs - ) - - return TracedPoolProxy(pool, *args, **kwargs) - - -class AsyncTracedCursor(TracedCursor): - async def traced_execution( - self, - query_method: typing.Callable[..., typing.Any], - *args: typing.Tuple[typing.Any, typing.Any], - **kwargs: typing.Dict[typing.Any, typing.Any] - ): - - with self._db_api_integration.get_tracer().start_as_current_span( - self._db_api_integration.name, kind=SpanKind.CLIENT - ) as span: - self._populate_span(span, *args) - try: - result = await query_method(*args, **kwargs) - return result - except Exception as ex: # pylint: disable=broad-except - if span.is_recording(): - span.set_status(Status(StatusCode.ERROR, str(ex))) - raise ex - - -def get_traced_cursor_proxy(cursor, db_api_integration, *args, **kwargs): - _traced_cursor = AsyncTracedCursor(db_api_integration) - - # pylint: disable=abstract-method - class AsyncTracedCursorProxy(AsyncProxyObject): - - # pylint: disable=unused-argument - def __init__(self, cursor, *args, **kwargs): - super().__init__(cursor) - - async def execute(self, *args, **kwargs): - result = await _traced_cursor.traced_execution( - self.__wrapped__.execute, *args, **kwargs - ) - return result - - async def executemany(self, *args, **kwargs): - result = await _traced_cursor.traced_execution( - self.__wrapped__.executemany, *args, **kwargs - ) - return result - - async def callproc(self, *args, **kwargs): - result = await _traced_cursor.traced_execution( - self.__wrapped__.callproc, *args, **kwargs - ) - return result - - return AsyncTracedCursorProxy(cursor, *args, **kwargs) diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/version.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/wrappers.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/wrappers.py deleted file mode 100644 index 8a3b6023bd5..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/wrappers.py +++ /dev/null @@ -1,223 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -The trace integration with aiopg based on dbapi integration, -where replaced sync wrap methods to async - -Usage ------ - -.. code-block:: python - - from opentelemetry import trace - from opentelemetry.instrumentation.aiopg import trace_integration - from opentelemetry.trace import TracerProvider - - trace.set_tracer_provider(TracerProvider()) - - trace_integration(aiopg.connection, "_connect", "postgresql", "sql") - -API ---- -""" -import logging -import typing - -import aiopg -import wrapt - -from opentelemetry.instrumentation.aiopg.aiopg_integration import ( - AiopgIntegration, - get_traced_connection_proxy, -) -from opentelemetry.instrumentation.aiopg.version import __version__ -from opentelemetry.instrumentation.utils import unwrap -from opentelemetry.trace import TracerProvider - -logger = logging.getLogger(__name__) - - -def trace_integration( - database_component: str, - database_type: str = "", - connection_attributes: typing.Dict = None, - tracer_provider: typing.Optional[TracerProvider] = None, -): - """Integrate with aiopg library. - based on dbapi integration, where replaced sync wrap methods to async - - Args: - database_component: Database driver name or - database name "postgreSQL". - database_type: The Database type. For any SQL database, "sql". - connection_attributes: Attribute names for database, port, host and - user in Connection object. - tracer_provider: The :class:`opentelemetry.trace.TracerProvider` to - use. If ommited the current configured one is used. - """ - - wrap_connect( - __name__, - database_component, - database_type, - connection_attributes, - __version__, - tracer_provider, - ) - - -def wrap_connect( - name: str, - database_component: str, - database_type: str = "", - connection_attributes: typing.Dict = None, - version: str = "", - tracer_provider: typing.Optional[TracerProvider] = None, -): - """Integrate with aiopg library. - https://github.com/aio-libs/aiopg - - Args: - name: Name of opentelemetry extension for aiopg. - database_component: Database driver name - or database name "postgreSQL". - database_type: The Database type. For any SQL database, "sql". - connection_attributes: Attribute names for database, port, host and - user in Connection object. - version: Version of opentelemetry extension for aiopg. - tracer_provider: The :class:`opentelemetry.trace.TracerProvider` to - use. If ommited the current configured one is used. - """ - - # pylint: disable=unused-argument - async def wrap_connect_( - wrapped: typing.Callable[..., typing.Any], - instance: typing.Any, - args: typing.Tuple[typing.Any, typing.Any], - kwargs: typing.Dict[typing.Any, typing.Any], - ): - db_integration = AiopgIntegration( - name, - database_component, - database_type=database_type, - connection_attributes=connection_attributes, - version=version, - tracer_provider=tracer_provider, - ) - return await db_integration.wrapped_connection(wrapped, args, kwargs) - - try: - wrapt.wrap_function_wrapper(aiopg, "connect", wrap_connect_) - except Exception as ex: # pylint: disable=broad-except - logger.warning("Failed to integrate with aiopg. %s", str(ex)) - - -def unwrap_connect(): - """Disable integration with aiopg library. - https://github.com/aio-libs/aiopg - """ - - unwrap(aiopg, "connect") - - -def instrument_connection( - name: str, - connection, - database_component: str, - database_type: str = "", - connection_attributes: typing.Dict = None, - version: str = "", - tracer_provider: typing.Optional[TracerProvider] = None, -): - """Enable instrumentation in a database connection. - - Args: - name: Name of opentelemetry extension for aiopg. - connection: The connection to instrument. - database_component: Database driver name or database name "postgreSQL". - database_type: The Database type. For any SQL database, "sql". - connection_attributes: Attribute names for database, port, host and - user in a connection object. - version: Version of opentelemetry extension for aiopg. - tracer_provider: The :class:`opentelemetry.trace.TracerProvider` to - use. If ommited the current configured one is used. - - Returns: - An instrumented connection. - """ - db_integration = AiopgIntegration( - name, - database_component, - database_type, - connection_attributes=connection_attributes, - version=version, - tracer_provider=tracer_provider, - ) - db_integration.get_connection_attributes(connection) - return get_traced_connection_proxy(connection, db_integration) - - -def uninstrument_connection(connection): - """Disable instrumentation in a database connection. - - Args: - connection: The connection to uninstrument. - - Returns: - An uninstrumented connection. - """ - if isinstance(connection, wrapt.ObjectProxy): - return connection.__wrapped__ - - logger.warning("Connection is not instrumented") - return connection - - -def wrap_create_pool( - name: str, - database_component: str, - database_type: str = "", - connection_attributes: typing.Dict = None, - version: str = "", - tracer_provider: typing.Optional[TracerProvider] = None, -): - # pylint: disable=unused-argument - async def wrap_create_pool_( - wrapped: typing.Callable[..., typing.Any], - instance: typing.Any, - args: typing.Tuple[typing.Any, typing.Any], - kwargs: typing.Dict[typing.Any, typing.Any], - ): - db_integration = AiopgIntegration( - name, - database_component, - database_type, - connection_attributes=connection_attributes, - version=version, - tracer_provider=tracer_provider, - ) - return await db_integration.wrapped_pool(wrapped, args, kwargs) - - try: - wrapt.wrap_function_wrapper(aiopg, "create_pool", wrap_create_pool_) - except Exception as ex: # pylint: disable=broad-except - logger.warning("Failed to integrate with DB API. %s", str(ex)) - - -def unwrap_create_pool(): - """Disable integration with aiopg library. - https://github.com/aio-libs/aiopg - """ - unwrap(aiopg, "create_pool") diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-aiopg/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py b/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py deleted file mode 100644 index 78ea4552e23..00000000000 --- a/instrumentation/opentelemetry-instrumentation-aiopg/tests/test_aiopg_integration.py +++ /dev/null @@ -1,504 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import asyncio -import logging -from unittest import mock -from unittest.mock import MagicMock - -import aiopg -from aiopg.utils import _ContextManager, _PoolAcquireContextManager - -import opentelemetry.instrumentation.aiopg -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.aiopg import AiopgInstrumentor, wrappers -from opentelemetry.instrumentation.aiopg.aiopg_integration import ( - AiopgIntegration, -) -from opentelemetry.sdk import resources -from opentelemetry.test.test_base import TestBase - - -def async_call(coro): - loop = asyncio.get_event_loop() - return loop.run_until_complete(coro) - - -class TestAiopgInstrumentor(TestBase): - def setUp(self): - super().setUp() - self.origin_aiopg_connect = aiopg.connect - self.origin_aiopg_create_pool = aiopg.create_pool - aiopg.connect = mock_connect - aiopg.create_pool = mock_create_pool - - def tearDown(self): - super().tearDown() - aiopg.connect = self.origin_aiopg_connect - aiopg.create_pool = self.origin_aiopg_create_pool - with self.disable_logging(): - AiopgInstrumentor().uninstrument() - - def test_instrumentor_connect(self): - AiopgInstrumentor().instrument() - - cnx = async_call(aiopg.connect(database="test")) - - cursor = async_call(cnx.cursor()) - - query = "SELECT * FROM test" - async_call(cursor.execute(query)) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - # Check version and name in span's instrumentation info - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.aiopg - ) - - # check that no spans are generated after uninstrument - AiopgInstrumentor().uninstrument() - - cnx = async_call(aiopg.connect(database="test")) - cursor = async_call(cnx.cursor()) - query = "SELECT * FROM test" - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - def test_instrumentor_create_pool(self): - AiopgInstrumentor().instrument() - - pool = async_call(aiopg.create_pool(database="test")) - cnx = async_call(pool.acquire()) - cursor = async_call(cnx.cursor()) - - query = "SELECT * FROM test" - async_call(cursor.execute(query)) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - # Check version and name in span's instrumentation info - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.aiopg - ) - - # check that no spans are generated after uninstrument - AiopgInstrumentor().uninstrument() - - pool = async_call(aiopg.create_pool(database="test")) - cnx = async_call(pool.acquire()) - cursor = async_call(cnx.cursor()) - query = "SELECT * FROM test" - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - def test_custom_tracer_provider_connect(self): - resource = resources.Resource.create({}) - result = self.create_tracer_provider(resource=resource) - tracer_provider, exporter = result - - AiopgInstrumentor().instrument(tracer_provider=tracer_provider) - - cnx = async_call(aiopg.connect(database="test")) - cursor = async_call(cnx.cursor()) - query = "SELECT * FROM test" - async_call(cursor.execute(query)) - - spans_list = exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertIs(span.resource, resource) - - def test_custom_tracer_provider_create_pool(self): - resource = resources.Resource.create({}) - result = self.create_tracer_provider(resource=resource) - tracer_provider, exporter = result - - AiopgInstrumentor().instrument(tracer_provider=tracer_provider) - - pool = async_call(aiopg.create_pool(database="test")) - cnx = async_call(pool.acquire()) - cursor = async_call(cnx.cursor()) - query = "SELECT * FROM test" - async_call(cursor.execute(query)) - - spans_list = exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - self.assertIs(span.resource, resource) - - def test_instrument_connection(self): - cnx = async_call(aiopg.connect(database="test")) - query = "SELECT * FROM test" - cursor = async_call(cnx.cursor()) - async_call(cursor.execute(query)) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 0) - - cnx = AiopgInstrumentor().instrument_connection(cnx) - cursor = async_call(cnx.cursor()) - async_call(cursor.execute(query)) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - def test_uninstrument_connection(self): - AiopgInstrumentor().instrument() - cnx = async_call(aiopg.connect(database="test")) - query = "SELECT * FROM test" - cursor = async_call(cnx.cursor()) - async_call(cursor.execute(query)) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - cnx = AiopgInstrumentor().uninstrument_connection(cnx) - cursor = async_call(cnx.cursor()) - async_call(cursor.execute(query)) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - -class TestAiopgIntegration(TestBase): - def setUp(self): - super().setUp() - self.tracer = self.tracer_provider.get_tracer(__name__) - - def test_span_succeeded(self): - connection_props = { - "database": "testdatabase", - "server_host": "testhost", - "server_port": 123, - "user": "testuser", - } - connection_attributes = { - "database": "database", - "port": "server_port", - "host": "server_host", - "user": "user", - } - db_integration = AiopgIntegration( - self.tracer, "testcomponent", "testtype", connection_attributes - ) - mock_connection = async_call( - db_integration.wrapped_connection( - mock_connect, {}, connection_props - ) - ) - cursor = async_call(mock_connection.cursor()) - async_call(cursor.execute("Test query", ("param1Value", False))) - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertEqual(span.name, "testcomponent.testdatabase") - self.assertIs(span.kind, trace_api.SpanKind.CLIENT) - - self.assertEqual(span.attributes["component"], "testcomponent") - self.assertEqual(span.attributes["db.type"], "testtype") - self.assertEqual(span.attributes["db.instance"], "testdatabase") - self.assertEqual(span.attributes["db.statement"], "Test query") - self.assertEqual( - span.attributes["db.statement.parameters"], - "('param1Value', False)", - ) - self.assertEqual(span.attributes["db.user"], "testuser") - self.assertEqual(span.attributes["net.peer.name"], "testhost") - self.assertEqual(span.attributes["net.peer.port"], 123) - self.assertIs( - span.status.status_code, trace_api.status.StatusCode.UNSET, - ) - - def test_span_not_recording(self): - connection_props = { - "database": "testdatabase", - "server_host": "testhost", - "server_port": 123, - "user": "testuser", - } - connection_attributes = { - "database": "database", - "port": "server_port", - "host": "server_host", - "user": "user", - } - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - db_integration = AiopgIntegration( - mock_tracer, "testcomponent", "testtype", connection_attributes - ) - mock_connection = async_call( - db_integration.wrapped_connection( - mock_connect, {}, connection_props - ) - ) - cursor = async_call(mock_connection.cursor()) - async_call(cursor.execute("Test query", ("param1Value", False))) - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_span_failed(self): - db_integration = AiopgIntegration(self.tracer, "testcomponent") - mock_connection = async_call( - db_integration.wrapped_connection(mock_connect, {}, {}) - ) - cursor = async_call(mock_connection.cursor()) - with self.assertRaises(Exception): - async_call(cursor.execute("Test query", throw_exception=True)) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertEqual(span.attributes["db.statement"], "Test query") - self.assertIs( - span.status.status_code, trace_api.status.StatusCode.ERROR, - ) - self.assertEqual(span.status.description, "Test Exception") - - def test_executemany(self): - db_integration = AiopgIntegration(self.tracer, "testcomponent") - mock_connection = async_call( - db_integration.wrapped_connection(mock_connect, {}, {}) - ) - cursor = async_call(mock_connection.cursor()) - async_call(cursor.executemany("Test query")) - spans_list = self.memory_exporter.get_finished_spans() - - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertEqual(span.attributes["db.statement"], "Test query") - - def test_callproc(self): - db_integration = AiopgIntegration(self.tracer, "testcomponent") - mock_connection = async_call( - db_integration.wrapped_connection(mock_connect, {}, {}) - ) - cursor = async_call(mock_connection.cursor()) - async_call(cursor.callproc("Test stored procedure")) - spans_list = self.memory_exporter.get_finished_spans() - - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertEqual( - span.attributes["db.statement"], "Test stored procedure" - ) - - def test_wrap_connect(self): - aiopg_mock = AiopgMock() - with mock.patch("aiopg.connect", aiopg_mock.connect): - wrappers.wrap_connect(self.tracer, "-") - connection = async_call(aiopg.connect()) - self.assertEqual(aiopg_mock.connect_call_count, 1) - self.assertIsInstance(connection.__wrapped__, mock.Mock) - - def test_unwrap_connect(self): - wrappers.wrap_connect(self.tracer, "-") - aiopg_mock = AiopgMock() - with mock.patch("aiopg.connect", aiopg_mock.connect): - connection = async_call(aiopg.connect()) - self.assertEqual(aiopg_mock.connect_call_count, 1) - wrappers.unwrap_connect() - connection = async_call(aiopg.connect()) - self.assertEqual(aiopg_mock.connect_call_count, 2) - self.assertIsInstance(connection, mock.Mock) - - def test_wrap_create_pool(self): - async def check_connection(pool): - async with pool.acquire() as connection: - self.assertEqual(aiopg_mock.create_pool_call_count, 1) - self.assertIsInstance( - connection.__wrapped__, AiopgConnectionMock - ) - - aiopg_mock = AiopgMock() - with mock.patch("aiopg.create_pool", aiopg_mock.create_pool): - wrappers.wrap_create_pool(self.tracer, "-") - pool = async_call(aiopg.create_pool()) - async_call(check_connection(pool)) - - def test_unwrap_create_pool(self): - async def check_connection(pool): - async with pool.acquire() as connection: - self.assertEqual(aiopg_mock.create_pool_call_count, 2) - self.assertIsInstance(connection, AiopgConnectionMock) - - aiopg_mock = AiopgMock() - with mock.patch("aiopg.create_pool", aiopg_mock.create_pool): - wrappers.wrap_create_pool(self.tracer, "-") - pool = async_call(aiopg.create_pool()) - self.assertEqual(aiopg_mock.create_pool_call_count, 1) - - wrappers.unwrap_create_pool() - pool = async_call(aiopg.create_pool()) - async_call(check_connection(pool)) - - def test_instrument_connection(self): - connection = mock.Mock() - # Avoid get_attributes failing because can't concatenate mock - connection.database = "-" - connection2 = wrappers.instrument_connection( - self.tracer, connection, "-" - ) - self.assertIs(connection2.__wrapped__, connection) - - def test_uninstrument_connection(self): - connection = mock.Mock() - # Set connection.database to avoid a failure because mock can't - # be concatenated - connection.database = "-" - connection2 = wrappers.instrument_connection( - self.tracer, connection, "-" - ) - self.assertIs(connection2.__wrapped__, connection) - - connection3 = wrappers.uninstrument_connection(connection2) - self.assertIs(connection3, connection) - - with self.assertLogs(level=logging.WARNING): - connection4 = wrappers.uninstrument_connection(connection) - self.assertIs(connection4, connection) - - -# pylint: disable=unused-argument -async def mock_connect(*args, **kwargs): - database = kwargs.get("database") - server_host = kwargs.get("server_host") - server_port = kwargs.get("server_port") - user = kwargs.get("user") - return MockConnection(database, server_port, server_host, user) - - -# pylint: disable=unused-argument -async def mock_create_pool(*args, **kwargs): - database = kwargs.get("database") - server_host = kwargs.get("server_host") - server_port = kwargs.get("server_port") - user = kwargs.get("user") - return MockPool(database, server_port, server_host, user) - - -class MockPool: - def __init__(self, database, server_port, server_host, user): - self.database = database - self.server_port = server_port - self.server_host = server_host - self.user = user - - async def release(self, conn): - return conn - - def acquire(self): - """Acquire free connection from the pool.""" - coro = self._acquire() - return _PoolAcquireContextManager(coro, self) - - async def _acquire(self): - connect = await mock_connect( - self.database, self.server_port, self.server_host, self.user - ) - return connect - - -class MockPsycopg2Connection: - def __init__(self, database, server_port, server_host, user): - self.database = database - self.server_port = server_port - self.server_host = server_host - self.user = user - - -class MockConnection: - def __init__(self, database, server_port, server_host, user): - self._conn = MockPsycopg2Connection( - database, server_port, server_host, user - ) - - # pylint: disable=no-self-use - def cursor(self): - coro = self._cursor() - return _ContextManager(coro) - - async def _cursor(self): - return MockCursor() - - def close(self): - pass - - -class MockCursor: - # pylint: disable=unused-argument, no-self-use - async def execute(self, query, params=None, throw_exception=False): - if throw_exception: - raise Exception("Test Exception") - - # pylint: disable=unused-argument, no-self-use - async def executemany(self, query, params=None, throw_exception=False): - if throw_exception: - raise Exception("Test Exception") - - # pylint: disable=unused-argument, no-self-use - async def callproc(self, query, params=None, throw_exception=False): - if throw_exception: - raise Exception("Test Exception") - - -class AiopgConnectionMock: - _conn = MagicMock() - - async def __aexit__(self, exc_type, exc_val, exc_tb): - pass - - async def __aenter__(self): - return MagicMock() - - -class AiopgPoolMock: - async def release(self, conn): - return conn - - def acquire(self): - coro = self._acquire() - return _PoolAcquireContextManager(coro, self) - - async def _acquire(self): - return AiopgConnectionMock() - - -class AiopgMock: - def __init__(self): - self.connect_call_count = 0 - self.create_pool_call_count = 0 - - async def connect(self, *args, **kwargs): - self.connect_call_count += 1 - return MagicMock() - - async def create_pool(self, *args, **kwargs): - self.create_pool_call_count += 1 - return AiopgPoolMock() diff --git a/instrumentation/opentelemetry-instrumentation-asgi/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-asgi/CHANGELOG.md deleted file mode 100644 index 686b8cf830a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asgi/CHANGELOG.md +++ /dev/null @@ -1,16 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-asgi - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) - -## 0.8b0 - -Released 2020-05-27 - -- Add ASGI middleware ([#716](https://github.com/open-telemetry/opentelemetry-python/pull/716)) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/README.rst b/instrumentation/opentelemetry-instrumentation-asgi/README.rst deleted file mode 100644 index 3eb8e2dda72..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asgi/README.rst +++ /dev/null @@ -1,71 +0,0 @@ -OpenTelemetry ASGI Instrumentation -================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-asgi.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-asgi/ - - -This library provides a ASGI middleware that can be used on any ASGI framework -(such as Django, Starlette, FastAPI or Quart) to track requests timing through OpenTelemetry. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-asgi - - -Usage (Quart) -------------- - -.. code-block:: python - - from quart import Quart - from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware - - app = Quart(__name__) - app.asgi_app = OpenTelemetryMiddleware(app.asgi_app) - - @app.route("/") - async def hello(): - return "Hello!" - - if __name__ == "__main__": - app.run(debug=True) - - -Usage (Django 3.0) ------------------- - -Modify the application's ``asgi.py`` file as shown below. - -.. code-block:: python - - import os - from django.core.asgi import get_asgi_application - from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware - - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'asgi_example.settings') - - application = get_asgi_application() - application = OpenTelemetryMiddleware(application) - - -Usage (Raw ASGI) ----------------- - -.. code-block:: python - - from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware - - app = ... # An ASGI application. - app = OpenTelemetryMiddleware(app) - - -References ----------- - -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-asgi/setup.cfg b/instrumentation/opentelemetry-instrumentation-asgi/setup.cfg deleted file mode 100644 index dafb8379437..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asgi/setup.cfg +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-asgi -description = ASGI instrumentation for OpenTelemetry -long_description = file: README.rst -long_description_content_type = text/x-rst -author = OpenTelemetry Authors -author_email = cncf-opentelemetry-contributors@lists.cncf.io -url = https://github.com/open-telemetry/opentelemetry-python/instrumentation/opentelemetry-instrumentation-asgi -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - asgiref ~= 3.0 - -[options.extras_require] -test = - opentelemetry-test - -[options.packages.find] -where = src diff --git a/instrumentation/opentelemetry-instrumentation-asgi/setup.py b/instrumentation/opentelemetry-instrumentation-asgi/setup.py deleted file mode 100644 index 3369352fe1e..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asgi/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "asgi", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py deleted file mode 100644 index 1c442889a1b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -The opentelemetry-instrumentation-asgi package provides an ASGI middleware that can be used -on any ASGI framework (such as Django-channels / Quart) to track requests -timing through OpenTelemetry. -""" - -import operator -import typing -import urllib -from functools import wraps -from typing import Tuple - -from asgiref.compatibility import guarantee_single_callable - -from opentelemetry import context, propagators, trace -from opentelemetry.instrumentation.asgi.version import __version__ # noqa -from opentelemetry.instrumentation.utils import http_status_to_status_code -from opentelemetry.trace.propagation.textmap import DictGetter -from opentelemetry.trace.status import Status, StatusCode - - -class CarrierGetter(DictGetter): - def get(self, carrier: dict, key: str) -> typing.List[str]: - """Getter implementation to retrieve a HTTP header value from the ASGI - scope. - - Args: - carrier: ASGI scope object - key: header name in scope - Returns: - A list with a single string with the header value if it exists, - else an empty list. - """ - headers = carrier.get("headers") - return [ - _value.decode("utf8") - for (_key, _value) in headers - if _key.decode("utf8") == key - ] - - -carrier_getter = CarrierGetter() - - -def collect_request_attributes(scope): - """Collects HTTP request attributes from the ASGI scope and returns a - dictionary to be used as span creation attributes.""" - server = scope.get("server") or ["0.0.0.0", 80] - port = server[1] - server_host = server[0] + (":" + str(port) if port != 80 else "") - full_path = scope.get("root_path", "") + scope.get("path", "") - http_url = scope.get("scheme", "http") + "://" + server_host + full_path - query_string = scope.get("query_string") - if query_string and http_url: - if isinstance(query_string, bytes): - query_string = query_string.decode("utf8") - http_url = http_url + ("?" + urllib.parse.unquote(query_string)) - - result = { - "component": scope["type"], - "http.scheme": scope.get("scheme"), - "http.host": server_host, - "host.port": port, - "http.flavor": scope.get("http_version"), - "http.target": scope.get("path"), - "http.url": http_url, - } - http_method = scope.get("method") - if http_method: - result["http.method"] = http_method - http_host_value = ",".join(carrier_getter.get(scope, "host")) - if http_host_value: - result["http.server_name"] = http_host_value - http_user_agent = carrier_getter.get(scope, "user-agent") - if len(http_user_agent) > 0: - result["http.user_agent"] = http_user_agent[0] - - if "client" in scope and scope["client"] is not None: - result["net.peer.ip"] = scope.get("client")[0] - result["net.peer.port"] = scope.get("client")[1] - - # remove None values - result = {k: v for k, v in result.items() if v is not None} - - return result - - -def set_status_code(span, status_code): - """Adds HTTP response attributes to span using the status_code argument.""" - if not span.is_recording(): - return - try: - status_code = int(status_code) - except ValueError: - span.set_status( - Status( - StatusCode.ERROR, - "Non-integer HTTP status: " + repr(status_code), - ) - ) - else: - span.set_attribute("http.status_code", status_code) - span.set_status(Status(http_status_to_status_code(status_code))) - - -def get_default_span_details(scope: dict) -> Tuple[str, dict]: - """Default implementation for span_details_callback - - Args: - scope: the asgi scope dictionary - - Returns: - a tuple of the span, and any attributes to attach to the - span. - """ - method_or_path = scope.get("method") or scope.get("path") - - return method_or_path, {} - - -class OpenTelemetryMiddleware: - """The ASGI application middleware. - - This class is an ASGI middleware that starts and annotates spans for any - requests it is invoked with. - - Args: - app: The ASGI application callable to forward requests to. - span_details_callback: Callback which should return a string - and a tuple, representing the desired span name and a - dictionary with any additional span attributes to set. - Optional: Defaults to get_default_span_details. - """ - - def __init__(self, app, span_details_callback=None): - self.app = guarantee_single_callable(app) - self.tracer = trace.get_tracer(__name__, __version__) - self.span_details_callback = ( - span_details_callback or get_default_span_details - ) - - async def __call__(self, scope, receive, send): - """The ASGI application - - Args: - scope: A ASGI environment. - receive: An awaitable callable yielding dictionaries - send: An awaitable callable taking a single dictionary as argument. - """ - if scope["type"] not in ("http", "websocket"): - return await self.app(scope, receive, send) - - token = context.attach(propagators.extract(carrier_getter, scope)) - span_name, additional_attributes = self.span_details_callback(scope) - - try: - with self.tracer.start_as_current_span( - span_name + " asgi", kind=trace.SpanKind.SERVER, - ) as span: - if span.is_recording(): - attributes = collect_request_attributes(scope) - attributes.update(additional_attributes) - for key, value in attributes.items(): - span.set_attribute(key, value) - - @wraps(receive) - async def wrapped_receive(): - with self.tracer.start_as_current_span( - span_name + " asgi." + scope["type"] + ".receive" - ) as receive_span: - message = await receive() - if receive_span.is_recording(): - if message["type"] == "websocket.receive": - set_status_code(receive_span, 200) - receive_span.set_attribute("type", message["type"]) - return message - - @wraps(send) - async def wrapped_send(message): - with self.tracer.start_as_current_span( - span_name + " asgi." + scope["type"] + ".send" - ) as send_span: - if send_span.is_recording(): - if message["type"] == "http.response.start": - status_code = message["status"] - set_status_code(send_span, status_code) - elif message["type"] == "websocket.send": - set_status_code(send_span, 200) - send_span.set_attribute("type", message["type"]) - await send(message) - - await self.app(scope, wrapped_receive, wrapped_send) - finally: - context.detach(token) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/version.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-asgi/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py deleted file mode 100644 index cf8944ce6df..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py +++ /dev/null @@ -1,361 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys -import unittest -import unittest.mock as mock - -import opentelemetry.instrumentation.asgi as otel_asgi -from opentelemetry import trace as trace_api -from opentelemetry.test.asgitestutil import ( - AsgiTestBase, - setup_testing_defaults, -) - - -async def http_app(scope, receive, send): - message = await receive() - assert scope["type"] == "http" - if message.get("type") == "http.request": - await send( - { - "type": "http.response.start", - "status": 200, - "headers": [[b"Content-Type", b"text/plain"]], - } - ) - await send({"type": "http.response.body", "body": b"*"}) - - -async def websocket_app(scope, receive, send): - assert scope["type"] == "websocket" - while True: - message = await receive() - if message.get("type") == "websocket.connect": - await send({"type": "websocket.accept"}) - - if message.get("type") == "websocket.receive": - if message.get("text") == "ping": - await send({"type": "websocket.send", "text": "pong"}) - - if message.get("type") == "websocket.disconnect": - break - - -async def simple_asgi(scope, receive, send): - assert isinstance(scope, dict) - if scope["type"] == "http": - await http_app(scope, receive, send) - elif scope["type"] == "websocket": - await websocket_app(scope, receive, send) - - -async def error_asgi(scope, receive, send): - assert isinstance(scope, dict) - assert scope["type"] == "http" - message = await receive() - if message.get("type") == "http.request": - try: - raise ValueError - except ValueError: - scope["hack_exc_info"] = sys.exc_info() - await send( - { - "type": "http.response.start", - "status": 200, - "headers": [[b"Content-Type", b"text/plain"]], - } - ) - await send({"type": "http.response.body", "body": b"*"}) - - -class TestAsgiApplication(AsgiTestBase): - def validate_outputs(self, outputs, error=None, modifiers=None): - # Ensure modifiers is a list - modifiers = modifiers or [] - # Check for expected outputs - self.assertEqual(len(outputs), 2) - response_start = outputs[0] - response_body = outputs[1] - self.assertEqual(response_start["type"], "http.response.start") - self.assertEqual(response_body["type"], "http.response.body") - - # Check http response body - self.assertEqual(response_body["body"], b"*") - - # Check http response start - self.assertEqual(response_start["status"], 200) - self.assertEqual( - response_start["headers"], [[b"Content-Type", b"text/plain"]] - ) - - exc_info = self.scope.get("hack_exc_info") - if error: - self.assertIs(exc_info[0], error) - self.assertIsInstance(exc_info[1], error) - self.assertIsNotNone(exc_info[2]) - else: - self.assertIsNone(exc_info) - - # Check spans - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 4) - expected = [ - { - "name": "GET asgi.http.receive", - "kind": trace_api.SpanKind.INTERNAL, - "attributes": {"type": "http.request"}, - }, - { - "name": "GET asgi.http.send", - "kind": trace_api.SpanKind.INTERNAL, - "attributes": { - "http.status_code": 200, - "type": "http.response.start", - }, - }, - { - "name": "GET asgi.http.send", - "kind": trace_api.SpanKind.INTERNAL, - "attributes": {"type": "http.response.body"}, - }, - { - "name": "GET asgi", - "kind": trace_api.SpanKind.SERVER, - "attributes": { - "component": "http", - "http.method": "GET", - "http.scheme": "http", - "host.port": 80, - "http.host": "127.0.0.1", - "http.flavor": "1.0", - "http.target": "/", - "http.url": "http://127.0.0.1/", - "net.peer.ip": "127.0.0.1", - "net.peer.port": 32767, - }, - }, - ] - # Run our expected modifiers - for modifier in modifiers: - expected = modifier(expected) - # Check that output matches - for span, expected in zip(span_list, expected): - self.assertEqual(span.name, expected["name"]) - self.assertEqual(span.kind, expected["kind"]) - self.assertDictEqual(dict(span.attributes), expected["attributes"]) - - def test_basic_asgi_call(self): - """Test that spans are emitted as expected.""" - app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) - self.seed_app(app) - self.send_default_request() - outputs = self.get_all_output() - self.validate_outputs(outputs) - - def test_wsgi_not_recording(self): - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_as_current_span.return_value = mock_span - mock_tracer.start_as_current_span.return_value.__enter__ = mock_span - mock_tracer.start_as_current_span.return_value.__exit__ = mock_span - with mock.patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) - self.seed_app(app) - self.send_default_request() - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_asgi_exc_info(self): - """Test that exception information is emitted as expected.""" - app = otel_asgi.OpenTelemetryMiddleware(error_asgi) - self.seed_app(app) - self.send_default_request() - outputs = self.get_all_output() - self.validate_outputs(outputs, error=ValueError) - - def test_override_span_name(self): - """Test that span_names can be overwritten by our callback function.""" - span_name = "Dymaxion" - - def get_predefined_span_details(_): - return span_name, {} - - def update_expected_span_name(expected): - for entry in expected: - entry["name"] = " ".join( - [span_name] + entry["name"].split(" ")[-1:] - ) - return expected - - app = otel_asgi.OpenTelemetryMiddleware( - simple_asgi, span_details_callback=get_predefined_span_details - ) - self.seed_app(app) - self.send_default_request() - outputs = self.get_all_output() - self.validate_outputs(outputs, modifiers=[update_expected_span_name]) - - def test_behavior_with_scope_server_as_none(self): - """Test that middleware is ok when server is none in scope.""" - - def update_expected_server(expected): - expected[3]["attributes"].update( - { - "http.host": "0.0.0.0", - "host.port": 80, - "http.url": "http://0.0.0.0/", - } - ) - return expected - - self.scope["server"] = None - app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) - self.seed_app(app) - self.send_default_request() - outputs = self.get_all_output() - self.validate_outputs(outputs, modifiers=[update_expected_server]) - - def test_host_header(self): - """Test that host header is converted to http.server_name.""" - hostname = b"server_name_1" - - def update_expected_server(expected): - expected[3]["attributes"].update( - {"http.server_name": hostname.decode("utf8")} - ) - return expected - - self.scope["headers"].append([b"host", hostname]) - app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) - self.seed_app(app) - self.send_default_request() - outputs = self.get_all_output() - self.validate_outputs(outputs, modifiers=[update_expected_server]) - - def test_user_agent(self): - """Test that host header is converted to http.server_name.""" - user_agent = b"test-agent" - - def update_expected_user_agent(expected): - expected[3]["attributes"].update( - {"http.user_agent": user_agent.decode("utf8")} - ) - return expected - - self.scope["headers"].append([b"user-agent", user_agent]) - app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) - self.seed_app(app) - self.send_default_request() - outputs = self.get_all_output() - self.validate_outputs(outputs, modifiers=[update_expected_user_agent]) - - def test_websocket(self): - self.scope = { - "type": "websocket", - "http_version": "1.1", - "scheme": "ws", - "path": "/", - "query_string": b"", - "headers": [], - "client": ("127.0.0.1", 32767), - "server": ("127.0.0.1", 80), - } - app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) - self.seed_app(app) - self.send_input({"type": "websocket.connect"}) - self.send_input({"type": "websocket.receive", "text": "ping"}) - self.send_input({"type": "websocket.disconnect"}) - self.get_all_output() - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 6) - expected = [ - "/ asgi.websocket.receive", - "/ asgi.websocket.send", - "/ asgi.websocket.receive", - "/ asgi.websocket.send", - "/ asgi.websocket.receive", - "/ asgi", - ] - actual = [span.name for span in span_list] - self.assertListEqual(actual, expected) - - def test_lifespan(self): - self.scope["type"] = "lifespan" - app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) - self.seed_app(app) - self.send_default_request() - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 0) - - -class TestAsgiAttributes(unittest.TestCase): - def setUp(self): - self.scope = {} - setup_testing_defaults(self.scope) - self.span = mock.create_autospec(trace_api.Span, spec_set=True) - - def test_request_attributes(self): - self.scope["query_string"] = b"foo=bar" - - attrs = otel_asgi.collect_request_attributes(self.scope) - self.assertDictEqual( - attrs, - { - "component": "http", - "http.method": "GET", - "http.host": "127.0.0.1", - "http.target": "/", - "http.url": "http://127.0.0.1/?foo=bar", - "host.port": 80, - "http.scheme": "http", - "http.flavor": "1.0", - "net.peer.ip": "127.0.0.1", - "net.peer.port": 32767, - }, - ) - - def test_query_string(self): - self.scope["query_string"] = b"foo=bar" - attrs = otel_asgi.collect_request_attributes(self.scope) - self.assertEqual(attrs["http.url"], "http://127.0.0.1/?foo=bar") - - def test_query_string_percent_bytes(self): - self.scope["query_string"] = b"foo%3Dbar" - attrs = otel_asgi.collect_request_attributes(self.scope) - self.assertEqual(attrs["http.url"], "http://127.0.0.1/?foo=bar") - - def test_query_string_percent_str(self): - self.scope["query_string"] = "foo%3Dbar" - attrs = otel_asgi.collect_request_attributes(self.scope) - self.assertEqual(attrs["http.url"], "http://127.0.0.1/?foo=bar") - - def test_response_attributes(self): - otel_asgi.set_status_code(self.span, 404) - expected = (mock.call("http.status_code", 404),) - self.assertEqual(self.span.set_attribute.call_count, 1) - self.assertEqual(self.span.set_attribute.call_count, 1) - self.span.set_attribute.assert_has_calls(expected, any_order=True) - - def test_response_attributes_invalid_status_code(self): - otel_asgi.set_status_code(self.span, "Invalid Status Code") - self.assertEqual(self.span.set_status.call_count, 1) - - -if __name__ == "__main__": - unittest.main() diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-asyncpg/CHANGELOG.md deleted file mode 100644 index 56f261c9b54..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-asyncpg - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) - -## Version 0.11b0 - -Released 2020-07-28 - -- Shouldn't capture query parameters by default - ([#854](https://github.com/open-telemetry/opentelemetry-python/pull/854)) - -## Version 0.10b0 - -Released 2020-06-23 - -- Initial Release ([#814](https://github.com/open-telemetry/opentelemetry-python/pull/814)) diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/README.rst b/instrumentation/opentelemetry-instrumentation-asyncpg/README.rst deleted file mode 100644 index 33c60852cd4..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/README.rst +++ /dev/null @@ -1,23 +0,0 @@ -OpenTelemetry asyncpg Instrumentation -===================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-asyncpg.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-asyncpg/ - -This library allows tracing PostgreSQL queries made by the -`asyncpg `_ library. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-asyncpg - -References ----------- - -* `OpenTelemetry asyncpg Instrumentation `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/setup.cfg b/instrumentation/opentelemetry-instrumentation-asyncpg/setup.cfg deleted file mode 100644 index 0e0e32fc8bc..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/setup.cfg +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-asyncpg -description = OpenTelemetry instrumentation for AsyncPG -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/instrumentation/opentelemetry-instrumentation-asyncpg -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - asyncpg >= 0.12.0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - asyncpg = opentelemetry.instrumentation.asyncpg:AsyncPGInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/setup.py b/instrumentation/opentelemetry-instrumentation-asyncpg/setup.py deleted file mode 100644 index 2ad47ac9d98..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "asyncpg", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py b/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py deleted file mode 100644 index 2f4ecaf3af6..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -This library allows tracing PostgreSQL queries made by the -`asyncpg `_ library. - -Usage ------ - -.. code-block:: python - - import asyncpg - from opentelemetry.instrumentation.asyncpg import AsyncPGInstrumentor - - # You can optionally pass a custom TracerProvider to AsyncPGInstrumentor.instrument() - AsyncPGInstrumentor().instrument() - conn = await asyncpg.connect(user='user', password='password', - database='database', host='127.0.0.1') - values = await conn.fetch('''SELECT 42;''') - -API ---- -""" - -import asyncpg -import wrapt -from asyncpg import exceptions - -from opentelemetry import trace -from opentelemetry.instrumentation.asyncpg.version import __version__ -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.utils import unwrap -from opentelemetry.trace import SpanKind -from opentelemetry.trace.status import Status, StatusCode - -_APPLIED = "_opentelemetry_tracer" - - -def _hydrate_span_from_args(connection, query, parameters) -> dict: - span_attributes = {"db.type": "sql"} - - params = getattr(connection, "_params", None) - span_attributes["db.instance"] = getattr(params, "database", None) - span_attributes["db.user"] = getattr(params, "user", None) - - if query is not None: - span_attributes["db.statement"] = query - - if parameters is not None and len(parameters) > 0: - span_attributes["db.statement.parameters"] = str(parameters) - - return span_attributes - - -class AsyncPGInstrumentor(BaseInstrumentor): - def __init__(self, capture_parameters=False): - super().__init__() - self.capture_parameters = capture_parameters - - def _instrument(self, **kwargs): - tracer_provider = kwargs.get( - "tracer_provider", trace.get_tracer_provider() - ) - setattr( - asyncpg, - _APPLIED, - tracer_provider.get_tracer("asyncpg", __version__), - ) - - for method in [ - "Connection.execute", - "Connection.executemany", - "Connection.fetch", - "Connection.fetchval", - "Connection.fetchrow", - ]: - wrapt.wrap_function_wrapper( - "asyncpg.connection", method, self._do_execute - ) - - def _uninstrument(self, **__): - delattr(asyncpg, _APPLIED) - for method in [ - "execute", - "executemany", - "fetch", - "fetchval", - "fetchrow", - ]: - unwrap(asyncpg.Connection, method) - - async def _do_execute(self, func, instance, args, kwargs): - tracer = getattr(asyncpg, _APPLIED) - - exception = None - - with tracer.start_as_current_span( - "postgresql", kind=SpanKind.CLIENT - ) as span: - if span.is_recording(): - span_attributes = _hydrate_span_from_args( - instance, - args[0], - args[1:] if self.capture_parameters else None, - ) - for attribute, value in span_attributes.items(): - span.set_attribute(attribute, value) - - try: - result = await func(*args, **kwargs) - except Exception as exc: # pylint: disable=W0703 - exception = exc - raise - finally: - if span.is_recording() and exception is not None: - span.set_status(Status(StatusCode.ERROR)) - - return result diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/version.py b/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-asyncpg/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/tests/test_asyncpg_wrapper.py b/instrumentation/opentelemetry-instrumentation-asyncpg/tests/test_asyncpg_wrapper.py deleted file mode 100644 index 33b121ce53a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/tests/test_asyncpg_wrapper.py +++ /dev/null @@ -1,35 +0,0 @@ -import asyncpg -from asyncpg import Connection - -from opentelemetry.instrumentation.asyncpg import AsyncPGInstrumentor -from opentelemetry.test.test_base import TestBase - - -class TestAsyncPGInstrumentation(TestBase): - def test_instrumentation_flags(self): - AsyncPGInstrumentor().instrument() - self.assertTrue(hasattr(asyncpg, "_opentelemetry_tracer")) - AsyncPGInstrumentor().uninstrument() - self.assertFalse(hasattr(asyncpg, "_opentelemetry_tracer")) - - def test_duplicated_instrumentation(self): - AsyncPGInstrumentor().instrument() - AsyncPGInstrumentor().instrument() - AsyncPGInstrumentor().instrument() - AsyncPGInstrumentor().uninstrument() - for method_name in ["execute", "fetch"]: - method = getattr(Connection, method_name, None) - self.assertFalse( - hasattr(method, "_opentelemetry_ext_asyncpg_applied") - ) - - def test_duplicated_uninstrumentation(self): - AsyncPGInstrumentor().instrument() - AsyncPGInstrumentor().uninstrument() - AsyncPGInstrumentor().uninstrument() - AsyncPGInstrumentor().uninstrument() - for method_name in ["execute", "fetch"]: - method = getattr(Connection, method_name, None) - self.assertFalse( - hasattr(method, "_opentelemetry_ext_asyncpg_applied") - ) diff --git a/instrumentation/opentelemetry-instrumentation-boto/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-boto/CHANGELOG.md deleted file mode 100644 index ea74ff9de27..00000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/CHANGELOG.md +++ /dev/null @@ -1,31 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-boto - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) - -## Version 0.11b0 - -Released 2020-07-28 - -- ext/boto and ext/botocore: fails to export spans via jaeger -([#866](https://github.com/open-telemetry/opentelemetry-python/pull/866)) - -## 0.9b0 - -Released 2020-06-10 - -- Initial release - diff --git a/instrumentation/opentelemetry-instrumentation-boto/LICENSE b/instrumentation/opentelemetry-instrumentation-boto/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-boto/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-boto/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-boto/README.rst b/instrumentation/opentelemetry-instrumentation-boto/README.rst deleted file mode 100644 index 2b40321c006..00000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/README.rst +++ /dev/null @@ -1,23 +0,0 @@ -OpenTelemetry Boto Tracing -========================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-boto.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-boto/ - -This library allows tracing requests made by the Boto library. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-boto - - -References ----------- - -* `OpenTelemetry Boto Tracing `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-boto/setup.cfg b/instrumentation/opentelemetry-instrumentation-boto/setup.cfg deleted file mode 100644 index fadbac4c5cb..00000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/setup.cfg +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-boto -description = OpenTelemetry Boto instrumentation -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/master/instrumentation/opentelemetry-instrumentation-boto -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - boto ~= 2.0 - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - opentelemetry-instrumentation-botocore == 0.16.dev0 - -[options.extras_require] -test = - boto~=2.0 - moto~=1.0 - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - boto = opentelemetry.instrumentation.boto:BotoInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-boto/setup.py b/instrumentation/opentelemetry-instrumentation-boto/setup.py deleted file mode 100644 index 2bd68894f33..00000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "boto", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/__init__.py b/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/__init__.py deleted file mode 100644 index 3bef955d148..00000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/__init__.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Instrument `Boto`_ to trace service requests. - -There are two options for instrumenting code. The first option is to use the -``opentelemetry-instrument`` executable which will automatically -instrument your Boto client. The second is to programmatically enable -instrumentation via the following code: - -.. _boto: https://pypi.org/project/boto/ - -Usage ------ - -.. code:: python - - from opentelemetry import trace - from opentelemetry.instrumentation.boto import BotoInstrumentor - from opentelemetry.sdk.trace import TracerProvider - import boto - - trace.set_tracer_provider(TracerProvider()) - - # Instrument Boto - BotoInstrumentor().instrument(tracer_provider=trace.get_tracer_provider()) - - # This will create a span with Boto-specific attributes - ec2 = boto.ec2.connect_to_region("us-west-2") - ec2.get_all_instances() - -API ---- -""" - -import logging -from inspect import currentframe - -from boto.connection import AWSAuthConnection, AWSQueryConnection -from wrapt import wrap_function_wrapper - -from opentelemetry.instrumentation.boto.version import __version__ -from opentelemetry.instrumentation.botocore import add_span_arg_tags, unwrap -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.sdk.trace import Resource -from opentelemetry.trace import SpanKind, get_tracer - -logger = logging.getLogger(__name__) - - -def _get_instance_region_name(instance): - region = getattr(instance, "region", None) - - if not region: - return None - if isinstance(region, str): - return region.split(":")[1] - return region.name - - -class BotoInstrumentor(BaseInstrumentor): - """A instrumentor for Boto - - See `BaseInstrumentor` - """ - - def __init__(self): - super().__init__() - self._original_boto = None - - def _instrument(self, **kwargs): - # AWSQueryConnection and AWSAuthConnection are two different classes - # called by different services for connection. - # For exemple EC2 uses AWSQueryConnection and S3 uses - # AWSAuthConnection - - # FIXME should the tracer provider be accessed via Configuration - # instead? - # pylint: disable=attribute-defined-outside-init - self._tracer = get_tracer( - __name__, __version__, kwargs.get("tracer_provider") - ) - - wrap_function_wrapper( - "boto.connection", - "AWSQueryConnection.make_request", - self._patched_query_request, - ) - wrap_function_wrapper( - "boto.connection", - "AWSAuthConnection.make_request", - self._patched_auth_request, - ) - - def _uninstrument(self, **kwargs): - unwrap(AWSQueryConnection, "make_request") - unwrap(AWSAuthConnection, "make_request") - - def _common_request( # pylint: disable=too-many-locals - self, - args_name, - traced_args, - operation_name, - original_func, - instance, - args, - kwargs, - ): - - endpoint_name = getattr(instance, "host").split(".")[0] - - with self._tracer.start_as_current_span( - "{}.command".format(endpoint_name), kind=SpanKind.CONSUMER, - ) as span: - if args: - http_method = args[0] - span.resource = Resource( - attributes={ - "endpoint": endpoint_name, - "http_method": http_method.lower(), - } - ) - else: - span.resource = Resource( - attributes={"endpoint": endpoint_name} - ) - - # Original func returns a boto.connection.HTTPResponse object - result = original_func(*args, **kwargs) - - if span.is_recording(): - add_span_arg_tags( - span, endpoint_name, args, args_name, traced_args, - ) - - # Obtaining region name - region_name = _get_instance_region_name(instance) - - meta = { - "aws.agent": "boto", - "aws.operation": operation_name, - } - if region_name: - meta["aws.region"] = region_name - - for key, value in meta.items(): - span.set_attribute(key, value) - - span.set_attribute( - "http.status_code", getattr(result, "status") - ) - span.set_attribute("http.method", getattr(result, "_method")) - - return result - - def _patched_query_request(self, original_func, instance, args, kwargs): - - return self._common_request( - ("operation_name", "params", "path", "verb"), - ["operation_name", "params", "path"], - args[0] if args else None, - original_func, - instance, - args, - kwargs, - ) - - def _patched_auth_request(self, original_func, instance, args, kwargs): - operation_name = None - - frame = currentframe().f_back - operation_name = None - while frame: - if frame.f_code.co_name == "make_request": - operation_name = frame.f_back.f_code.co_name - break - frame = frame.f_back - - return self._common_request( - ( - "method", - "path", - "headers", - "data", - "host", - "auth_path", - "sender", - ), - ["path", "data", "host"], - operation_name, - original_func, - instance, - args, - kwargs, - ) diff --git a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/version.py b/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/src/opentelemetry/instrumentation/boto/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-boto/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-boto/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-boto/tests/conftest.py b/instrumentation/opentelemetry-instrumentation-boto/tests/conftest.py deleted file mode 100644 index 884c6753c16..00000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/tests/conftest.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from os import environ - - -def pytest_sessionstart(session): - # pylint: disable=unused-argument - environ["AWS_ACCESS_KEY_ID"] = "testing" - environ["AWS_SECRET_ACCESS_KEY"] = "testing" - environ["AWS_SECURITY_TOKEN"] = "testing" - environ["AWS_SESSION_TOKEN"] = "testing" - - -def pytest_sessionfinish(session): - # pylint: disable=unused-argument - environ.pop("AWS_ACCESS_KEY_ID") - environ.pop("AWS_SECRET_ACCESS_KEY") - environ.pop("AWS_SECURITY_TOKEN") - environ.pop("AWS_SESSION_TOKEN") diff --git a/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py b/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py deleted file mode 100644 index cb45514c799..00000000000 --- a/instrumentation/opentelemetry-instrumentation-boto/tests/test_boto_instrumentation.py +++ /dev/null @@ -1,294 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest import skipUnless -from unittest.mock import Mock, patch - -import boto.awslambda -import boto.ec2 -import boto.elasticache -import boto.s3 -import boto.sts -from moto import ( # pylint: disable=import-error - mock_ec2_deprecated, - mock_lambda_deprecated, - mock_s3_deprecated, - mock_sts_deprecated, -) - -from opentelemetry.instrumentation.boto import BotoInstrumentor -from opentelemetry.sdk.resources import Resource -from opentelemetry.test.test_base import TestBase - - -def assert_span_http_status_code(span, code): - """Assert on the span's 'http.status_code' tag""" - tag = span.attributes["http.status_code"] - assert tag == code, "%r != %r" % (tag, code) - - -class TestBotoInstrumentor(TestBase): - """Botocore integration testsuite""" - - def setUp(self): - super().setUp() - BotoInstrumentor().instrument() - - def tearDown(self): - BotoInstrumentor().uninstrument() - - @mock_ec2_deprecated - def test_ec2_client(self): - ec2 = boto.ec2.connect_to_region("us-west-2") - - ec2.get_all_instances() - - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertEqual(span.attributes["aws.operation"], "DescribeInstances") - assert_span_http_status_code(span, 200) - self.assertEqual(span.attributes["http.method"], "POST") - self.assertEqual(span.attributes["aws.region"], "us-west-2") - - # Create an instance - ec2.run_instances(21) - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 2) - span = spans[1] - self.assertEqual(span.attributes["aws.operation"], "RunInstances") - assert_span_http_status_code(span, 200) - self.assertEqual( - span.resource, - Resource( - attributes={"endpoint": "ec2", "http_method": "runinstances"} - ), - ) - self.assertEqual(span.attributes["http.method"], "POST") - self.assertEqual(span.attributes["aws.region"], "us-west-2") - self.assertEqual(span.name, "ec2.command") - - @mock_ec2_deprecated - def test_not_recording(self): - mock_tracer = Mock() - mock_span = Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - with patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - ec2 = boto.ec2.connect_to_region("us-west-2") - ec2.get_all_instances() - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - @mock_ec2_deprecated - def test_analytics_enabled_with_rate(self): - ec2 = boto.ec2.connect_to_region("us-west-2") - - ec2.get_all_instances() - - spans = self.memory_exporter.get_finished_spans() - assert spans - - @mock_ec2_deprecated - def test_analytics_enabled_without_rate(self): - ec2 = boto.ec2.connect_to_region("us-west-2") - - ec2.get_all_instances() - - spans = self.memory_exporter.get_finished_spans() - assert spans - - @mock_s3_deprecated - def test_s3_client(self): - s3 = boto.s3.connect_to_region("us-east-1") - - s3.get_all_buckets() - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 1) - span = spans[0] - assert_span_http_status_code(span, 200) - self.assertEqual(span.attributes["http.method"], "GET") - self.assertEqual(span.attributes["aws.operation"], "get_all_buckets") - - # Create a bucket command - s3.create_bucket("cheese") - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 2) - span = spans[1] - assert_span_http_status_code(span, 200) - self.assertEqual(span.attributes["http.method"], "PUT") - self.assertEqual(span.attributes["path"], "/") - self.assertEqual(span.attributes["aws.operation"], "create_bucket") - - # Get the created bucket - s3.get_bucket("cheese") - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 3) - span = spans[2] - assert_span_http_status_code(span, 200) - self.assertEqual( - span.resource, - Resource(attributes={"endpoint": "s3", "http_method": "head"}), - ) - self.assertEqual(span.attributes["http.method"], "HEAD") - self.assertEqual(span.attributes["aws.operation"], "head_bucket") - self.assertEqual(span.name, "s3.command") - - # Checking for resource incase of error - try: - s3.get_bucket("big_bucket") - except Exception: # pylint: disable=broad-except - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[2] - self.assertEqual( - span.resource, - Resource(attributes={"endpoint": "s3", "http_method": "head"}), - ) - - @mock_s3_deprecated - def test_s3_put(self): - s3 = boto.s3.connect_to_region("us-east-1") - s3.create_bucket("mybucket") - bucket = s3.get_bucket("mybucket") - key = boto.s3.key.Key(bucket) - key.key = "foo" - key.set_contents_from_string("bar") - - spans = self.memory_exporter.get_finished_spans() - assert spans - # create bucket - self.assertEqual(len(spans), 3) - self.assertEqual(spans[0].attributes["aws.operation"], "create_bucket") - assert_span_http_status_code(spans[0], 200) - self.assertEqual( - spans[0].resource, - Resource(attributes={"endpoint": "s3", "http_method": "put"}), - ) - # get bucket - self.assertEqual(spans[1].attributes["aws.operation"], "head_bucket") - self.assertEqual( - spans[1].resource, - Resource(attributes={"endpoint": "s3", "http_method": "head"}), - ) - # put object - self.assertEqual( - spans[2].attributes["aws.operation"], "_send_file_internal" - ) - self.assertEqual( - spans[2].resource, - Resource(attributes={"endpoint": "s3", "http_method": "put"}), - ) - - @mock_lambda_deprecated - def test_unpatch(self): - - lamb = boto.awslambda.connect_to_region("us-east-2") - - BotoInstrumentor().uninstrument() - - # multiple calls - lamb.list_functions() - spans = self.memory_exporter.get_finished_spans() - assert not spans, spans - - @mock_s3_deprecated - def test_double_patch(self): - s3 = boto.s3.connect_to_region("us-east-1") - - BotoInstrumentor().instrument() - BotoInstrumentor().instrument() - - # Get the created bucket - s3.create_bucket("cheese") - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 1) - - @mock_lambda_deprecated - def test_lambda_client(self): - lamb = boto.awslambda.connect_to_region("us-east-2") - - # multiple calls - lamb.list_functions() - lamb.list_functions() - - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 2) - span = spans[0] - assert_span_http_status_code(span, 200) - self.assertEqual( - span.resource, - Resource(attributes={"endpoint": "lambda", "http_method": "get"}), - ) - self.assertEqual(span.attributes["http.method"], "GET") - self.assertEqual(span.attributes["aws.region"], "us-east-2") - self.assertEqual(span.attributes["aws.operation"], "list_functions") - - @mock_sts_deprecated - def test_sts_client(self): - sts = boto.sts.connect_to_region("us-west-2") - - sts.get_federation_token(12, duration=10) - - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[0] - self.assertEqual( - span.resource, - Resource( - attributes={ - "endpoint": "sts", - "http_method": "getfederationtoken", - } - ), - ) - self.assertEqual(span.attributes["aws.region"], "us-west-2") - self.assertEqual( - span.attributes["aws.operation"], "GetFederationToken" - ) - - # checking for protection on sts against security leak - self.assertTrue("args.path" not in span.attributes.keys()) - - @skipUnless( - False, - ( - "Test to reproduce the case where args sent to patched function " - "are None, can't be mocked: needs AWS credentials" - ), - ) - def test_elasticache_client(self): - elasticache = boto.elasticache.connect_to_region("us-west-2") - - elasticache.describe_cache_clusters() - - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[0] - self.assertEqual( - span.resource, Resource(attributes={"endpoint": "elasticcache"}) - ) - self.assertEqual(span.attributes["aws.region"], "us-west-2") diff --git a/instrumentation/opentelemetry-instrumentation-botocore/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-botocore/CHANGELOG.md deleted file mode 100644 index 9a01165b8f5..00000000000 --- a/instrumentation/opentelemetry-instrumentation-botocore/CHANGELOG.md +++ /dev/null @@ -1,30 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-botocore - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) - -## Version 0.11b0 - -Released 2020-07-28 - -- ext/boto and ext/botocore: fails to export spans via jaeger -([#866](https://github.com/open-telemetry/opentelemetry-python/pull/866)) - -## 0.9b0 - -Released 2020-06-10 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-botocore/LICENSE b/instrumentation/opentelemetry-instrumentation-botocore/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-botocore/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-botocore/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-botocore/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-botocore/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-botocore/README.rst b/instrumentation/opentelemetry-instrumentation-botocore/README.rst deleted file mode 100644 index 4f5eb9d9aff..00000000000 --- a/instrumentation/opentelemetry-instrumentation-botocore/README.rst +++ /dev/null @@ -1,23 +0,0 @@ -OpenTelemetry Botocore Tracing -============================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-botocore.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-botocore/ - -This library allows tracing requests made by the Botocore library. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-botocore - - -References ----------- - -* `OpenTelemetry Botocore Tracing `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-botocore/setup.cfg b/instrumentation/opentelemetry-instrumentation-botocore/setup.cfg deleted file mode 100644 index 6bd0190ac20..00000000000 --- a/instrumentation/opentelemetry-instrumentation-botocore/setup.cfg +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-botocore -description = OpenTelemetry Botocore instrumentation -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/master/instrumentation/opentelemetry-instrumentation-botocore -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - botocore ~= 1.0 - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - -[options.extras_require] -test = - moto ~= 1.0 - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - botocore = opentelemetry.instrumentation.botocore:BotocoreInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-botocore/setup.py b/instrumentation/opentelemetry-instrumentation-botocore/setup.py deleted file mode 100644 index fd5045efaa6..00000000000 --- a/instrumentation/opentelemetry-instrumentation-botocore/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "botocore", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py deleted file mode 100644 index b574b86cfb2..00000000000 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py +++ /dev/null @@ -1,222 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Instrument `Botocore`_ to trace service requests. - -There are two options for instrumenting code. The first option is to use the -``opentelemetry-instrument`` executable which will automatically -instrument your Botocore client. The second is to programmatically enable -instrumentation via the following code: - -.. _Botocore: https://pypi.org/project/botocore/ - -Usage ------ - -.. code:: python - - from opentelemetry import trace - from opentelemetry.instrumentation.botocore import BotocoreInstrumentor - from opentelemetry.sdk.trace import TracerProvider - import botocore - - trace.set_tracer_provider(TracerProvider()) - - # Instrument Botocore - BotocoreInstrumentor().instrument( - tracer_provider=trace.get_tracer_provider() - ) - - # This will create a span with Botocore-specific attributes - session = botocore.session.get_session() - session.set_credentials( - access_key="access-key", secret_key="secret-key" - ) - ec2 = self.session.create_client("ec2", region_name="us-west-2") - ec2.describe_instances() - -API ---- -""" - -import logging - -from botocore.client import BaseClient -from wrapt import ObjectProxy, wrap_function_wrapper - -from opentelemetry.instrumentation.botocore.version import __version__ -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.sdk.trace import Resource -from opentelemetry.trace import SpanKind, get_tracer - -logger = logging.getLogger(__name__) - - -class BotocoreInstrumentor(BaseInstrumentor): - """A instrumentor for Botocore - - See `BaseInstrumentor` - """ - - def _instrument(self, **kwargs): - - # FIXME should the tracer provider be accessed via Configuration - # instead? - # pylint: disable=attribute-defined-outside-init - self._tracer = get_tracer( - __name__, __version__, kwargs.get("tracer_provider") - ) - - wrap_function_wrapper( - "botocore.client", - "BaseClient._make_api_call", - self._patched_api_call, - ) - - def _uninstrument(self, **kwargs): - unwrap(BaseClient, "_make_api_call") - - def _patched_api_call(self, original_func, instance, args, kwargs): - - endpoint_name = deep_getattr(instance, "_endpoint._endpoint_prefix") - - with self._tracer.start_as_current_span( - "{}.command".format(endpoint_name), kind=SpanKind.CONSUMER, - ) as span: - - operation = None - if args and span.is_recording(): - operation = args[0] - span.resource = Resource( - attributes={ - "endpoint": endpoint_name, - "operation": operation.lower(), - } - ) - - else: - span.resource = Resource( - attributes={"endpoint": endpoint_name} - ) - - add_span_arg_tags( - span, - endpoint_name, - args, - ("action", "params", "path", "verb"), - {"params", "path", "verb"}, - ) - - if span.is_recording(): - region_name = deep_getattr(instance, "meta.region_name") - - meta = { - "aws.agent": "botocore", - "aws.operation": operation, - "aws.region": region_name, - } - for key, value in meta.items(): - span.set_attribute(key, value) - - result = original_func(*args, **kwargs) - - if span.is_recording(): - span.set_attribute( - "http.status_code", - result["ResponseMetadata"]["HTTPStatusCode"], - ) - span.set_attribute( - "retry_attempts", - result["ResponseMetadata"]["RetryAttempts"], - ) - - return result - - -def unwrap(obj, attr): - function = getattr(obj, attr, None) - if ( - function - and isinstance(function, ObjectProxy) - and hasattr(function, "__wrapped__") - ): - setattr(obj, attr, function.__wrapped__) - - -def add_span_arg_tags(span, endpoint_name, args, args_names, args_traced): - def truncate_arg_value(value, max_len=1024): - """Truncate values which are bytes and greater than `max_len`. - Useful for parameters like "Body" in `put_object` operations. - """ - if isinstance(value, bytes) and len(value) > max_len: - return b"..." - - return value - - def flatten_dict(dict_, sep=".", prefix=""): - """ - Returns a normalized dict of depth 1 with keys in order of embedding - """ - # adapted from https://stackoverflow.com/a/19647596 - return ( - { - prefix + sep + k if prefix else k: v - for kk, vv in dict_.items() - for k, v in flatten_dict(vv, sep, kk).items() - } - if isinstance(dict_, dict) - else {prefix: dict_} - ) - - if not span.is_recording(): - return - - if endpoint_name not in {"kms", "sts"}: - tags = dict( - (name, value) - for (name, value) in zip(args_names, args) - if name in args_traced - ) - tags = flatten_dict(tags) - for key, value in { - k: truncate_arg_value(v) - for k, v in tags.items() - if k not in {"s3": ["params.Body"]}.get(endpoint_name, []) - }.items(): - span.set_attribute(key, value) - - -def deep_getattr(obj, attr_string, default=None): - """ - Returns the attribute of ``obj`` at the dotted path given by - ``attr_string``, if no such attribute is reachable, returns ``default``. - - >>> deep_getattr(cass, "cluster") - >> deep_getattr(cass, "cluster.metadata.partitioner") - u"org.apache.cassandra.dht.Murmur3Partitioner" - - >>> deep_getattr(cass, "i.dont.exist", default="default") - "default" - """ - attrs = attr_string.split(".") - for attr in attrs: - try: - obj = getattr(obj, attr) - except AttributeError: - return default - - return obj diff --git a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/version.py b/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py deleted file mode 100644 index fba0182eec4..00000000000 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/test_botocore_instrumentation.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest.mock import Mock, patch - -import botocore.session -from botocore.exceptions import ParamValidationError -from moto import ( # pylint: disable=import-error - mock_ec2, - mock_kinesis, - mock_kms, - mock_lambda, - mock_s3, - mock_sqs, -) - -from opentelemetry.instrumentation.botocore import BotocoreInstrumentor -from opentelemetry.sdk.resources import Resource -from opentelemetry.test.test_base import TestBase - - -def assert_span_http_status_code(span, code): - """Assert on the span"s "http.status_code" tag""" - tag = span.attributes["http.status_code"] - assert tag == code, "%r != %r" % (tag, code) - - -class TestBotocoreInstrumentor(TestBase): - """Botocore integration testsuite""" - - def setUp(self): - super().setUp() - BotocoreInstrumentor().instrument() - - self.session = botocore.session.get_session() - self.session.set_credentials( - access_key="access-key", secret_key="secret-key" - ) - - def tearDown(self): - super().tearDown() - BotocoreInstrumentor().uninstrument() - - @mock_ec2 - def test_traced_client(self): - ec2 = self.session.create_client("ec2", region_name="us-west-2") - - ec2.describe_instances() - - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[0] - self.assertEqual(len(spans), 1) - self.assertEqual(span.attributes["aws.agent"], "botocore") - self.assertEqual(span.attributes["aws.region"], "us-west-2") - self.assertEqual(span.attributes["aws.operation"], "DescribeInstances") - assert_span_http_status_code(span, 200) - self.assertEqual( - span.resource, - Resource( - attributes={ - "endpoint": "ec2", - "operation": "describeinstances", - } - ), - ) - self.assertEqual(span.name, "ec2.command") - - @mock_ec2 - def test_not_recording(self): - mock_tracer = Mock() - mock_span = Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - with patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - ec2 = self.session.create_client("ec2", region_name="us-west-2") - ec2.describe_instances() - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - @mock_ec2 - def test_traced_client_analytics(self): - ec2 = self.session.create_client("ec2", region_name="us-west-2") - ec2.describe_instances() - - spans = self.memory_exporter.get_finished_spans() - assert spans - - @mock_s3 - def test_s3_client(self): - s3 = self.session.create_client("s3", region_name="us-west-2") - - s3.list_buckets() - s3.list_buckets() - - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[0] - self.assertEqual(len(spans), 2) - self.assertEqual(span.attributes["aws.operation"], "ListBuckets") - assert_span_http_status_code(span, 200) - self.assertEqual( - span.resource, - Resource( - attributes={"endpoint": "s3", "operation": "listbuckets"} - ), - ) - - # testing for span error - self.memory_exporter.get_finished_spans() - with self.assertRaises(ParamValidationError): - s3.list_objects(bucket="mybucket") - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[2] - self.assertEqual( - span.resource, - Resource( - attributes={"endpoint": "s3", "operation": "listobjects"} - ), - ) - - # Comment test for issue 1088 - @mock_s3 - def test_s3_put(self): - params = dict(Key="foo", Bucket="mybucket", Body=b"bar") - s3 = self.session.create_client("s3", region_name="us-west-2") - location = {"LocationConstraint": "us-west-2"} - s3.create_bucket(Bucket="mybucket", CreateBucketConfiguration=location) - s3.put_object(**params) - - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[0] - self.assertEqual(len(spans), 2) - self.assertEqual(span.attributes["aws.operation"], "CreateBucket") - assert_span_http_status_code(span, 200) - self.assertEqual( - span.resource, - Resource( - attributes={"endpoint": "s3", "operation": "createbucket"} - ), - ) - self.assertEqual(spans[1].attributes["aws.operation"], "PutObject") - self.assertEqual( - spans[1].resource, - Resource(attributes={"endpoint": "s3", "operation": "putobject"}), - ) - self.assertEqual(spans[1].attributes["params.Key"], str(params["Key"])) - self.assertEqual( - spans[1].attributes["params.Bucket"], str(params["Bucket"]) - ) - self.assertTrue("params.Body" not in spans[1].attributes.keys()) - - @mock_sqs - def test_sqs_client(self): - sqs = self.session.create_client("sqs", region_name="us-east-1") - - sqs.list_queues() - - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[0] - self.assertEqual(len(spans), 1) - self.assertEqual(span.attributes["aws.region"], "us-east-1") - self.assertEqual(span.attributes["aws.operation"], "ListQueues") - assert_span_http_status_code(span, 200) - self.assertEqual( - span.resource, - Resource( - attributes={"endpoint": "sqs", "operation": "listqueues"} - ), - ) - - @mock_kinesis - def test_kinesis_client(self): - kinesis = self.session.create_client( - "kinesis", region_name="us-east-1" - ) - - kinesis.list_streams() - - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[0] - self.assertEqual(len(spans), 1) - self.assertEqual(span.attributes["aws.region"], "us-east-1") - self.assertEqual(span.attributes["aws.operation"], "ListStreams") - assert_span_http_status_code(span, 200) - self.assertEqual( - span.resource, - Resource( - attributes={"endpoint": "kinesis", "operation": "liststreams"} - ), - ) - - @mock_kinesis - def test_unpatch(self): - kinesis = self.session.create_client( - "kinesis", region_name="us-east-1" - ) - - BotocoreInstrumentor().uninstrument() - - kinesis.list_streams() - spans = self.memory_exporter.get_finished_spans() - assert not spans, spans - - @mock_sqs - def test_double_patch(self): - sqs = self.session.create_client("sqs", region_name="us-east-1") - - BotocoreInstrumentor().instrument() - BotocoreInstrumentor().instrument() - - sqs.list_queues() - - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 1) - - @mock_lambda - def test_lambda_client(self): - lamb = self.session.create_client("lambda", region_name="us-east-1") - - lamb.list_functions() - - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[0] - self.assertEqual(len(spans), 1) - self.assertEqual(span.attributes["aws.region"], "us-east-1") - self.assertEqual(span.attributes["aws.operation"], "ListFunctions") - assert_span_http_status_code(span, 200) - self.assertEqual( - span.resource, - Resource( - attributes={"endpoint": "lambda", "operation": "listfunctions"} - ), - ) - - @mock_kms - def test_kms_client(self): - kms = self.session.create_client("kms", region_name="us-east-1") - - kms.list_keys(Limit=21) - - spans = self.memory_exporter.get_finished_spans() - assert spans - span = spans[0] - self.assertEqual(len(spans), 1) - self.assertEqual(span.attributes["aws.region"], "us-east-1") - self.assertEqual(span.attributes["aws.operation"], "ListKeys") - assert_span_http_status_code(span, 200) - self.assertEqual( - span.resource, - Resource(attributes={"endpoint": "kms", "operation": "listkeys"}), - ) - - # checking for protection on sts against security leak - self.assertTrue("params" not in span.attributes.keys()) diff --git a/instrumentation/opentelemetry-instrumentation-celery/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-celery/CHANGELOG.md deleted file mode 100644 index 8ebb8c3d240..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.14b0 - -Released 2020-10-13 - -- Span operation names now include the task type. ([#1135](https://github.com/open-telemetry/opentelemetry-python/pull/1135)) -- Added automatic context propagation. ([#1135](https://github.com/open-telemetry/opentelemetry-python/pull/1135)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-celery - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) - -## Version 0.10b0 - -Released 2020-06-23 - -- Add instrumentation for Celery ([#780](https://github.com/open-telemetry/opentelemetry-python/pull/780)) diff --git a/instrumentation/opentelemetry-instrumentation-celery/LICENSE b/instrumentation/opentelemetry-instrumentation-celery/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-celery/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-celery/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-celery/README.rst b/instrumentation/opentelemetry-instrumentation-celery/README.rst deleted file mode 100644 index 92e5a770a0a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/README.rst +++ /dev/null @@ -1,68 +0,0 @@ -OpenTelemetry Celery Instrumentation -==================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-celery.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-celery/ - -Instrumentation for Celery. - - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-celery - -Usage ------ - -* Start broker backend - -:: - docker run -p 5672:5672 rabbitmq - - -* Run instrumented task - -.. code-block:: python - - from opentelemetry import trace - from opentelemetry.sdk.trace import TracerProvider - from opentelemetry.sdk.trace.export import BatchExportSpanProcessor - from opentelemetry.instrumentation.celery import CeleryInstrumentor - - from celery import Celery - from celery.signals import worker_process_init - - @worker_process_init.connect(weak=False) - def init_celery_tracing(*args, **kwargs): - trace.set_tracer_provider(TracerProvider()) - span_processor = BatchExportSpanProcessor(ConsoleSpanExporter()) - trace.get_tracer_provider().add_span_processor(span_processor) - CeleryInstrumentor().instrument() - - app = Celery("tasks", broker="amqp://localhost") - - @app.task - def add(x, y): - return x + y - - add.delay(42, 50) - - -Setting up tracing --------------------- - -When tracing a celery worker process, tracing and instrumention both must be initialized after the celery worker -process is initialized. This is required for any tracing components that might use threading to work correctly -such as the BatchExportSpanProcessor. Celery provides a signal called ``worker_process_init`` that can be used to -accomplish this as shown in the example above. - -References ----------- -* `OpenTelemetry Celery Instrumentation `_ -* `OpenTelemetry Project `_ - diff --git a/instrumentation/opentelemetry-instrumentation-celery/setup.cfg b/instrumentation/opentelemetry-instrumentation-celery/setup.cfg deleted file mode 100644 index d1f8866a95d..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/setup.cfg +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-celery -description = OpenTelemetry Celery Instrumentation -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/master/instrumentation/opentelemetry-instrumentation-celery -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - celery ~= 4.0 - -[options.extras_require] -test = - pytest - celery ~= 4.0 - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - celery = opentelemetry.instrumentation.celery:CeleryInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-celery/setup.py b/instrumentation/opentelemetry-instrumentation-celery/setup.py deleted file mode 100644 index ca679306607..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "celery", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py b/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py deleted file mode 100644 index d225e6bd069..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py +++ /dev/null @@ -1,262 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Instrument `celery`_ to trace Celery applications. - -.. _celery: https://pypi.org/project/celery/ - -Usage ------ - -* Start broker backend - -.. code:: - - docker run -p 5672:5672 rabbitmq - - -* Run instrumented task - -.. code:: python - - from opentelemetry import trace - from opentelemetry.sdk.trace import TracerProvider - from opentelemetry.sdk.trace.export import BatchExportSpanProcessor - from opentelemetry.instrumentation.celery import CeleryInstrumentor - - from celery import Celery - from celery.signals import worker_process_init - - @worker_process_init.connect(weak=False) - def init_celery_tracing(*args, **kwargs): - trace.set_tracer_provider(TracerProvider()) - span_processor = BatchExportSpanProcessor(ConsoleSpanExporter()) - trace.get_tracer_provider().add_span_processor(span_processor) - CeleryInstrumentor().instrument() - - app = Celery("tasks", broker="amqp://localhost") - - @app.task - def add(x, y): - return x + y - - add.delay(42, 50) - -API ---- -""" - -import logging -import signal -from collections.abc import Iterable - -from celery import signals # pylint: disable=no-name-in-module - -from opentelemetry import propagators, trace -from opentelemetry.instrumentation.celery import utils -from opentelemetry.instrumentation.celery.version import __version__ -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.trace.propagation.textmap import DictGetter -from opentelemetry.trace.status import Status, StatusCode - -logger = logging.getLogger(__name__) - -# Task operations -_TASK_TAG_KEY = "celery.action" -_TASK_APPLY_ASYNC = "apply_async" -_TASK_RUN = "run" - -_TASK_RETRY_REASON_KEY = "celery.retry.reason" -_TASK_REVOKED_REASON_KEY = "celery.revoked.reason" -_TASK_REVOKED_TERMINATED_SIGNAL_KEY = "celery.terminated.signal" -_TASK_NAME_KEY = "celery.task_name" -_MESSAGE_ID_ATTRIBUTE_NAME = "messaging.message_id" - - -class CarrierGetter(DictGetter): - def get(self, carrier, key): - value = getattr(carrier, key, []) - if isinstance(value, str) or not isinstance(value, Iterable): - value = (value,) - return value - - def keys(self, carrier): - return [] - - -carrier_getter = CarrierGetter() - - -class CeleryInstrumentor(BaseInstrumentor): - def _instrument(self, **kwargs): - tracer_provider = kwargs.get("tracer_provider") - - # pylint: disable=attribute-defined-outside-init - self._tracer = trace.get_tracer(__name__, __version__, tracer_provider) - - signals.task_prerun.connect(self._trace_prerun, weak=False) - signals.task_postrun.connect(self._trace_postrun, weak=False) - signals.before_task_publish.connect( - self._trace_before_publish, weak=False - ) - signals.after_task_publish.connect( - self._trace_after_publish, weak=False - ) - signals.task_failure.connect(self._trace_failure, weak=False) - signals.task_retry.connect(self._trace_retry, weak=False) - - def _uninstrument(self, **kwargs): - signals.task_prerun.disconnect(self._trace_prerun) - signals.task_postrun.disconnect(self._trace_postrun) - signals.before_task_publish.disconnect(self._trace_before_publish) - signals.after_task_publish.disconnect(self._trace_after_publish) - signals.task_failure.disconnect(self._trace_failure) - signals.task_retry.disconnect(self._trace_retry) - - def _trace_prerun(self, *args, **kwargs): - task = utils.retrieve_task(kwargs) - task_id = utils.retrieve_task_id(kwargs) - - if task is None or task_id is None: - return - - request = task.request - tracectx = propagators.extract(carrier_getter, request) or None - - logger.debug("prerun signal start task_id=%s", task_id) - - operation_name = "{0}/{1}".format(_TASK_RUN, task.name) - span = self._tracer.start_span( - operation_name, context=tracectx, kind=trace.SpanKind.CONSUMER - ) - - activation = self._tracer.use_span(span, end_on_exit=True) - activation.__enter__() - utils.attach_span(task, task_id, (span, activation)) - - @staticmethod - def _trace_postrun(*args, **kwargs): - task = utils.retrieve_task(kwargs) - task_id = utils.retrieve_task_id(kwargs) - - if task is None or task_id is None: - return - - logger.debug("postrun signal task_id=%s", task_id) - - # retrieve and finish the Span - span, activation = utils.retrieve_span(task, task_id) - if span is None: - logger.warning("no existing span found for task_id=%s", task_id) - return - - # request context tags - if span.is_recording(): - span.set_attribute(_TASK_TAG_KEY, _TASK_RUN) - utils.set_attributes_from_context(span, kwargs) - utils.set_attributes_from_context(span, task.request) - span.set_attribute(_TASK_NAME_KEY, task.name) - - activation.__exit__(None, None, None) - utils.detach_span(task, task_id) - - def _trace_before_publish(self, *args, **kwargs): - task = utils.retrieve_task_from_sender(kwargs) - task_id = utils.retrieve_task_id_from_message(kwargs) - - if task is None or task_id is None: - return - - operation_name = "{0}/{1}".format(_TASK_APPLY_ASYNC, task.name) - span = self._tracer.start_span( - operation_name, kind=trace.SpanKind.PRODUCER - ) - - # apply some attributes here because most of the data is not available - if span.is_recording(): - span.set_attribute(_TASK_TAG_KEY, _TASK_APPLY_ASYNC) - span.set_attribute(_MESSAGE_ID_ATTRIBUTE_NAME, task_id) - span.set_attribute(_TASK_NAME_KEY, task.name) - utils.set_attributes_from_context(span, kwargs) - - activation = self._tracer.use_span(span, end_on_exit=True) - activation.__enter__() - utils.attach_span(task, task_id, (span, activation), is_publish=True) - - headers = kwargs.get("headers") - if headers: - propagators.inject(type(headers).__setitem__, headers) - - @staticmethod - def _trace_after_publish(*args, **kwargs): - task = utils.retrieve_task_from_sender(kwargs) - task_id = utils.retrieve_task_id_from_message(kwargs) - - if task is None or task_id is None: - return - - # retrieve and finish the Span - _, activation = utils.retrieve_span(task, task_id, is_publish=True) - if activation is None: - logger.warning("no existing span found for task_id=%s", task_id) - return - - activation.__exit__(None, None, None) - utils.detach_span(task, task_id, is_publish=True) - - @staticmethod - def _trace_failure(*args, **kwargs): - task = utils.retrieve_task_from_sender(kwargs) - task_id = utils.retrieve_task_id(kwargs) - - if task is None or task_id is None: - return - - # retrieve and pass exception info to activation - span, _ = utils.retrieve_span(task, task_id) - if span is None or not span.is_recording(): - return - - status_kwargs = {"status_code": StatusCode.ERROR} - - ex = kwargs.get("einfo") - - if ( - hasattr(task, "throws") - and ex is not None - and isinstance(ex.exception, task.throws) - ): - return - - if ex is not None: - status_kwargs["description"] = str(ex) - span.set_status(Status(**status_kwargs)) - - @staticmethod - def _trace_retry(*args, **kwargs): - task = utils.retrieve_task_from_sender(kwargs) - task_id = utils.retrieve_task_id_from_request(kwargs) - reason = utils.retrieve_reason(kwargs) - - if task is None or task_id is None or reason is None: - return - - span, _ = utils.retrieve_span(task, task_id) - if span is None or not span.is_recording(): - return - - # Add retry reason metadata to span - # Use `str(reason)` instead of `reason.message` in case we get - # something that isn't an `Exception` - span.set_attribute(_TASK_RETRY_REASON_KEY, str(reason)) diff --git a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/utils.py b/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/utils.py deleted file mode 100644 index 9a441345981..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/utils.py +++ /dev/null @@ -1,221 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging - -from celery import registry # pylint: disable=no-name-in-module - -logger = logging.getLogger(__name__) - -# Celery Context key -CTX_KEY = "__otel_task_span" - -# Celery Context attributes -CELERY_CONTEXT_ATTRIBUTES = ( - "compression", - "correlation_id", - "countdown", - "delivery_info", - "declare", - "eta", - "exchange", - "expires", - "hostname", - "id", - "priority", - "queue", - "reply_to", - "retries", - "routing_key", - "serializer", - "timelimit", - "origin", - "state", -) - - -# pylint:disable=too-many-branches -def set_attributes_from_context(span, context): - """Helper to extract meta values from a Celery Context""" - if not span.is_recording(): - return - for key in CELERY_CONTEXT_ATTRIBUTES: - value = context.get(key) - - # Skip this key if it is not set - if value is None or value == "": - continue - - # Skip `timelimit` if it is not set (it's default/unset value is a - # tuple or a list of `None` values - if key == "timelimit" and value in [(None, None), [None, None]]: - continue - - # Skip `retries` if it's value is `0` - if key == "retries" and value == 0: - continue - - attribute_name = None - - # Celery 4.0 uses `origin` instead of `hostname`; this change preserves - # the same name for the tag despite Celery version - if key == "origin": - key = "hostname" - - elif key == "delivery_info": - # Get also destination from this - routing_key = value.get("routing_key") - if routing_key is not None: - span.set_attribute("messaging.destination", routing_key) - value = str(value) - - elif key == "id": - attribute_name = "messaging.message_id" - - elif key == "correlation_id": - attribute_name = "messaging.conversation_id" - - elif key == "routing_key": - attribute_name = "messaging.destination" - - # according to https://docs.celeryproject.org/en/stable/userguide/routing.html#exchange-types - elif key == "declare": - attribute_name = "messaging.destination_kind" - for declare in value: - if declare.exchange.type == "direct": - value = "queue" - break - if declare.exchange.type == "topic": - value = "topic" - break - - # set attribute name if not set specially for a key - if attribute_name is None: - attribute_name = "celery.{}".format(key) - - span.set_attribute(attribute_name, value) - - -def attach_span(task, task_id, span, is_publish=False): - """Helper to propagate a `Span` for the given `Task` instance. This - function uses a `dict` that stores the Span using the - `(task_id, is_publish)` as a key. This is useful when information must be - propagated from one Celery signal to another. - - We use (task_id, is_publish) for the key to ensure that publishing a - task from within another task does not cause any conflicts. - - This mostly happens when either a task fails and a retry policy is in place, - or when a task is manually retries (e.g. `task.retry()`), we end up trying - to publish a task with the same id as the task currently running. - - Previously publishing the new task would overwrite the existing `celery.run` span - in the `dict` causing that span to be forgotten and never finished - NOTE: We cannot test for this well yet, because we do not run a celery worker, - and cannot run `task.apply_async()` - """ - span_dict = getattr(task, CTX_KEY, None) - if span_dict is None: - span_dict = dict() - setattr(task, CTX_KEY, span_dict) - - span_dict[(task_id, is_publish)] = span - - -def detach_span(task, task_id, is_publish=False): - """Helper to remove a `Span` in a Celery task when it's propagated. - This function handles tasks where the `Span` is not attached. - """ - span_dict = getattr(task, CTX_KEY, None) - if span_dict is None: - return - - # See note in `attach_span` for key info - span_dict.pop((task_id, is_publish), (None, None)) - - -def retrieve_span(task, task_id, is_publish=False): - """Helper to retrieve an active `Span` stored in a `Task` - instance - """ - span_dict = getattr(task, CTX_KEY, None) - if span_dict is None: - return (None, None) - - # See note in `attach_span` for key info - return span_dict.get((task_id, is_publish), (None, None)) - - -def retrieve_task(kwargs): - task = kwargs.get("task") - if task is None: - logger.debug("Unable to retrieve task from signal arguments") - return task - - -def retrieve_task_from_sender(kwargs): - sender = kwargs.get("sender") - if sender is None: - logger.debug("Unable to retrieve the sender from signal arguments") - - # before and after publish signals sender is the task name - # for retry and failure signals sender is the task object - if isinstance(sender, str): - sender = registry.tasks.get(sender) - if sender is None: - logger.debug("Unable to retrieve the task from sender=%s", sender) - - return sender - - -def retrieve_task_id(kwargs): - task_id = kwargs.get("task_id") - if task_id is None: - logger.debug("Unable to retrieve task_id from signal arguments") - return task_id - - -def retrieve_task_id_from_request(kwargs): - # retry signal does not include task_id as argument so use request argument - request = kwargs.get("request") - if request is None: - logger.debug("Unable to retrieve the request from signal arguments") - - task_id = getattr(request, "id") - if task_id is None: - logger.debug("Unable to retrieve the task_id from the request") - - return task_id - - -def retrieve_task_id_from_message(kwargs): - """Helper to retrieve the `Task` identifier from the message `body`. - This helper supports Protocol Version 1 and 2. The Protocol is well - detailed in the official documentation: - http://docs.celeryproject.org/en/latest/internals/protocol.html - """ - headers = kwargs.get("headers") - body = kwargs.get("body") - if headers is not None and len(headers) > 0: - # Protocol Version 2 (default from Celery 4.0) - return headers.get("id") - # Protocol Version 1 - return body.get("id") - - -def retrieve_reason(kwargs): - reason = kwargs.get("reason") - if not reason: - logger.debug("Unable to retrieve the retry reason") - return reason diff --git a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/version.py b/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-celery/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-celery/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-celery/tests/celery_test_tasks.py b/instrumentation/opentelemetry-instrumentation-celery/tests/celery_test_tasks.py deleted file mode 100644 index d9660412f04..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/tests/celery_test_tasks.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from celery import Celery - - -class Config: - result_backend = "rpc" - broker_backend = "memory" - - -app = Celery(broker="memory:///") -app.config_from_object(Config) - - -@app.task -def task_add(num_a, num_b): - return num_a + num_b diff --git a/instrumentation/opentelemetry-instrumentation-celery/tests/test_tasks.py b/instrumentation/opentelemetry-instrumentation-celery/tests/test_tasks.py deleted file mode 100644 index 3a05ebf331a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/tests/test_tasks.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import threading -import time - -from opentelemetry.instrumentation.celery import CeleryInstrumentor -from opentelemetry.test.test_base import TestBase -from opentelemetry.trace import SpanKind - -from .celery_test_tasks import app, task_add - - -class TestCeleryInstrumentation(TestBase): - def setUp(self): - super().setUp() - self._worker = app.Worker(app=app, pool="solo", concurrency=1) - self._thread = threading.Thread(target=self._worker.start) - self._thread.daemon = True - self._thread.start() - - def tearDown(self): - super().tearDown() - self._worker.stop() - self._thread.join() - - def test_task(self): - CeleryInstrumentor().instrument() - - result = task_add.delay(1, 2) - while not result.ready(): - time.sleep(0.05) - - spans = self.sorted_spans(self.memory_exporter.get_finished_spans()) - self.assertEqual(len(spans), 2) - - consumer, producer = spans - - self.assertEqual(consumer.name, "run/tests.celery_test_tasks.task_add") - self.assertEqual(consumer.kind, SpanKind.CONSUMER) - self.assert_span_has_attributes( - consumer, - { - "celery.action": "run", - "celery.state": "SUCCESS", - "messaging.destination": "celery", - "celery.task_name": "tests.celery_test_tasks.task_add", - }, - ) - - self.assertEqual( - producer.name, "apply_async/tests.celery_test_tasks.task_add" - ) - self.assertEqual(producer.kind, SpanKind.PRODUCER) - self.assert_span_has_attributes( - producer, - { - "celery.action": "apply_async", - "celery.task_name": "tests.celery_test_tasks.task_add", - "messaging.destination_kind": "queue", - "messaging.destination": "celery", - }, - ) - - self.assertNotEqual(consumer.parent, producer.context) - self.assertEqual(consumer.parent.span_id, producer.context.span_id) - self.assertEqual(consumer.context.trace_id, producer.context.trace_id) diff --git a/instrumentation/opentelemetry-instrumentation-celery/tests/test_utils.py b/instrumentation/opentelemetry-instrumentation-celery/tests/test_utils.py deleted file mode 100644 index f48f06aafbe..00000000000 --- a/instrumentation/opentelemetry-instrumentation-celery/tests/test_utils.py +++ /dev/null @@ -1,232 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest -from unittest import mock - -from celery import Celery - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.celery import utils -from opentelemetry.sdk import trace - - -class TestUtils(unittest.TestCase): - def setUp(self): - self.app = Celery("celery.test_app") - - def test_set_attributes_from_context(self): - # it should extract only relevant keys - context = { - "correlation_id": "44b7f305", - "delivery_info": {"eager": True}, - "eta": "soon", - "expires": "later", - "hostname": "localhost", - "id": "44b7f305", - "reply_to": "44b7f305", - "retries": 4, - "timelimit": ("now", "later"), - "custom_meta": "custom_value", - "routing_key": "celery", - } - - span = trace._Span("name", mock.Mock(spec=trace_api.SpanContext)) - utils.set_attributes_from_context(span, context) - - self.assertEqual( - span.attributes.get("messaging.message_id"), "44b7f305" - ) - self.assertEqual( - span.attributes.get("messaging.conversation_id"), "44b7f305" - ) - self.assertEqual( - span.attributes.get("messaging.destination"), "celery" - ) - - self.assertEqual( - span.attributes["celery.delivery_info"], str({"eager": True}) - ) - self.assertEqual(span.attributes.get("celery.eta"), "soon") - self.assertEqual(span.attributes.get("celery.expires"), "later") - self.assertEqual(span.attributes.get("celery.hostname"), "localhost") - - self.assertEqual(span.attributes.get("celery.reply_to"), "44b7f305") - self.assertEqual(span.attributes.get("celery.retries"), 4) - self.assertEqual( - span.attributes.get("celery.timelimit"), ("now", "later") - ) - self.assertNotIn("custom_meta", span.attributes) - - def test_set_attributes_not_recording(self): - # it should extract only relevant keys - context = { - "correlation_id": "44b7f305", - "delivery_info": {"eager": True}, - "eta": "soon", - "expires": "later", - "hostname": "localhost", - "id": "44b7f305", - "reply_to": "44b7f305", - "retries": 4, - "timelimit": ("now", "later"), - "custom_meta": "custom_value", - "routing_key": "celery", - } - - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - utils.set_attributes_from_context(mock_span, context) - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_set_attributes_from_context_empty_keys(self): - # it should not extract empty keys - context = { - "correlation_id": None, - "exchange": "", - "timelimit": (None, None), - "retries": 0, - } - - span = trace._Span("name", mock.Mock(spec=trace_api.SpanContext)) - utils.set_attributes_from_context(span, context) - - self.assertEqual(len(span.attributes), 0) - # edge case: `timelimit` can also be a list of None values - context = { - "timelimit": [None, None], - } - - utils.set_attributes_from_context(span, context) - - self.assertEqual(len(span.attributes), 0) - - def test_span_propagation(self): - # ensure spans getter and setter works properly - @self.app.task - def fn_task(): - return 42 - - # propagate and retrieve a Span - task_id = "7c6731af-9533-40c3-83a9-25b58f0d837f" - span = trace._Span("name", mock.Mock(spec=trace_api.SpanContext)) - utils.attach_span(fn_task, task_id, span) - span_after = utils.retrieve_span(fn_task, task_id) - self.assertIs(span, span_after) - - def test_span_delete(self): - # ensure the helper removes properly a propagated Span - @self.app.task - def fn_task(): - return 42 - - # propagate a Span - task_id = "7c6731af-9533-40c3-83a9-25b58f0d837f" - span = trace._Span("name", mock.Mock(spec=trace_api.SpanContext)) - utils.attach_span(fn_task, task_id, span) - # delete the Span - utils.detach_span(fn_task, task_id) - self.assertEqual(utils.retrieve_span(fn_task, task_id), (None, None)) - - def test_span_delete_empty(self): - # ensure detach_span doesn't raise an exception if span is not present - @self.app.task - def fn_task(): - return 42 - - # delete the Span - task_id = "7c6731af-9533-40c3-83a9-25b58f0d837f" - try: - utils.detach_span(fn_task, task_id) - self.assertEqual( - utils.retrieve_span(fn_task, task_id), (None, None) - ) - except Exception as ex: # pylint: disable=broad-except - self.fail("Exception was raised: %s" % ex) - - def test_task_id_from_protocol_v1(self): - # ensures a `task_id` is properly returned when Protocol v1 is used. - # `context` is an example of an emitted Signal with Protocol v1 - context = { - "body": { - "expires": None, - "utc": True, - "args": ["user"], - "chord": None, - "callbacks": None, - "errbacks": None, - "taskset": None, - "id": "dffcaec1-dd92-4a1a-b3ab-d6512f4beeb7", - "retries": 0, - "task": "tests.contrib.celery.test_integration.fn_task_parameters", - "timelimit": (None, None), - "eta": None, - "kwargs": {"force_logout": True}, - }, - "sender": "tests.contrib.celery.test_integration.fn_task_parameters", - "exchange": "celery", - "routing_key": "celery", - "retry_policy": None, - "headers": {}, - "properties": {}, - } - - task_id = utils.retrieve_task_id_from_message(context) - self.assertEqual(task_id, "dffcaec1-dd92-4a1a-b3ab-d6512f4beeb7") - - def test_task_id_from_protocol_v2(self): - # ensures a `task_id` is properly returned when Protocol v2 is used. - # `context` is an example of an emitted Signal with Protocol v2 - context = { - "body": ( - ["user"], - {"force_logout": True}, - { - u"chord": None, - u"callbacks": None, - u"errbacks": None, - u"chain": None, - }, - ), - "sender": u"tests.contrib.celery.test_integration.fn_task_parameters", - "exchange": u"", - "routing_key": u"celery", - "retry_policy": None, - "headers": { - u"origin": u"gen83744@hostname", - u"root_id": "7e917b83-4018-431d-9832-73a28e1fb6c0", - u"expires": None, - u"shadow": None, - u"id": "7e917b83-4018-431d-9832-73a28e1fb6c0", - u"kwargsrepr": u"{'force_logout': True}", - u"lang": u"py", - u"retries": 0, - u"task": u"tests.contrib.celery.test_integration.fn_task_parameters", - u"group": None, - u"timelimit": [None, None], - u"parent_id": None, - u"argsrepr": u"['user']", - u"eta": None, - }, - "properties": { - u"reply_to": "c3054a07-5b28-3855-b18c-1623a24aaeca", - u"correlation_id": "7e917b83-4018-431d-9832-73a28e1fb6c0", - }, - } - - task_id = utils.retrieve_task_id_from_message(context) - self.assertEqual(task_id, "7e917b83-4018-431d-9832-73a28e1fb6c0") diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-dbapi/CHANGELOG.md deleted file mode 100644 index e110055da1a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-dbapi/CHANGELOG.md +++ /dev/null @@ -1,31 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- bugfix: cursors and connections now produce spans when used with context managers - ([#1028](https://github.com/open-telemetry/opentelemetry-python/pull/1028)) -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-dbapi - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) - -## 0.7b1 - -Released 2020-05-12 - -- Implement instrument_connection and uninstrument_connection ([#624](https://github.com/open-telemetry/opentelemetry-python/pull/624)) - -## 0.4a0 - -Released 2020-02-21 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/LICENSE b/instrumentation/opentelemetry-instrumentation-dbapi/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-dbapi/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-dbapi/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-dbapi/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-dbapi/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-dbapi/README.rst b/instrumentation/opentelemetry-instrumentation-dbapi/README.rst deleted file mode 100644 index 5137a1c1ff7..00000000000 --- a/instrumentation/opentelemetry-instrumentation-dbapi/README.rst +++ /dev/null @@ -1,21 +0,0 @@ -OpenTelemetry Database API instrumentation -========================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-dbapi.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-dbapi/ - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-dbapi - - -References ----------- - -* `OpenTelemetry Database API Instrumentation `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/setup.cfg b/instrumentation/opentelemetry-instrumentation-dbapi/setup.cfg deleted file mode 100644 index 7c4daa40299..00000000000 --- a/instrumentation/opentelemetry-instrumentation-dbapi/setup.cfg +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-dbapi -description = OpenTelemetry Database API instrumentation -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/master/instrumentation/opentelemetry-instrumentation-dbapi -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - wrapt >= 1.0.0, < 2.0.0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/setup.py b/instrumentation/opentelemetry-instrumentation-dbapi/setup.py deleted file mode 100644 index cfe98f38952..00000000000 --- a/instrumentation/opentelemetry-instrumentation-dbapi/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "dbapi", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py deleted file mode 100644 index 0047ab18517..00000000000 --- a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py +++ /dev/null @@ -1,385 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -The trace integration with Database API supports libraries that follow the -Python Database API Specification v2.0. -``_ - -Usage ------ - -.. code-block:: python - - import mysql.connector - import pyodbc - - from opentelemetry import trace - from opentelemetry.instrumentation.dbapi import trace_integration - from opentelemetry.trace import TracerProvider - - trace.set_tracer_provider(TracerProvider()) - - # Ex: mysql.connector - trace_integration(mysql.connector, "connect", "mysql", "sql") - # Ex: pyodbc - trace_integration(pyodbc, "Connection", "odbc", "sql") - -API ---- -""" - -import functools -import logging -import typing - -import wrapt - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.dbapi.version import __version__ -from opentelemetry.instrumentation.utils import unwrap -from opentelemetry.trace import SpanKind, TracerProvider, get_tracer -from opentelemetry.trace.status import Status, StatusCode - -logger = logging.getLogger(__name__) - - -def trace_integration( - connect_module: typing.Callable[..., typing.Any], - connect_method_name: str, - database_component: str, - database_type: str = "", - connection_attributes: typing.Dict = None, - tracer_provider: typing.Optional[TracerProvider] = None, -): - """Integrate with DB API library. - https://www.python.org/dev/peps/pep-0249/ - - Args: - connect_module: Module name where connect method is available. - connect_method_name: The connect method name. - database_component: Database driver name or database name "JDBI", - "jdbc", "odbc", "postgreSQL". - database_type: The Database type. For any SQL database, "sql". - connection_attributes: Attribute names for database, port, host and - user in Connection object. - tracer_provider: The :class:`opentelemetry.trace.TracerProvider` to - use. If ommited the current configured one is used. - """ - wrap_connect( - __name__, - connect_module, - connect_method_name, - database_component, - database_type, - connection_attributes, - version=__version__, - tracer_provider=tracer_provider, - ) - - -def wrap_connect( - name: str, - connect_module: typing.Callable[..., typing.Any], - connect_method_name: str, - database_component: str, - database_type: str = "", - connection_attributes: typing.Dict = None, - version: str = "", - tracer_provider: typing.Optional[TracerProvider] = None, -): - """Integrate with DB API library. - https://www.python.org/dev/peps/pep-0249/ - - Args: - tracer: The :class:`opentelemetry.trace.Tracer` to use. - connect_module: Module name where connect method is available. - connect_method_name: The connect method name. - database_component: Database driver name or database name "JDBI", - "jdbc", "odbc", "postgreSQL". - database_type: The Database type. For any SQL database, "sql". - connection_attributes: Attribute names for database, port, host and - user in Connection object. - """ - - # pylint: disable=unused-argument - def wrap_connect_( - wrapped: typing.Callable[..., typing.Any], - instance: typing.Any, - args: typing.Tuple[typing.Any, typing.Any], - kwargs: typing.Dict[typing.Any, typing.Any], - ): - db_integration = DatabaseApiIntegration( - name, - database_component, - database_type=database_type, - connection_attributes=connection_attributes, - version=version, - tracer_provider=tracer_provider, - ) - return db_integration.wrapped_connection(wrapped, args, kwargs) - - try: - wrapt.wrap_function_wrapper( - connect_module, connect_method_name, wrap_connect_ - ) - except Exception as ex: # pylint: disable=broad-except - logger.warning("Failed to integrate with DB API. %s", str(ex)) - - -def unwrap_connect( - connect_module: typing.Callable[..., typing.Any], connect_method_name: str, -): - """Disable integration with DB API library. - https://www.python.org/dev/peps/pep-0249/ - - Args: - connect_module: Module name where the connect method is available. - connect_method_name: The connect method name. - """ - unwrap(connect_module, connect_method_name) - - -def instrument_connection( - name: str, - connection, - database_component: str, - database_type: str = "", - connection_attributes: typing.Dict = None, - version: str = "", - tracer_provider: typing.Optional[TracerProvider] = None, -): - """Enable instrumentation in a database connection. - - Args: - tracer: The :class:`opentelemetry.trace.Tracer` to use. - connection: The connection to instrument. - database_component: Database driver name or database name "JDBI", - "jdbc", "odbc", "postgreSQL". - database_type: The Database type. For any SQL database, "sql". - connection_attributes: Attribute names for database, port, host and - user in a connection object. - - Returns: - An instrumented connection. - """ - db_integration = DatabaseApiIntegration( - name, - database_component, - database_type, - connection_attributes=connection_attributes, - version=version, - tracer_provider=tracer_provider, - ) - db_integration.get_connection_attributes(connection) - return get_traced_connection_proxy(connection, db_integration) - - -def uninstrument_connection(connection): - """Disable instrumentation in a database connection. - - Args: - connection: The connection to uninstrument. - - Returns: - An uninstrumented connection. - """ - if isinstance(connection, wrapt.ObjectProxy): - return connection.__wrapped__ - - logger.warning("Connection is not instrumented") - return connection - - -class DatabaseApiIntegration: - def __init__( - self, - name: str, - database_component: str, - database_type: str = "sql", - connection_attributes=None, - version: str = "", - tracer_provider: typing.Optional[TracerProvider] = None, - ): - self.connection_attributes = connection_attributes - if self.connection_attributes is None: - self.connection_attributes = { - "database": "database", - "port": "port", - "host": "host", - "user": "user", - } - self._name = name - self._version = version - self._tracer_provider = tracer_provider - self.database_component = database_component - self.database_type = database_type - self.connection_props = {} - self.span_attributes = {} - self.name = "" - self.database = "" - - def get_tracer(self): - return get_tracer( - self._name, - instrumenting_library_version=self._version, - tracer_provider=self._tracer_provider, - ) - - def wrapped_connection( - self, - connect_method: typing.Callable[..., typing.Any], - args: typing.Tuple[typing.Any, typing.Any], - kwargs: typing.Dict[typing.Any, typing.Any], - ): - """Add object proxy to connection object. - """ - connection = connect_method(*args, **kwargs) - self.get_connection_attributes(connection) - return get_traced_connection_proxy(connection, self) - - def get_connection_attributes(self, connection): - # Populate span fields using connection - for key, value in self.connection_attributes.items(): - # Allow attributes nested in connection object - attribute = functools.reduce( - lambda attribute, attribute_value: getattr( - attribute, attribute_value, None - ), - value.split("."), - connection, - ) - if attribute: - self.connection_props[key] = attribute - self.name = self.database_component - self.database = self.connection_props.get("database", "") - if self.database: - # PyMySQL encodes names with utf-8 - if hasattr(self.database, "decode"): - self.database = self.database.decode(errors="ignore") - self.name += "." + self.database - user = self.connection_props.get("user") - if user is not None: - self.span_attributes["db.user"] = str(user) - host = self.connection_props.get("host") - if host is not None: - self.span_attributes["net.peer.name"] = host - port = self.connection_props.get("port") - if port is not None: - self.span_attributes["net.peer.port"] = port - - -def get_traced_connection_proxy( - connection, db_api_integration, *args, **kwargs -): - # pylint: disable=abstract-method - class TracedConnectionProxy(wrapt.ObjectProxy): - # pylint: disable=unused-argument - def __init__(self, connection, *args, **kwargs): - wrapt.ObjectProxy.__init__(self, connection) - - def cursor(self, *args, **kwargs): - return get_traced_cursor_proxy( - self.__wrapped__.cursor(*args, **kwargs), db_api_integration - ) - - def __enter__(self): - self.__wrapped__.__enter__() - return self - - def __exit__(self, *args, **kwargs): - self.__wrapped__.__exit__(*args, **kwargs) - - return TracedConnectionProxy(connection, *args, **kwargs) - - -class TracedCursor: - def __init__(self, db_api_integration: DatabaseApiIntegration): - self._db_api_integration = db_api_integration - - def _populate_span( - self, span: trace_api.Span, *args: typing.Tuple[typing.Any, typing.Any] - ): - if not span.is_recording(): - return - statement = args[0] if args else "" - span.set_attribute( - "component", self._db_api_integration.database_component - ) - span.set_attribute("db.type", self._db_api_integration.database_type) - span.set_attribute("db.instance", self._db_api_integration.database) - span.set_attribute("db.statement", statement) - - for ( - attribute_key, - attribute_value, - ) in self._db_api_integration.span_attributes.items(): - span.set_attribute(attribute_key, attribute_value) - - if len(args) > 1: - span.set_attribute("db.statement.parameters", str(args[1])) - - def traced_execution( - self, - query_method: typing.Callable[..., typing.Any], - *args: typing.Tuple[typing.Any, typing.Any], - **kwargs: typing.Dict[typing.Any, typing.Any] - ): - - with self._db_api_integration.get_tracer().start_as_current_span( - self._db_api_integration.name, kind=SpanKind.CLIENT - ) as span: - self._populate_span(span, *args) - try: - result = query_method(*args, **kwargs) - return result - except Exception as ex: # pylint: disable=broad-except - if span.is_recording(): - span.set_status(Status(StatusCode.ERROR, str(ex))) - raise ex - - -def get_traced_cursor_proxy(cursor, db_api_integration, *args, **kwargs): - _traced_cursor = TracedCursor(db_api_integration) - - # pylint: disable=abstract-method - class TracedCursorProxy(wrapt.ObjectProxy): - - # pylint: disable=unused-argument - def __init__(self, cursor, *args, **kwargs): - wrapt.ObjectProxy.__init__(self, cursor) - - def execute(self, *args, **kwargs): - return _traced_cursor.traced_execution( - self.__wrapped__.execute, *args, **kwargs - ) - - def executemany(self, *args, **kwargs): - return _traced_cursor.traced_execution( - self.__wrapped__.executemany, *args, **kwargs - ) - - def callproc(self, *args, **kwargs): - return _traced_cursor.traced_execution( - self.__wrapped__.callproc, *args, **kwargs - ) - - def __enter__(self): - self.__wrapped__.__enter__() - return self - - def __exit__(self, *args, **kwargs): - self.__wrapped__.__exit__(*args, **kwargs) - - return TracedCursorProxy(cursor, *args, **kwargs) diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/version.py b/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-dbapi/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py b/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py deleted file mode 100644 index f2abb8b6dca..00000000000 --- a/instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py +++ /dev/null @@ -1,229 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import logging -from unittest import mock - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation import dbapi -from opentelemetry.test.test_base import TestBase - - -class TestDBApiIntegration(TestBase): - def setUp(self): - super().setUp() - self.tracer = self.tracer_provider.get_tracer(__name__) - - def test_span_succeeded(self): - connection_props = { - "database": "testdatabase", - "server_host": "testhost", - "server_port": 123, - "user": "testuser", - } - connection_attributes = { - "database": "database", - "port": "server_port", - "host": "server_host", - "user": "user", - } - db_integration = dbapi.DatabaseApiIntegration( - self.tracer, "testcomponent", "testtype", connection_attributes - ) - mock_connection = db_integration.wrapped_connection( - mock_connect, {}, connection_props - ) - cursor = mock_connection.cursor() - cursor.execute("Test query", ("param1Value", False)) - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertEqual(span.name, "testcomponent.testdatabase") - self.assertIs(span.kind, trace_api.SpanKind.CLIENT) - - self.assertEqual(span.attributes["component"], "testcomponent") - self.assertEqual(span.attributes["db.type"], "testtype") - self.assertEqual(span.attributes["db.instance"], "testdatabase") - self.assertEqual(span.attributes["db.statement"], "Test query") - self.assertEqual( - span.attributes["db.statement.parameters"], - "('param1Value', False)", - ) - self.assertEqual(span.attributes["db.user"], "testuser") - self.assertEqual(span.attributes["net.peer.name"], "testhost") - self.assertEqual(span.attributes["net.peer.port"], 123) - self.assertIs( - span.status.status_code, trace_api.status.StatusCode.UNSET, - ) - - def test_span_not_recording(self): - connection_props = { - "database": "testdatabase", - "server_host": "testhost", - "server_port": 123, - "user": "testuser", - } - connection_attributes = { - "database": "database", - "port": "server_port", - "host": "server_host", - "user": "user", - } - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - db_integration = dbapi.DatabaseApiIntegration( - mock_tracer, "testcomponent", "testtype", connection_attributes - ) - mock_connection = db_integration.wrapped_connection( - mock_connect, {}, connection_props - ) - cursor = mock_connection.cursor() - cursor.execute("Test query", ("param1Value", False)) - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_span_failed(self): - db_integration = dbapi.DatabaseApiIntegration( - self.tracer, "testcomponent" - ) - mock_connection = db_integration.wrapped_connection( - mock_connect, {}, {} - ) - cursor = mock_connection.cursor() - with self.assertRaises(Exception): - cursor.execute("Test query", throw_exception=True) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertEqual(span.attributes["db.statement"], "Test query") - self.assertIs( - span.status.status_code, trace_api.status.StatusCode.ERROR, - ) - self.assertEqual(span.status.description, "Test Exception") - - def test_executemany(self): - db_integration = dbapi.DatabaseApiIntegration( - self.tracer, "testcomponent" - ) - mock_connection = db_integration.wrapped_connection( - mock_connect, {}, {} - ) - cursor = mock_connection.cursor() - cursor.executemany("Test query") - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertEqual(span.attributes["db.statement"], "Test query") - - def test_callproc(self): - db_integration = dbapi.DatabaseApiIntegration( - self.tracer, "testcomponent" - ) - mock_connection = db_integration.wrapped_connection( - mock_connect, {}, {} - ) - cursor = mock_connection.cursor() - cursor.callproc("Test stored procedure") - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertEqual( - span.attributes["db.statement"], "Test stored procedure" - ) - - @mock.patch("opentelemetry.instrumentation.dbapi") - def test_wrap_connect(self, mock_dbapi): - dbapi.wrap_connect(self.tracer, mock_dbapi, "connect", "-") - connection = mock_dbapi.connect() - self.assertEqual(mock_dbapi.connect.call_count, 1) - self.assertIsInstance(connection.__wrapped__, mock.Mock) - - @mock.patch("opentelemetry.instrumentation.dbapi") - def test_unwrap_connect(self, mock_dbapi): - dbapi.wrap_connect(self.tracer, mock_dbapi, "connect", "-") - connection = mock_dbapi.connect() - self.assertEqual(mock_dbapi.connect.call_count, 1) - - dbapi.unwrap_connect(mock_dbapi, "connect") - connection = mock_dbapi.connect() - self.assertEqual(mock_dbapi.connect.call_count, 2) - self.assertIsInstance(connection, mock.Mock) - - def test_instrument_connection(self): - connection = mock.Mock() - # Avoid get_attributes failing because can't concatenate mock - connection.database = "-" - connection2 = dbapi.instrument_connection(self.tracer, connection, "-") - self.assertIs(connection2.__wrapped__, connection) - - def test_uninstrument_connection(self): - connection = mock.Mock() - # Set connection.database to avoid a failure because mock can't - # be concatenated - connection.database = "-" - connection2 = dbapi.instrument_connection(self.tracer, connection, "-") - self.assertIs(connection2.__wrapped__, connection) - - connection3 = dbapi.uninstrument_connection(connection2) - self.assertIs(connection3, connection) - - with self.assertLogs(level=logging.WARNING): - connection4 = dbapi.uninstrument_connection(connection) - self.assertIs(connection4, connection) - - -# pylint: disable=unused-argument -def mock_connect(*args, **kwargs): - database = kwargs.get("database") - server_host = kwargs.get("server_host") - server_port = kwargs.get("server_port") - user = kwargs.get("user") - return MockConnection(database, server_port, server_host, user) - - -class MockConnection: - def __init__(self, database, server_port, server_host, user): - self.database = database - self.server_port = server_port - self.server_host = server_host - self.user = user - - # pylint: disable=no-self-use - def cursor(self): - return MockCursor() - - -class MockCursor: - # pylint: disable=unused-argument, no-self-use - def execute(self, query, params=None, throw_exception=False): - if throw_exception: - raise Exception("Test Exception") - - # pylint: disable=unused-argument, no-self-use - def executemany(self, query, params=None, throw_exception=False): - if throw_exception: - raise Exception("Test Exception") - - # pylint: disable=unused-argument, no-self-use - def callproc(self, query, params=None, throw_exception=False): - if throw_exception: - raise Exception("Test Exception") diff --git a/instrumentation/opentelemetry-instrumentation-django/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-django/CHANGELOG.md deleted file mode 100644 index 5876936a0b5..00000000000 --- a/instrumentation/opentelemetry-instrumentation-django/CHANGELOG.md +++ /dev/null @@ -1,60 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.15b0 - -Released 2020-11-02 - -- Django instrumentation is now enabled by default but can be disabled by setting `OTEL_PYTHON_DJANGO_INSTRUMENT` to `False` - ([#1239](https://github.com/open-telemetry/opentelemetry-python/pull/1239)) -- Bugfix use request.path replace request.get_full_path(). It will get correct span name - ([#1309](https://github.com/open-telemetry/opentelemetry-python/pull/1309#)) -- Record span status and http.status_code attribute on exception - ([#1257](https://github.com/open-telemetry/opentelemetry-python/pull/1257)) -- Added capture of http.route - ([#1226](https://github.com/open-telemetry/opentelemetry-python/issues/1226)) -- Add support for tracking http metrics - ([#1230](https://github.com/open-telemetry/opentelemetry-python/pull/1230)) - -## Version 0.14b0 - -Released 2020-10-13 - -- Changed span name extraction from request to comply semantic convention ([#992](https://github.com/open-telemetry/opentelemetry-python/pull/992)) -- Added support for `OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS` ([#1154](https://github.com/open-telemetry/opentelemetry-python/pull/1154)) - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-django - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) -- Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) - -## Version 0.11b0 - -Released 2020-07-28 - -- Use one general exclude list instead of two ([#872](https://github.com/open-telemetry/opentelemetry-python/pull/872)) - -## 0.8b0 - -Released 2020-05-27 - -- Add exclude list for paths and hosts to prevent from tracing - ([#670](https://github.com/open-telemetry/opentelemetry-python/pull/670)) -- Add support for django >= 1.10 (#717) - -## 0.7b1 - -Released 2020-05-12 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-django/LICENSE b/instrumentation/opentelemetry-instrumentation-django/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-django/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-django/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-django/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-django/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-django/README.rst b/instrumentation/opentelemetry-instrumentation-django/README.rst deleted file mode 100644 index a2b98cabf40..00000000000 --- a/instrumentation/opentelemetry-instrumentation-django/README.rst +++ /dev/null @@ -1,53 +0,0 @@ -OpenTelemetry Django Tracing -============================ - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-django.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-django/ - -This library allows tracing requests for Django applications. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-django - -Configuration -------------- - -Exclude lists -************* -To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_DJANGO_EXCLUDED_URLS`` with comma delimited regexes representing which URLs to exclude. - -For example, - -:: - - export OTEL_PYTHON_DJANGO_EXCLUDED_URLS="client/.*/info,healthcheck" - -will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``. - -Request attributes -******************** -To extract certain attributes from Django's request object and use them as span attributes, set the environment variable ``OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS`` to a comma -delimited list of request attribute names. - -For example, - -:: - - export OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS='path_info,content_type' - -will extract path_info and content_type attributes from every traced request and add them as span attritbues. - -Django Request object reference: https://docs.djangoproject.com/en/3.1/ref/request-response/#attributes - -References ----------- - -* `Django `_ -* `OpenTelemetry Instrumentation for Django `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-django/setup.cfg b/instrumentation/opentelemetry-instrumentation-django/setup.cfg deleted file mode 100644 index 44a921283d2..00000000000 --- a/instrumentation/opentelemetry-instrumentation-django/setup.cfg +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-django -description = OpenTelemetry Instrumentation for Django -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/master/instrumentation/opentelemetry-instrumentation-django -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - django >= 1.10 - opentelemetry-instrumentation-wsgi == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - opentelemetry-api == 0.16.dev0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - django = opentelemetry.instrumentation.django:DjangoInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-django/setup.py b/instrumentation/opentelemetry-instrumentation-django/setup.py deleted file mode 100644 index fb9d615ce30..00000000000 --- a/instrumentation/opentelemetry-instrumentation-django/setup.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from os.path import dirname, join - -from setuptools import setup - -PACKAGE_INFO = {} -with open( - join( - dirname(__file__), - "src", - "opentelemetry", - "instrumentation", - "django", - "version.py", - ) -) as f: - exec(f.read(), PACKAGE_INFO) - -setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/__init__.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/__init__.py deleted file mode 100644 index 26e21a1f7fa..00000000000 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/__init__.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from logging import getLogger - -from django.conf import settings - -from opentelemetry.configuration import Configuration -from opentelemetry.instrumentation.django.middleware import _DjangoMiddleware -from opentelemetry.instrumentation.django.version import __version__ -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.metric import ( - HTTPMetricRecorder, - HTTPMetricType, - MetricMixin, -) - -_logger = getLogger(__name__) - - -class DjangoInstrumentor(BaseInstrumentor, MetricMixin): - """An instrumentor for Django - - See `BaseInstrumentor` - """ - - _opentelemetry_middleware = ".".join( - [_DjangoMiddleware.__module__, _DjangoMiddleware.__qualname__] - ) - - def _instrument(self, **kwargs): - - # FIXME this is probably a pattern that will show up in the rest of the - # ext. Find a better way of implementing this. - # FIXME Probably the evaluation of strings into boolean values can be - # built inside the Configuration class itself with the magic method - # __bool__ - - if Configuration().DJANGO_INSTRUMENT is False: - return - - # This can not be solved, but is an inherent problem of this approach: - # the order of middleware entries matters, and here you have no control - # on that: - # https://docs.djangoproject.com/en/3.0/topics/http/middleware/#activating-middleware - # https://docs.djangoproject.com/en/3.0/ref/middleware/#middleware-ordering - - settings_middleware = getattr(settings, "MIDDLEWARE", []) - # Django allows to specify middlewares as a tuple, so we convert this tuple to a - # list, otherwise we wouldn't be able to call append/remove - if isinstance(settings_middleware, tuple): - settings_middleware = list(settings_middleware) - - settings_middleware.insert(0, self._opentelemetry_middleware) - self.init_metrics( - __name__, __version__, - ) - metric_recorder = HTTPMetricRecorder(self.meter, HTTPMetricType.SERVER) - setattr(settings, "OTEL_METRIC_RECORDER", metric_recorder) - setattr(settings, "MIDDLEWARE", settings_middleware) - - def _uninstrument(self, **kwargs): - settings_middleware = getattr(settings, "MIDDLEWARE", None) - - # FIXME This is starting to smell like trouble. We have 2 mechanisms - # that may make this condition be True, one implemented in - # BaseInstrumentor and another one implemented in _instrument. Both - # stop _instrument from running and thus, settings_middleware not being - # set. - if settings_middleware is None or ( - self._opentelemetry_middleware not in settings_middleware - ): - return - - settings_middleware.remove(self._opentelemetry_middleware) - setattr(settings, "MIDDLEWARE", settings_middleware) diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py deleted file mode 100644 index 1f465ca57a7..00000000000 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py +++ /dev/null @@ -1,232 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import time -from logging import getLogger - -from django.conf import settings - -from opentelemetry.configuration import Configuration -from opentelemetry.context import attach, detach -from opentelemetry.instrumentation.django.version import __version__ -from opentelemetry.instrumentation.utils import extract_attributes_from_object -from opentelemetry.instrumentation.wsgi import ( - add_response_attributes, - carrier_getter, - collect_request_attributes, -) -from opentelemetry.propagators import extract -from opentelemetry.trace import SpanKind, get_tracer -from opentelemetry.util import ExcludeList - -try: - from django.core.urlresolvers import ( # pylint: disable=no-name-in-module - resolve, - Resolver404, - ) -except ImportError: - from django.urls import resolve, Resolver404 - -try: - from django.utils.deprecation import MiddlewareMixin -except ImportError: - MiddlewareMixin = object - -_logger = getLogger(__name__) -_attributes_by_preference = [ - ["http.scheme", "http.host", "http.target"], - ["http.scheme", "http.server_name", "net.host.port", "http.target"], - ["http.scheme", "net.host.name", "net.host.port", "http.target"], - ["http.url"], -] - - -class _DjangoMiddleware(MiddlewareMixin): - """Django Middleware for OpenTelemetry""" - - _environ_activation_key = ( - "opentelemetry-instrumentor-django.activation_key" - ) - _environ_token = "opentelemetry-instrumentor-django.token" - _environ_span_key = "opentelemetry-instrumentor-django.span_key" - _environ_exception_key = "opentelemetry-instrumentor-django.exception_key" - - _excluded_urls = Configuration().DJANGO_EXCLUDED_URLS or [] - if _excluded_urls: - _excluded_urls = ExcludeList(str.split(_excluded_urls, ",")) - else: - _excluded_urls = ExcludeList(_excluded_urls) - - _traced_request_attrs = [ - attr.strip() - for attr in (Configuration().DJANGO_TRACED_REQUEST_ATTRS or "").split( - "," - ) - ] - - @staticmethod - def _get_span_name(request): - try: - if getattr(request, "resolver_match"): - match = request.resolver_match - else: - match = resolve(request.path) - - if hasattr(match, "route"): - return match.route - - # Instead of using `view_name`, better to use `_func_name` as some applications can use similar - # view names in different modules - if hasattr(match, "_func_name"): - return match._func_name # pylint: disable=protected-access - - # Fallback for safety as `_func_name` private field - return match.view_name - - except Resolver404: - return "HTTP {}".format(request.method) - - @staticmethod - def _get_metric_labels_from_attributes(attributes): - labels = {} - labels["http.method"] = attributes.get("http.method", "") - for attrs in _attributes_by_preference: - labels_from_attributes = { - attr: attributes.get(attr, None) for attr in attrs - } - if set(attrs).issubset(attributes.keys()): - labels.update(labels_from_attributes) - break - if attributes.get("http.flavor"): - labels["http.flavor"] = attributes.get("http.flavor") - return labels - - def process_request(self, request): - # request.META is a dictionary containing all available HTTP headers - # Read more about request.META here: - # https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.META - - if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): - return - - # pylint:disable=W0212 - request._otel_start_time = time.time() - - environ = request.META - - token = attach(extract(carrier_getter, environ)) - - tracer = get_tracer(__name__, __version__) - - span = tracer.start_span( - self._get_span_name(request), - kind=SpanKind.SERVER, - start_time=environ.get( - "opentelemetry-instrumentor-django.starttime_key" - ), - ) - - attributes = collect_request_attributes(environ) - # pylint:disable=W0212 - request._otel_labels = self._get_metric_labels_from_attributes( - attributes - ) - - if span.is_recording(): - attributes = extract_attributes_from_object( - request, self._traced_request_attrs, attributes - ) - for key, value in attributes.items(): - span.set_attribute(key, value) - - activation = tracer.use_span(span, end_on_exit=True) - activation.__enter__() - - request.META[self._environ_activation_key] = activation - request.META[self._environ_span_key] = span - request.META[self._environ_token] = token - - # pylint: disable=unused-argument - def process_view(self, request, view_func, *args, **kwargs): - # Process view is executed before the view function, here we get the - # route template from request.resolver_match. It is not set yet in process_request - if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): - return - - if ( - self._environ_activation_key in request.META.keys() - and self._environ_span_key in request.META.keys() - ): - span = request.META[self._environ_span_key] - - if span.is_recording(): - match = getattr(request, "resolver_match") - if match: - route = getattr(match, "route") - if route: - span.set_attribute("http.route", route) - - def process_exception(self, request, exception): - if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): - return - - if self._environ_activation_key in request.META.keys(): - request.META[self._environ_exception_key] = exception - - def process_response(self, request, response): - if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): - return response - - if ( - self._environ_activation_key in request.META.keys() - and self._environ_span_key in request.META.keys() - ): - add_response_attributes( - request.META[self._environ_span_key], - "{} {}".format(response.status_code, response.reason_phrase), - response, - ) - # pylint:disable=W0212 - request._otel_labels["http.status_code"] = str( - response.status_code - ) - request.META.pop(self._environ_span_key) - - exception = request.META.pop(self._environ_exception_key, None) - if exception: - request.META[self._environ_activation_key].__exit__( - type(exception), - exception, - getattr(exception, "__traceback__", None), - ) - else: - request.META[self._environ_activation_key].__exit__( - None, None, None - ) - request.META.pop(self._environ_activation_key) - - if self._environ_token in request.META.keys(): - detach(request.environ.get(self._environ_token)) - request.META.pop(self._environ_token) - - try: - metric_recorder = getattr(settings, "OTEL_METRIC_RECORDER", None) - if metric_recorder is not None: - # pylint:disable=W0212 - metric_recorder.record_server_duration_range( - request._otel_start_time, time.time(), request._otel_labels - ) - except Exception as ex: # pylint: disable=W0703 - _logger.warning("Error recording duration metrics: %s", ex) - return response diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/version.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-django/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-django/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py deleted file mode 100644 index 3f70f62bec0..00000000000 --- a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py +++ /dev/null @@ -1,315 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from sys import modules -from unittest.mock import Mock, patch - -from django import VERSION -from django.conf import settings -from django.conf.urls import url -from django.test import Client -from django.test.utils import setup_test_environment, teardown_test_environment - -from opentelemetry.configuration import Configuration -from opentelemetry.instrumentation.django import DjangoInstrumentor -from opentelemetry.sdk.util import get_dict_as_key -from opentelemetry.test.test_base import TestBase -from opentelemetry.test.wsgitestutil import WsgiTestBase -from opentelemetry.trace import SpanKind -from opentelemetry.trace.status import StatusCode -from opentelemetry.util import ExcludeList - -# pylint: disable=import-error -from .views import ( - error, - excluded, - excluded_noarg, - excluded_noarg2, - route_span_name, - traced, - traced_template, -) - -DJANGO_2_2 = VERSION >= (2, 2) - -urlpatterns = [ - url(r"^traced/", traced), - url(r"^route/(?P[0-9]{4})/template/$", traced_template), - url(r"^error/", error), - url(r"^excluded_arg/", excluded), - url(r"^excluded_noarg/", excluded_noarg), - url(r"^excluded_noarg2/", excluded_noarg2), - url(r"^span_name/([0-9]{4})/$", route_span_name), -] -_django_instrumentor = DjangoInstrumentor() - - -class TestMiddleware(TestBase, WsgiTestBase): - @classmethod - def setUpClass(cls): - super().setUpClass() - settings.configure(ROOT_URLCONF=modules[__name__]) - - def setUp(self): - super().setUp() - setup_test_environment() - _django_instrumentor.instrument() - Configuration._reset() # pylint: disable=protected-access - - def tearDown(self): - super().tearDown() - teardown_test_environment() - _django_instrumentor.uninstrument() - - def test_templated_route_get(self): - Client().get("/route/2020/template/") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - - span = spans[0] - - self.assertEqual( - span.name, - "^route/(?P[0-9]{4})/template/$" - if DJANGO_2_2 - else "tests.views.traced", - ) - self.assertEqual(span.kind, SpanKind.SERVER) - self.assertEqual(span.status.status_code, StatusCode.UNSET) - self.assertEqual(span.attributes["http.method"], "GET") - self.assertEqual( - span.attributes["http.url"], - "http://testserver/route/2020/template/", - ) - self.assertEqual( - span.attributes["http.route"], - "^route/(?P[0-9]{4})/template/$", - ) - self.assertEqual(span.attributes["http.scheme"], "http") - self.assertEqual(span.attributes["http.status_code"], 200) - self.assertEqual(span.attributes["http.status_text"], "OK") - - def test_traced_get(self): - Client().get("/traced/") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - - span = spans[0] - - self.assertEqual( - span.name, "^traced/" if DJANGO_2_2 else "tests.views.traced" - ) - self.assertEqual(span.kind, SpanKind.SERVER) - self.assertEqual(span.status.status_code, StatusCode.UNSET) - self.assertEqual(span.attributes["http.method"], "GET") - self.assertEqual( - span.attributes["http.url"], "http://testserver/traced/" - ) - self.assertEqual(span.attributes["http.route"], "^traced/") - self.assertEqual(span.attributes["http.scheme"], "http") - self.assertEqual(span.attributes["http.status_code"], 200) - self.assertEqual(span.attributes["http.status_text"], "OK") - - self.assertIsNotNone(_django_instrumentor.meter) - self.assertEqual(len(_django_instrumentor.meter.metrics), 1) - recorder = _django_instrumentor.meter.metrics.pop() - match_key = get_dict_as_key( - { - "http.flavor": "1.1", - "http.method": "GET", - "http.status_code": "200", - "http.url": "http://testserver/traced/", - } - ) - for key in recorder.bound_instruments.keys(): - self.assertEqual(key, match_key) - # pylint: disable=protected-access - bound = recorder.bound_instruments.get(key) - for view_data in bound.view_datas: - self.assertEqual(view_data.labels, key) - self.assertEqual(view_data.aggregator.current.count, 1) - self.assertGreaterEqual(view_data.aggregator.current.sum, 0) - - def test_not_recording(self): - mock_tracer = Mock() - mock_span = Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - with patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - Client().get("/traced/") - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_traced_post(self): - Client().post("/traced/") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - - span = spans[0] - - self.assertEqual( - span.name, "^traced/" if DJANGO_2_2 else "tests.views.traced" - ) - self.assertEqual(span.kind, SpanKind.SERVER) - self.assertEqual(span.status.status_code, StatusCode.UNSET) - self.assertEqual(span.attributes["http.method"], "POST") - self.assertEqual( - span.attributes["http.url"], "http://testserver/traced/" - ) - self.assertEqual(span.attributes["http.route"], "^traced/") - self.assertEqual(span.attributes["http.scheme"], "http") - self.assertEqual(span.attributes["http.status_code"], 200) - self.assertEqual(span.attributes["http.status_text"], "OK") - - def test_error(self): - with self.assertRaises(ValueError): - Client().get("/error/") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - - span = spans[0] - - self.assertEqual( - span.name, "^error/" if DJANGO_2_2 else "tests.views.error" - ) - self.assertEqual(span.kind, SpanKind.SERVER) - self.assertEqual(span.status.status_code, StatusCode.ERROR) - self.assertEqual(span.attributes["http.method"], "GET") - self.assertEqual( - span.attributes["http.url"], "http://testserver/error/" - ) - self.assertEqual(span.attributes["http.route"], "^error/") - self.assertEqual(span.attributes["http.scheme"], "http") - self.assertEqual(span.attributes["http.status_code"], 500) - self.assertIsNotNone(_django_instrumentor.meter) - self.assertEqual(len(_django_instrumentor.meter.metrics), 1) - - self.assertEqual(len(span.events), 1) - event = span.events[0] - self.assertEqual(event.name, "exception") - self.assertEqual(event.attributes["exception.type"], "ValueError") - self.assertEqual(event.attributes["exception.message"], "error") - - recorder = _django_instrumentor.meter.metrics.pop() - match_key = get_dict_as_key( - { - "http.flavor": "1.1", - "http.method": "GET", - "http.status_code": "500", - "http.url": "http://testserver/error/", - } - ) - for key in recorder.bound_instruments.keys(): - self.assertEqual(key, match_key) - # pylint: disable=protected-access - bound = recorder.bound_instruments.get(key) - for view_data in bound.view_datas: - self.assertEqual(view_data.labels, key) - self.assertEqual(view_data.aggregator.current.count, 1) - - @patch( - "opentelemetry.instrumentation.django.middleware._DjangoMiddleware._excluded_urls", - ExcludeList(["http://testserver/excluded_arg/123", "excluded_noarg"]), - ) - def test_exclude_lists(self): - client = Client() - client.get("/excluded_arg/123") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 0) - - client.get("/excluded_arg/125") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - client.get("/excluded_noarg/") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - client.get("/excluded_noarg2/") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - def test_span_name(self): - # test no query_string - Client().get("/span_name/1234/") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - span = span_list[0] - self.assertEqual( - span.name, - "^span_name/([0-9]{4})/$" - if DJANGO_2_2 - else "tests.views.route_span_name", - ) - - def test_span_name_for_query_string(self): - """ - request not have query string - """ - Client().get("/span_name/1234/?query=test") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - span = span_list[0] - self.assertEqual( - span.name, - "^span_name/([0-9]{4})/$" - if DJANGO_2_2 - else "tests.views.route_span_name", - ) - - def test_span_name_404(self): - Client().get("/span_name/1234567890/") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - span = span_list[0] - self.assertEqual(span.name, "HTTP GET") - - def test_traced_request_attrs(self): - with patch( - "opentelemetry.instrumentation.django.middleware._DjangoMiddleware._traced_request_attrs", - [], - ): - Client().get("/span_name/1234/", CONTENT_TYPE="test/ct") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - span = span_list[0] - self.assertNotIn("path_info", span.attributes) - self.assertNotIn("content_type", span.attributes) - self.memory_exporter.clear() - - with patch( - "opentelemetry.instrumentation.django.middleware._DjangoMiddleware._traced_request_attrs", - ["path_info", "content_type", "non_existing_variable"], - ): - Client().get("/span_name/1234/", CONTENT_TYPE="test/ct") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - span = span_list[0] - self.assertEqual(span.attributes["path_info"], "/span_name/1234/") - self.assertEqual(span.attributes["content_type"], "test/ct") - self.assertNotIn("non_existing_variable", span.attributes) diff --git a/instrumentation/opentelemetry-instrumentation-django/tests/views.py b/instrumentation/opentelemetry-instrumentation-django/tests/views.py deleted file mode 100644 index 872222a8422..00000000000 --- a/instrumentation/opentelemetry-instrumentation-django/tests/views.py +++ /dev/null @@ -1,31 +0,0 @@ -from django.http import HttpResponse - - -def traced(request): # pylint: disable=unused-argument - return HttpResponse() - - -def traced_template(request, year): # pylint: disable=unused-argument - return HttpResponse() - - -def error(request): # pylint: disable=unused-argument - raise ValueError("error") - - -def excluded(request): # pylint: disable=unused-argument - return HttpResponse() - - -def excluded_noarg(request): # pylint: disable=unused-argument - return HttpResponse() - - -def excluded_noarg2(request): # pylint: disable=unused-argument - return HttpResponse() - - -def route_span_name( - request, *args, **kwargs -): # pylint: disable=unused-argument - return HttpResponse() diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-elasticsearch/CHANGELOG.md deleted file mode 100644 index f07f5140233..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/CHANGELOG.md +++ /dev/null @@ -1,24 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) -- Change package name to opentelemetry-instrumentation-elasticsearch - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) - -## Version 0.10b0 - -Released 2020-06-23 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/LICENSE b/instrumentation/opentelemetry-instrumentation-elasticsearch/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-elasticsearch/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-elasticsearch/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-elasticsearch/README.rst b/instrumentation/opentelemetry-instrumentation-elasticsearch/README.rst deleted file mode 100644 index 9f898e78358..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/README.rst +++ /dev/null @@ -1,23 +0,0 @@ -OpenTelemetry elasticsearch Integration -======================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-elasticsearch.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-elasticsearch/ - -This library allows tracing elasticsearch made by the -`elasticsearch `_ library. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-elasticsearch - -References ----------- - -* `OpenTelemetry elasticsearch Integration `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/setup.cfg b/instrumentation/opentelemetry-instrumentation-elasticsearch/setup.cfg deleted file mode 100644 index b1ebcfe76ad..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/setup.cfg +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-elasticsearch -description = OpenTelemetry elasticsearch instrumentation -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/master/instrumentation/opentelemetry-instrumentation-elasticsearch -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - wrapt >= 1.0.0, < 2.0.0 - elasticsearch >= 2.0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - elasticsearch-dsl >= 2.0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - elasticsearch = opentelemetry.instrumentation.elasticsearch:ElasticsearchInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/setup.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/setup.py deleted file mode 100644 index cd7a7f10129..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "elasticsearch", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py deleted file mode 100644 index 541cdbfa6e3..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -This library allows tracing HTTP elasticsearch made by the -`elasticsearch `_ library. - -Usage ------ - -.. code-block:: python - - from opentelemetry import trace - from opentelemetry.instrumentation.elasticsearch import ElasticSearchInstrumentor - from opentelemetry.sdk.trace import TracerProvider - import elasticsearch - - trace.set_tracer_provider(TracerProvider()) - - # instrument elasticsearch - ElasticSearchInstrumentor().instrument(tracer_provider=trace.get_tracer_provider()) - - # Using elasticsearch as normal now will automatically generate spans - es = elasticsearch.Elasticsearch() - es.index(index='my-index', doc_type='my-type', id=1, body={'my': 'data', 'timestamp': datetime.now()}) - es.get(index='my-index', doc_type='my-type', id=1) - -API ---- - -Elasticsearch instrumentation prefixes operation names with the string "Elasticsearch". This -can be changed to a different string by either setting the `OTEL_PYTHON_ELASTICSEARCH_NAME_PREFIX` -environment variable or by passing the prefix as an argument to the instrumentor. For example, - - -.. code-block:: python - - ElasticSearchInstrumentor("my-custom-prefix").instrument() -""" - -import functools -import types -from logging import getLogger -from os import environ - -import elasticsearch -import elasticsearch.exceptions -from wrapt import ObjectProxy -from wrapt import wrap_function_wrapper as _wrap - -from opentelemetry import context, propagators, trace -from opentelemetry.instrumentation.elasticsearch.version import __version__ -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.utils import unwrap -from opentelemetry.trace import SpanKind, get_tracer -from opentelemetry.trace.status import Status, StatusCode - -logger = getLogger(__name__) - - -# Values to add as tags from the actual -# payload returned by Elasticsearch, if any. -_ATTRIBUTES_FROM_RESULT = [ - "found", - "timed_out", - "took", -] - -_DEFALT_OP_NAME = "request" - - -class ElasticsearchInstrumentor(BaseInstrumentor): - """An instrumentor for elasticsearch - See `BaseInstrumentor` - """ - - def __init__(self, span_name_prefix=None): - if not span_name_prefix: - span_name_prefix = environ.get( - "OTEL_PYTHON_ELASTICSEARCH_NAME_PREFIX", "Elasticsearch", - ) - self._span_name_prefix = span_name_prefix.strip() - super().__init__() - - def _instrument(self, **kwargs): - """ - Instruments elasticsarch module - """ - tracer_provider = kwargs.get("tracer_provider") - tracer = get_tracer(__name__, __version__, tracer_provider) - _wrap( - elasticsearch, - "Transport.perform_request", - _wrap_perform_request(tracer, self._span_name_prefix), - ) - - def _uninstrument(self, **kwargs): - unwrap(elasticsearch.Transport, "perform_request") - - -def _wrap_perform_request(tracer, span_name_prefix): - # pylint: disable=R0912 - def wrapper(wrapped, _, args, kwargs): - method = url = None - try: - method, url, *_ = args - except IndexError: - logger.warning( - "expected perform_request to receive two positional arguments. " - "Got %d", - len(args), - ) - - op_name = span_name_prefix + (url or method or _DEFALT_OP_NAME) - params = kwargs.get("params", {}) - body = kwargs.get("body", None) - - with tracer.start_as_current_span( - op_name, kind=SpanKind.CLIENT, - ) as span: - if span.is_recording(): - attributes = { - "component": "elasticsearch-py", - "db.type": "elasticsearch", - } - if url: - attributes["elasticsearch.url"] = url - if method: - attributes["elasticsearch.method"] = method - if body: - attributes["db.statement"] = str(body) - if params: - attributes["elasticsearch.params"] = str(params) - for key, value in attributes.items(): - span.set_attribute(key, value) - try: - rv = wrapped(*args, **kwargs) - if isinstance(rv, dict) and span.is_recording(): - for member in _ATTRIBUTES_FROM_RESULT: - if member in rv: - span.set_attribute( - "elasticsearch.{0}".format(member), - str(rv[member]), - ) - return rv - except Exception as ex: # pylint: disable=broad-except - if span.is_recording(): - span.set_status(Status(StatusCode.ERROR, str(ex))) - raise ex - - return wrapper diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/version.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es2.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es2.py deleted file mode 100644 index 008a95d6716..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es2.py +++ /dev/null @@ -1,33 +0,0 @@ -from elasticsearch_dsl import ( # pylint: disable=no-name-in-module - DocType, - String, -) - - -class Article(DocType): - title = String(analyzer="snowball", fields={"raw": String()}) - body = String(analyzer="snowball") - - class Meta: - index = "test-index" - - -dsl_create_statement = { - "mappings": { - "article": { - "properties": { - "title": { - "analyzer": "snowball", - "fields": {"raw": {"type": "string"}}, - "type": "string", - }, - "body": {"analyzer": "snowball", "type": "string"}, - } - } - }, - "settings": {"analysis": {}}, -} -dsl_index_result = (1, {}, '{"created": true}') -dsl_index_span_name = "Elasticsearch/test-index/article/2" -dsl_index_url = "/test-index/article/2" -dsl_search_method = "GET" diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es5.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es5.py deleted file mode 100644 index cf32d988635..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es5.py +++ /dev/null @@ -1,33 +0,0 @@ -from elasticsearch_dsl import ( # pylint: disable=no-name-in-module - DocType, - Keyword, - Text, -) - - -class Article(DocType): - title = Text(analyzer="snowball", fields={"raw": Keyword()}) - body = Text(analyzer="snowball") - - class Meta: - index = "test-index" - - -dsl_create_statement = { - "mappings": { - "article": { - "properties": { - "title": { - "analyzer": "snowball", - "fields": {"raw": {"type": "keyword"}}, - "type": "text", - }, - "body": {"analyzer": "snowball", "type": "text"}, - } - } - }, -} -dsl_index_result = (1, {}, '{"created": true}') -dsl_index_span_name = "Elasticsearch/test-index/article/2" -dsl_index_url = "/test-index/article/2" -dsl_search_method = "GET" diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es6.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es6.py deleted file mode 100644 index b27d291ba3f..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es6.py +++ /dev/null @@ -1,33 +0,0 @@ -from elasticsearch_dsl import ( # pylint: disable=unused-import - Document, - Keyword, - Text, -) - - -class Article(Document): - title = Text(analyzer="snowball", fields={"raw": Keyword()}) - body = Text(analyzer="snowball") - - class Index: - name = "test-index" - - -dsl_create_statement = { - "mappings": { - "doc": { - "properties": { - "title": { - "analyzer": "snowball", - "fields": {"raw": {"type": "keyword"}}, - "type": "text", - }, - "body": {"analyzer": "snowball", "type": "text"}, - } - } - } -} -dsl_index_result = (1, {}, '{"result": "created"}') -dsl_index_span_name = "Elasticsearch/test-index/doc/2" -dsl_index_url = "/test-index/doc/2" -dsl_search_method = "GET" diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es7.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es7.py deleted file mode 100644 index a2d37a54a9a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/helpers_es7.py +++ /dev/null @@ -1,31 +0,0 @@ -from elasticsearch_dsl import ( # pylint: disable=unused-import - Document, - Keyword, - Text, -) - - -class Article(Document): - title = Text(analyzer="snowball", fields={"raw": Keyword()}) - body = Text(analyzer="snowball") - - class Index: - name = "test-index" - - -dsl_create_statement = { - "mappings": { - "properties": { - "title": { - "analyzer": "snowball", - "fields": {"raw": {"type": "keyword"}}, - "type": "text", - }, - "body": {"analyzer": "snowball", "type": "text"}, - } - } -} -dsl_index_result = (1, {}, '{"result": "created"}') -dsl_index_span_name = "Elasticsearch/test-index/_doc/2" -dsl_index_url = "/test-index/_doc/2" -dsl_search_method = "POST" diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py deleted file mode 100644 index ea0e6ce2fbf..00000000000 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py +++ /dev/null @@ -1,327 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import threading -from ast import literal_eval -from unittest import mock - -import elasticsearch -import elasticsearch.exceptions -from elasticsearch import Elasticsearch -from elasticsearch_dsl import Search - -import opentelemetry.instrumentation.elasticsearch -from opentelemetry.instrumentation.elasticsearch import ( - ElasticsearchInstrumentor, -) -from opentelemetry.test.test_base import TestBase -from opentelemetry.trace.status import StatusCode - -major_version = elasticsearch.VERSION[0] - -if major_version == 7: - from . import helpers_es7 as helpers # pylint: disable=no-name-in-module -elif major_version == 6: - from . import helpers_es6 as helpers # pylint: disable=no-name-in-module -elif major_version == 5: - from . import helpers_es5 as helpers # pylint: disable=no-name-in-module -else: - from . import helpers_es2 as helpers # pylint: disable=no-name-in-module - - -Article = helpers.Article - - -@mock.patch( - "elasticsearch.connection.http_urllib3.Urllib3HttpConnection.perform_request" -) -class TestElasticsearchIntegration(TestBase): - def setUp(self): - super().setUp() - self.tracer = self.tracer_provider.get_tracer(__name__) - ElasticsearchInstrumentor().instrument() - - def tearDown(self): - super().tearDown() - with self.disable_logging(): - ElasticsearchInstrumentor().uninstrument() - - def get_ordered_finished_spans(self): - return sorted( - self.memory_exporter.get_finished_spans(), - key=lambda s: s.start_time, - ) - - def test_instrumentor(self, request_mock): - request_mock.return_value = (1, {}, {}) - - es = Elasticsearch() - es.index(index="sw", doc_type="people", id=1, body={"name": "adam"}) - - spans_list = self.get_ordered_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - # Check version and name in span's instrumentation info - # self.check_span_instrumentation_info(span, opentelemetry.instrumentation.elasticsearch) - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.elasticsearch - ) - - # check that no spans are generated after uninstrument - ElasticsearchInstrumentor().uninstrument() - - es.index(index="sw", doc_type="people", id=1, body={"name": "adam"}) - - spans_list = self.get_ordered_finished_spans() - self.assertEqual(len(spans_list), 1) - - def test_span_not_recording(self, request_mock): - request_mock.return_value = (1, {}, {}) - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = mock_span - with mock.patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - Elasticsearch() - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - ElasticsearchInstrumentor().uninstrument() - - def test_prefix_arg(self, request_mock): - prefix = "prefix-from-env" - ElasticsearchInstrumentor().uninstrument() - ElasticsearchInstrumentor(span_name_prefix=prefix).instrument() - request_mock.return_value = (1, {}, {}) - self._test_prefix(prefix) - - def test_prefix_env(self, request_mock): - prefix = "prefix-from-args" - env_var = "OTEL_PYTHON_ELASTICSEARCH_NAME_PREFIX" - os.environ[env_var] = prefix - ElasticsearchInstrumentor().uninstrument() - ElasticsearchInstrumentor().instrument() - request_mock.return_value = (1, {}, {}) - del os.environ[env_var] - self._test_prefix(prefix) - - def _test_prefix(self, prefix): - es = Elasticsearch() - es.index(index="sw", doc_type="people", id=1, body={"name": "adam"}) - - spans_list = self.get_ordered_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertTrue(span.name.startswith(prefix)) - - def test_result_values(self, request_mock): - request_mock.return_value = ( - 1, - {}, - '{"found": false, "timed_out": true, "took": 7}', - ) - es = Elasticsearch() - es.get(index="test-index", doc_type="tweet", id=1) - - spans = self.get_ordered_finished_spans() - - self.assertEqual(1, len(spans)) - self.assertEqual("False", spans[0].attributes["elasticsearch.found"]) - self.assertEqual( - "True", spans[0].attributes["elasticsearch.timed_out"] - ) - self.assertEqual("7", spans[0].attributes["elasticsearch.took"]) - - def test_trace_error_unknown(self, request_mock): - exc = RuntimeError("custom error") - request_mock.side_effect = exc - self._test_trace_error(StatusCode.ERROR, exc) - - def test_trace_error_not_found(self, request_mock): - msg = "record not found" - exc = elasticsearch.exceptions.NotFoundError(404, msg) - request_mock.return_value = (1, {}, {}) - request_mock.side_effect = exc - self._test_trace_error(StatusCode.ERROR, exc) - - def _test_trace_error(self, code, exc): - es = Elasticsearch() - try: - es.get(index="test-index", doc_type="tweet", id=1) - except Exception: # pylint: disable=broad-except - pass - - spans = self.get_ordered_finished_spans() - self.assertEqual(1, len(spans)) - span = spans[0] - self.assertFalse(span.status.is_ok) - self.assertEqual(span.status.status_code, code) - self.assertEqual(span.status.description, str(exc)) - - def test_parent(self, request_mock): - request_mock.return_value = (1, {}, {}) - es = Elasticsearch() - with self.tracer.start_as_current_span("parent"): - es.index( - index="sw", doc_type="people", id=1, body={"name": "adam"} - ) - - spans = self.get_ordered_finished_spans() - self.assertEqual(len(spans), 2) - - self.assertEqual(spans[0].name, "parent") - self.assertEqual(spans[1].name, "Elasticsearch/sw/people/1") - self.assertIsNotNone(spans[1].parent) - self.assertEqual(spans[1].parent.span_id, spans[0].context.span_id) - - def test_multithread(self, request_mock): - request_mock.return_value = (1, {}, {}) - es = Elasticsearch() - ev = threading.Event() - - # 1. Start tracing from thread-1; make thread-2 wait - # 2. Trace something from thread-2, make thread-1 join before finishing. - # 3. Check the spans got different parents, and are in the expected order. - def target1(parent_span): - with self.tracer.use_span(parent_span): - es.get(index="test-index", doc_type="tweet", id=1) - ev.set() - ev.wait() - - def target2(): - ev.wait() - es.get(index="test-index", doc_type="tweet", id=2) - ev.set() - - with self.tracer.start_as_current_span("parent") as span: - t1 = threading.Thread(target=target1, args=(span,)) - t1.start() - - t2 = threading.Thread(target=target2) - t2.start() - t1.join() - t2.join() - - spans = self.get_ordered_finished_spans() - self.assertEqual(3, len(spans)) - s1, s2, s3 = spans - - self.assertEqual(s1.name, "parent") - - self.assertEqual(s2.name, "Elasticsearch/test-index/tweet/1") - self.assertIsNotNone(s2.parent) - self.assertEqual(s2.parent.span_id, s1.context.span_id) - self.assertEqual(s3.name, "Elasticsearch/test-index/tweet/2") - self.assertIsNone(s3.parent) - - def test_dsl_search(self, request_mock): - request_mock.return_value = (1, {}, '{"hits": {"hits": []}}') - - client = Elasticsearch() - search = Search(using=client, index="test-index").filter( - "term", author="testing" - ) - search.execute() - spans = self.get_ordered_finished_spans() - span = spans[0] - self.assertEqual(1, len(spans)) - self.assertEqual(span.name, "Elasticsearch/test-index/_search") - self.assertIsNotNone(span.end_time) - self.assertEqual( - span.attributes, - { - "component": "elasticsearch-py", - "db.type": "elasticsearch", - "elasticsearch.url": "/test-index/_search", - "elasticsearch.method": helpers.dsl_search_method, - "db.statement": str( - { - "query": { - "bool": { - "filter": [{"term": {"author": "testing"}}] - } - } - } - ), - }, - ) - - def test_dsl_create(self, request_mock): - request_mock.return_value = (1, {}, {}) - client = Elasticsearch() - Article.init(using=client) - - spans = self.get_ordered_finished_spans() - self.assertEqual(2, len(spans)) - span1, span2 = spans - self.assertEqual(span1.name, "Elasticsearch/test-index") - self.assertEqual( - span1.attributes, - { - "component": "elasticsearch-py", - "db.type": "elasticsearch", - "elasticsearch.url": "/test-index", - "elasticsearch.method": "HEAD", - }, - ) - - self.assertEqual(span2.name, "Elasticsearch/test-index") - attributes = { - "component": "elasticsearch-py", - "db.type": "elasticsearch", - "elasticsearch.url": "/test-index", - "elasticsearch.method": "PUT", - } - self.assert_span_has_attributes(span2, attributes) - self.assertEqual( - literal_eval(span2.attributes["db.statement"]), - helpers.dsl_create_statement, - ) - - def test_dsl_index(self, request_mock): - request_mock.return_value = helpers.dsl_index_result - - client = Elasticsearch() - article = Article( - meta={"id": 2}, - title="About searching", - body="A few words here, a few words there", - ) - res = article.save(using=client) - self.assertTrue(res) - spans = self.get_ordered_finished_spans() - self.assertEqual(1, len(spans)) - span = spans[0] - self.assertEqual(span.name, helpers.dsl_index_span_name) - attributes = { - "component": "elasticsearch-py", - "db.type": "elasticsearch", - "elasticsearch.url": helpers.dsl_index_url, - "elasticsearch.method": "PUT", - } - self.assert_span_has_attributes(span, attributes) - self.assertEqual( - literal_eval(span.attributes["db.statement"]), - { - "body": "A few words here, a few words there", - "title": "About searching", - }, - ) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-falcon/CHANGELOG.md deleted file mode 100644 index 85dcb366d0b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-falcon/CHANGELOG.md +++ /dev/null @@ -1,15 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.14b0 - -Released 2020-10-13 - -- Added support for `OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS` ([#1158](https://github.com/open-telemetry/opentelemetry-python/pull/1158)) - -## Version 0.13b0 - -Released 2020-09-17 - -- Initial release. Added instrumentation for Falcon 2.0+ diff --git a/instrumentation/opentelemetry-instrumentation-falcon/LICENSE b/instrumentation/opentelemetry-instrumentation-falcon/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-falcon/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-falcon/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-falcon/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-falcon/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-falcon/README.rst b/instrumentation/opentelemetry-instrumentation-falcon/README.rst deleted file mode 100644 index 8230deaf764..00000000000 --- a/instrumentation/opentelemetry-instrumentation-falcon/README.rst +++ /dev/null @@ -1,53 +0,0 @@ -OpenTelemetry Falcon Tracing -============================ - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-falcon.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-falcon/ - -This library builds on the OpenTelemetry WSGI middleware to track web requests -in Falcon applications. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-falcon - -Configuration -------------- - -Exclude lists -************* -To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_FALCON_EXCLUDED_URLS`` with comma delimited regexes representing which URLs to exclude. - -For example, - -:: - - export OTEL_PYTHON_FALCON_EXCLUDED_URLS="client/.*/info,healthcheck" - -will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``. - -Request attributes -******************** -To extract certain attributes from Falcon's request object and use them as span attributes, set the environment variable ``OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS`` to a comma -delimited list of request attribute names. - -For example, - -:: - - export OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS='query_string,uri_template' - -will extract path_info and content_type attributes from every traced request and add them as span attritbues. - -Falcon Request object reference: https://falcon.readthedocs.io/en/stable/api/request_and_response.html#id1 - -References ----------- - -* `OpenTelemetry Falcon Instrumentation `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-falcon/setup.cfg b/instrumentation/opentelemetry-instrumentation-falcon/setup.cfg deleted file mode 100644 index 88e287c6071..00000000000 --- a/instrumentation/opentelemetry-instrumentation-falcon/setup.cfg +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-falcon -description = Falcon instrumentation for OpenTelemetry -long_description = file: README.rst -long_description_content_type = text/x-rst -author = OpenTelemetry Authors -author_email = cncf-opentelemetry-contributors@lists.cncf.io -url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-falcon -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.4 -package_dir= - =src -packages=find_namespace: -install_requires = - falcon ~= 2.0 - opentelemetry-instrumentation-wsgi == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - opentelemetry-api == 0.16.dev0 - -[options.extras_require] -test = - falcon ~= 2.0 - opentelemetry-test == 0.16.dev0 - parameterized == 0.7.4 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - falcon = opentelemetry.instrumentation.falcon:FalconInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-falcon/setup.py b/instrumentation/opentelemetry-instrumentation-falcon/setup.py deleted file mode 100644 index eb61edde629..00000000000 --- a/instrumentation/opentelemetry-instrumentation-falcon/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "falcon", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py deleted file mode 100644 index 55f8e98dcb1..00000000000 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ /dev/null @@ -1,222 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -This library builds on the OpenTelemetry WSGI middleware to track web requests -in Falcon applications. In addition to opentelemetry-instrumentation-wsgi, -it supports falcon-specific features such as: - -* The Falcon resource and method name is used as the Span name. -* The ``falcon.resource`` Span attribute is set so the matched resource. -* Error from Falcon resources are properly caught and recorded. - -Usage ------ - -.. code-block:: python - - from falcon import API - from opentelemetry.instrumentation.falcon import FalconInstrumentor - - FalconInstrumentor().instrument() - - app = falcon.API() - - class HelloWorldResource(object): - def on_get(self, req, resp): - resp.body = 'Hello World' - - app.add_route('/hello', HelloWorldResource()) - -API ---- -""" - -import sys -from logging import getLogger - -import falcon - -import opentelemetry.instrumentation.wsgi as otel_wsgi -from opentelemetry import configuration, context, propagators, trace -from opentelemetry.configuration import Configuration -from opentelemetry.instrumentation.falcon.version import __version__ -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.utils import ( - extract_attributes_from_object, - http_status_to_status_code, -) -from opentelemetry.trace.status import Status -from opentelemetry.util import ExcludeList, time_ns - -_logger = getLogger(__name__) - -_ENVIRON_STARTTIME_KEY = "opentelemetry-falcon.starttime_key" -_ENVIRON_SPAN_KEY = "opentelemetry-falcon.span_key" -_ENVIRON_ACTIVATION_KEY = "opentelemetry-falcon.activation_key" -_ENVIRON_TOKEN = "opentelemetry-falcon.token" -_ENVIRON_EXC = "opentelemetry-falcon.exc" - - -def get_excluded_urls(): - urls = configuration.Configuration().FALCON_EXCLUDED_URLS or "" - if urls: - urls = str.split(urls, ",") - return ExcludeList(urls) - - -_excluded_urls = get_excluded_urls() - - -class FalconInstrumentor(BaseInstrumentor): - # pylint: disable=protected-access,attribute-defined-outside-init - """An instrumentor for falcon.API - - See `BaseInstrumentor` - """ - - def _instrument(self, **kwargs): - self._original_falcon_api = falcon.API - falcon.API = _InstrumentedFalconAPI - - def _uninstrument(self, **kwargs): - falcon.API = self._original_falcon_api - - -class _InstrumentedFalconAPI(falcon.API): - def __init__(self, *args, **kwargs): - middlewares = kwargs.pop("middleware", []) - if not isinstance(middlewares, (list, tuple)): - middlewares = [middlewares] - - self._tracer = trace.get_tracer(__name__, __version__) - trace_middleware = _TraceMiddleware( - self._tracer, kwargs.get("traced_request_attributes") - ) - middlewares.insert(0, trace_middleware) - kwargs["middleware"] = middlewares - super().__init__(*args, **kwargs) - - def __call__(self, env, start_response): - if _excluded_urls.url_disabled(env.get("PATH_INFO", "/")): - return super().__call__(env, start_response) - - start_time = time_ns() - - token = context.attach( - propagators.extract(otel_wsgi.carrier_getter, env) - ) - span = self._tracer.start_span( - otel_wsgi.get_default_span_name(env), - kind=trace.SpanKind.SERVER, - start_time=start_time, - ) - if span.is_recording(): - attributes = otel_wsgi.collect_request_attributes(env) - for key, value in attributes.items(): - span.set_attribute(key, value) - - activation = self._tracer.use_span(span, end_on_exit=True) - activation.__enter__() - env[_ENVIRON_SPAN_KEY] = span - env[_ENVIRON_ACTIVATION_KEY] = activation - - def _start_response(status, response_headers, *args, **kwargs): - otel_wsgi.add_response_attributes(span, status, response_headers) - response = start_response( - status, response_headers, *args, **kwargs - ) - activation.__exit__(None, None, None) - context.detach(token) - return response - - try: - return super().__call__(env, _start_response) - except Exception as exc: - activation.__exit__( - type(exc), exc, getattr(exc, "__traceback__", None), - ) - context.detach(token) - raise - - -class _TraceMiddleware: - # pylint:disable=R0201,W0613 - - def __init__(self, tracer=None, traced_request_attrs=None): - self.tracer = tracer - self._traced_request_attrs = traced_request_attrs or [ - attr.strip() - for attr in ( - Configuration().FALCON_TRACED_REQUEST_ATTRS or "" - ).split(",") - ] - - def process_request(self, req, resp): - span = req.env.get(_ENVIRON_SPAN_KEY) - if not span or not span.is_recording(): - return - - attributes = extract_attributes_from_object( - req, self._traced_request_attrs - ) - for key, value in attributes.items(): - span.set_attribute(key, value) - - def process_resource(self, req, resp, resource, params): - span = req.env.get(_ENVIRON_SPAN_KEY) - if not span or not span.is_recording(): - return - - resource_name = resource.__class__.__name__ - span.set_attribute("falcon.resource", resource_name) - span.update_name( - "{0}.on_{1}".format(resource_name, req.method.lower()) - ) - - def process_response( - self, req, resp, resource, req_succeeded=None - ): # pylint:disable=R0201 - span = req.env.get(_ENVIRON_SPAN_KEY) - if not span or not span.is_recording(): - return - - status = resp.status - reason = None - if resource is None: - status = "404" - reason = "NotFound" - - exc_type, exc, _ = sys.exc_info() - if exc_type and not req_succeeded: - if "HTTPNotFound" in exc_type.__name__: - status = "404" - reason = "NotFound" - else: - status = "500" - reason = "{}: {}".format(exc_type.__name__, exc) - - status = status.split(" ")[0] - try: - status_code = int(status) - except ValueError: - pass - finally: - span.set_attribute("http.status_code", status_code) - span.set_status( - Status( - status_code=http_status_to_status_code(status_code), - description=reason, - ) - ) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/version.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/app.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/app.py deleted file mode 100644 index dcbfe11b491..00000000000 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/app.py +++ /dev/null @@ -1,41 +0,0 @@ -import falcon - -# pylint:disable=R0201,W0613,E0602 - - -class HelloWorldResource: - def _handle_request(self, _, resp): - # pylint: disable=no-member - resp.status = falcon.HTTP_201 - resp.body = "Hello World" - - def on_get(self, req, resp): - self._handle_request(req, resp) - - def on_put(self, req, resp): - self._handle_request(req, resp) - - def on_patch(self, req, resp): - self._handle_request(req, resp) - - def on_post(self, req, resp): - self._handle_request(req, resp) - - def on_delete(self, req, resp): - self._handle_request(req, resp) - - def on_head(self, req, resp): - self._handle_request(req, resp) - - -class ErrorResource: - def on_get(self, req, resp): - print(non_existent_var) # noqa - - -def make_app(): - app = falcon.API() - app.add_route("/hello", HelloWorldResource()) - app.add_route("/ping", HelloWorldResource()) - app.add_route("/error", ErrorResource()) - return app diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py deleted file mode 100644 index fe33a2f2dd1..00000000000 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ /dev/null @@ -1,201 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest.mock import Mock, patch - -from falcon import testing - -from opentelemetry.instrumentation.falcon import FalconInstrumentor -from opentelemetry.test.test_base import TestBase -from opentelemetry.trace.status import StatusCode -from opentelemetry.util import ExcludeList - -from .app import make_app - - -class TestFalconInstrumentation(TestBase): - def setUp(self): - super().setUp() - FalconInstrumentor().instrument() - self.app = make_app() - - def client(self): - return testing.TestClient(self.app) - - def tearDown(self): - super().tearDown() - with self.disable_logging(): - FalconInstrumentor().uninstrument() - - def test_get(self): - self._test_method("GET") - - def test_post(self): - self._test_method("POST") - - def test_patch(self): - self._test_method("PATCH") - - def test_put(self): - self._test_method("PUT") - - def test_delete(self): - self._test_method("DELETE") - - def test_head(self): - self._test_method("HEAD") - - def _test_method(self, method): - self.client().simulate_request(method=method, path="/hello") - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertEqual( - span.name, "HelloWorldResource.on_{0}".format(method.lower()) - ) - self.assertEqual(span.status.status_code, StatusCode.UNSET) - self.assert_span_has_attributes( - span, - { - "component": "http", - "http.method": method, - "http.server_name": "falconframework.org", - "http.scheme": "http", - "host.port": 80, - "http.host": "falconframework.org", - "http.target": "/", - "net.peer.ip": "127.0.0.1", - "net.peer.port": "65133", - "http.flavor": "1.1", - "falcon.resource": "HelloWorldResource", - "http.status_text": "Created", - "http.status_code": 201, - }, - ) - self.memory_exporter.clear() - - def test_404(self): - self.client().simulate_get("/does-not-exist") - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertEqual(span.name, "HTTP GET") - self.assertEqual(span.status.status_code, StatusCode.ERROR) - self.assert_span_has_attributes( - span, - { - "component": "http", - "http.method": "GET", - "http.server_name": "falconframework.org", - "http.scheme": "http", - "host.port": 80, - "http.host": "falconframework.org", - "http.target": "/", - "net.peer.ip": "127.0.0.1", - "net.peer.port": "65133", - "http.flavor": "1.1", - "http.status_text": "Not Found", - "http.status_code": 404, - }, - ) - - def test_500(self): - try: - self.client().simulate_get("/error") - except NameError: - pass - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertEqual(span.name, "ErrorResource.on_get") - self.assertFalse(span.status.is_ok) - self.assertEqual(span.status.status_code, StatusCode.ERROR) - self.assertEqual( - span.status.description, - "NameError: name 'non_existent_var' is not defined", - ) - self.assert_span_has_attributes( - span, - { - "component": "http", - "http.method": "GET", - "http.server_name": "falconframework.org", - "http.scheme": "http", - "host.port": 80, - "http.host": "falconframework.org", - "http.target": "/", - "net.peer.ip": "127.0.0.1", - "net.peer.port": "65133", - "http.flavor": "1.1", - "http.status_code": 500, - }, - ) - - def test_uninstrument(self): - self.client().simulate_get(path="/hello") - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - - self.memory_exporter.clear() - - FalconInstrumentor().uninstrument() - self.app = make_app() - self.client().simulate_get(path="/hello") - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 0) - - @patch( - "opentelemetry.instrumentation.falcon._excluded_urls", - ExcludeList(["ping"]), - ) - def test_exclude_lists(self): - self.client().simulate_get(path="/ping") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 0) - - self.client().simulate_get(path="/hello") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - def test_traced_request_attributes(self): - self.client().simulate_get(path="/hello?q=abc") - span = self.memory_exporter.get_finished_spans()[0] - self.assertNotIn("query_string", span.attributes) - self.memory_exporter.clear() - - middleware = self.app._middleware[0][ # pylint:disable=W0212 - 0 - ].__self__ - with patch.object( - middleware, "_traced_request_attrs", ["query_string"] - ): - self.client().simulate_get(path="/hello?q=abc") - span = self.memory_exporter.get_finished_spans()[0] - self.assertIn("query_string", span.attributes) - self.assertEqual(span.attributes["query_string"], "q=abc") - - def test_traced_not_recording(self): - mock_tracer = Mock() - mock_span = Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = mock_span - with patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - self.client().simulate_get(path="/hello?q=abc") - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-fastapi/CHANGELOG.md deleted file mode 100644 index c8c5cea0d3d..00000000000 --- a/instrumentation/opentelemetry-instrumentation-fastapi/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.11b0 - -Released 2020-07-28 - -- Initial release ([#890](https://github.com/open-telemetry/opentelemetry-python/pull/890)) \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/README.rst b/instrumentation/opentelemetry-instrumentation-fastapi/README.rst deleted file mode 100644 index 4cc612da760..00000000000 --- a/instrumentation/opentelemetry-instrumentation-fastapi/README.rst +++ /dev/null @@ -1,43 +0,0 @@ -OpenTelemetry FastAPI Instrumentation -======================================= - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-fastapi.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-fastapi/ - - -This library provides automatic and manual instrumentation of FastAPI web frameworks, -instrumenting http requests served by applications utilizing the framework. - -auto-instrumentation using the opentelemetry-instrumentation package is also supported. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-fastapi - - -Usage ------ - -.. code-block:: python - - import fastapi - from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor - - app = fastapi.FastAPI() - - @app.get("/foobar") - async def foobar(): - return {"message": "hello world"} - - FastAPIInstrumentor.instrument_app(app) - - -References ----------- - -* `OpenTelemetry Project `_ \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/setup.cfg b/instrumentation/opentelemetry-instrumentation-fastapi/setup.cfg deleted file mode 100644 index d6b6bdc54f6..00000000000 --- a/instrumentation/opentelemetry-instrumentation-fastapi/setup.cfg +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-fastapi -description = OpenTelemetry FastAPI Instrumentation -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/master/instrumentation/opentelemetry-instrumentation-fastapi -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.6 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation-asgi == 0.16.dev0 - -[options.entry_points] -opentelemetry_instrumentor = - fastapi = opentelemetry.instrumentation.fastapi:FastAPIInstrumentor - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - fastapi ~= 0.58.1 - requests ~= 2.23.0 # needed for testclient - -[options.packages.find] -where = src diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/setup.py b/instrumentation/opentelemetry-instrumentation-fastapi/setup.py deleted file mode 100644 index 13c7c5a99c0..00000000000 --- a/instrumentation/opentelemetry-instrumentation-fastapi/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "fastapi", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py deleted file mode 100644 index 57c9a5bfc79..00000000000 --- a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from typing import Optional - -import fastapi -from starlette.routing import Match - -from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware -from opentelemetry.instrumentation.fastapi.version import __version__ # noqa -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor - - -class FastAPIInstrumentor(BaseInstrumentor): - """An instrumentor for FastAPI - - See `BaseInstrumentor` - """ - - _original_fastapi = None - - @staticmethod - def instrument_app(app: fastapi.FastAPI): - """Instrument an uninstrumented FastAPI application. - """ - if not getattr(app, "is_instrumented_by_opentelemetry", False): - app.add_middleware( - OpenTelemetryMiddleware, - span_details_callback=_get_route_details, - ) - app.is_instrumented_by_opentelemetry = True - - def _instrument(self, **kwargs): - self._original_fastapi = fastapi.FastAPI - fastapi.FastAPI = _InstrumentedFastAPI - - def _uninstrument(self, **kwargs): - fastapi.FastAPI = self._original_fastapi - - -class _InstrumentedFastAPI(fastapi.FastAPI): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.add_middleware( - OpenTelemetryMiddleware, span_details_callback=_get_route_details - ) - - -def _get_route_details(scope): - """Callback to retrieve the fastapi route being served. - - TODO: there is currently no way to retrieve http.route from - a starlette application from scope. - - See: https://github.com/encode/starlette/pull/804 - """ - app = scope["app"] - route = None - for starlette_route in app.routes: - match, _ = starlette_route.matches(scope) - if match == Match.FULL: - route = starlette_route.path - break - if match == Match.PARTIAL: - route = starlette_route.path - # method only exists for http, if websocket - # leave it blank. - span_name = route or scope.get("method", "") - attributes = {} - if route: - attributes["http.route"] = route - return span_name, attributes diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/version.py b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-fastapi/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py deleted file mode 100644 index 47617d4e959..00000000000 --- a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest - -import fastapi -from fastapi.testclient import TestClient - -import opentelemetry.instrumentation.fastapi as otel_fastapi -from opentelemetry.test.test_base import TestBase - - -class TestFastAPIManualInstrumentation(TestBase): - def _create_app(self): - app = self._create_fastapi_app() - self._instrumentor.instrument_app(app) - return app - - def setUp(self): - super().setUp() - self._instrumentor = otel_fastapi.FastAPIInstrumentor() - self._app = self._create_app() - self._client = TestClient(self._app) - - def test_basic_fastapi_call(self): - self._client.get("/foobar") - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 3) - for span in spans: - self.assertIn("/foobar", span.name) - - def test_fastapi_route_attribute_added(self): - """Ensure that fastapi routes are used as the span name.""" - self._client.get("/user/123") - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 3) - for span in spans: - self.assertIn("/user/{username}", span.name) - self.assertEqual( - spans[-1].attributes["http.route"], "/user/{username}" - ) - # ensure that at least one attribute that is populated by - # the asgi instrumentation is successfully feeding though. - self.assertEqual(spans[-1].attributes["http.flavor"], "1.1") - - @staticmethod - def _create_fastapi_app(): - app = fastapi.FastAPI() - - @app.get("/foobar") - async def _(): - return {"message": "hello world"} - - @app.get("/user/{username}") - async def _(username: str): - return {"message": username} - - return app - - -class TestAutoInstrumentation(TestFastAPIManualInstrumentation): - """Test the auto-instrumented variant - - Extending the manual instrumentation as most test cases apply - to both. - """ - - def _create_app(self): - # instrumentation is handled by the instrument call - self._instrumentor.instrument() - return self._create_fastapi_app() - - def tearDown(self): - self._instrumentor.uninstrument() - super().tearDown() - - -class TestAutoInstrumentationLogic(unittest.TestCase): - def test_instrumentation(self): - """Verify that instrumentation methods are instrumenting and - removing as expected. - """ - instrumentor = otel_fastapi.FastAPIInstrumentor() - original = fastapi.FastAPI - instrumentor.instrument() - try: - instrumented = fastapi.FastAPI - self.assertIsNot(original, instrumented) - finally: - instrumentor.uninstrument() - - should_be_original = fastapi.FastAPI - self.assertIs(original, should_be_original) diff --git a/instrumentation/opentelemetry-instrumentation-flask/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-flask/CHANGELOG.md deleted file mode 100644 index 4c7e7a055b8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-flask/CHANGELOG.md +++ /dev/null @@ -1,56 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.15b0 - -Released 2020-11-02 - -- Use `url.rule` instead of `request.endpoint` for span name - ([#1260](https://github.com/open-telemetry/opentelemetry-python/pull/1260)) - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-flask - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) -- Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) - -## Version 0.11b0 - -- Use one general exclude list instead of two ([#872](https://github.com/open-telemetry/opentelemetry-python/pull/872)) - -## 0.7b1 - -Released 2020-05-12 - -- Add exclude list for paths and hosts - ([#630](https://github.com/open-telemetry/opentelemetry-python/pull/630)) - -## 0.6b0 - -Released 2020-03-30 - -- Add an entry_point to be usable in auto-instrumentation - ([#327](https://github.com/open-telemetry/opentelemetry-python/pull/327)) - -## 0.4a0 - -Released 2020-02-21 - -- Use string keys for WSGI environ values - ([#366](https://github.com/open-telemetry/opentelemetry-python/pull/366)) - -## 0.3a0 - -Released 2019-12-11 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-flask/LICENSE b/instrumentation/opentelemetry-instrumentation-flask/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-flask/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-flask/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-flask/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-flask/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-flask/README.rst b/instrumentation/opentelemetry-instrumentation-flask/README.rst deleted file mode 100644 index f79d8fd6041..00000000000 --- a/instrumentation/opentelemetry-instrumentation-flask/README.rst +++ /dev/null @@ -1,38 +0,0 @@ -OpenTelemetry Flask Tracing -=========================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-flask.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-flask/ - -This library builds on the OpenTelemetry WSGI middleware to track web requests -in Flask applications. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-flask - -Configuration -------------- - -Exclude lists -************* -To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_FLASK_EXCLUDED_URLS`` with comma delimited regexes representing which URLs to exclude. - -For example, - -:: - - export OTEL_PYTHON_FLASK_EXCLUDED_URLS="client/.*/info,healthcheck" - -will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``. - -References ----------- - -* `OpenTelemetry Flask Instrumentation `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-flask/setup.cfg b/instrumentation/opentelemetry-instrumentation-flask/setup.cfg deleted file mode 100644 index 2e9f943ceb1..00000000000 --- a/instrumentation/opentelemetry-instrumentation-flask/setup.cfg +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-flask -description = Flask instrumentation for OpenTelemetry -long_description = file: README.rst -long_description_content_type = text/x-rst -author = OpenTelemetry Authors -author_email = cncf-opentelemetry-contributors@lists.cncf.io -url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-flask -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - flask ~= 1.0 - opentelemetry-instrumentation-wsgi == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - opentelemetry-api == 0.16.dev0 - -[options.extras_require] -test = - flask~=1.0 - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src diff --git a/instrumentation/opentelemetry-instrumentation-flask/setup.py b/instrumentation/opentelemetry-instrumentation-flask/setup.py deleted file mode 100644 index 587b697a7b0..00000000000 --- a/instrumentation/opentelemetry-instrumentation-flask/setup.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "flask", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup( - version=PACKAGE_INFO["__version__"], - entry_points={ - "opentelemetry_instrumentor": [ - "flask = opentelemetry.instrumentation.flask:FlaskInstrumentor" - ] - }, -) diff --git a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py deleted file mode 100644 index 1235b09a307..00000000000 --- a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py +++ /dev/null @@ -1,227 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Note: This package is not named "flask" because of -# https://github.com/PyCQA/pylint/issues/2648 - -""" -This library builds on the OpenTelemetry WSGI middleware to track web requests -in Flask applications. In addition to opentelemetry-instrumentation-wsgi, it supports -flask-specific features such as: - -* The Flask endpoint name is used as the Span name. -* The ``http.route`` Span attribute is set so that one can see which URL rule - matched a request. - -Usage ------ - -.. code-block:: python - - from flask import Flask - from opentelemetry.instrumentation.flask import FlaskInstrumentor - - app = Flask(__name__) - - FlaskInstrumentor().instrument_app(app) - - @app.route("/") - def hello(): - return "Hello!" - - if __name__ == "__main__": - app.run(debug=True) - -API ---- -""" - -from logging import getLogger - -import flask - -import opentelemetry.instrumentation.wsgi as otel_wsgi -from opentelemetry import configuration, context, propagators, trace -from opentelemetry.instrumentation.flask.version import __version__ -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.util import ExcludeList, time_ns - -_logger = getLogger(__name__) - -_ENVIRON_STARTTIME_KEY = "opentelemetry-flask.starttime_key" -_ENVIRON_SPAN_KEY = "opentelemetry-flask.span_key" -_ENVIRON_ACTIVATION_KEY = "opentelemetry-flask.activation_key" -_ENVIRON_TOKEN = "opentelemetry-flask.token" - - -def get_excluded_urls(): - urls = configuration.Configuration().FLASK_EXCLUDED_URLS or [] - if urls: - urls = str.split(urls, ",") - return ExcludeList(urls) - - -_excluded_urls = get_excluded_urls() - - -def _rewrapped_app(wsgi_app): - def _wrapped_app(environ, start_response): - # We want to measure the time for route matching, etc. - # In theory, we could start the span here and use - # update_name later but that API is "highly discouraged" so - # we better avoid it. - environ[_ENVIRON_STARTTIME_KEY] = time_ns() - - def _start_response(status, response_headers, *args, **kwargs): - if not _excluded_urls.url_disabled(flask.request.url): - span = flask.request.environ.get(_ENVIRON_SPAN_KEY) - - if span: - otel_wsgi.add_response_attributes( - span, status, response_headers - ) - else: - _logger.warning( - "Flask environ's OpenTelemetry span " - "missing at _start_response(%s)", - status, - ) - - return start_response(status, response_headers, *args, **kwargs) - - return wsgi_app(environ, _start_response) - - return _wrapped_app - - -def _before_request(): - if _excluded_urls.url_disabled(flask.request.url): - return - - environ = flask.request.environ - span_name = None - try: - span_name = flask.request.url_rule.rule - except AttributeError: - pass - if span_name is None: - span_name = otel_wsgi.get_default_span_name(environ) - token = context.attach( - propagators.extract(otel_wsgi.carrier_getter, environ) - ) - - tracer = trace.get_tracer(__name__, __version__) - - span = tracer.start_span( - span_name, - kind=trace.SpanKind.SERVER, - start_time=environ.get(_ENVIRON_STARTTIME_KEY), - ) - if span.is_recording(): - attributes = otel_wsgi.collect_request_attributes(environ) - if flask.request.url_rule: - # For 404 that result from no route found, etc, we - # don't have a url_rule. - attributes["http.route"] = flask.request.url_rule.rule - for key, value in attributes.items(): - span.set_attribute(key, value) - - activation = tracer.use_span(span, end_on_exit=True) - activation.__enter__() - environ[_ENVIRON_ACTIVATION_KEY] = activation - environ[_ENVIRON_SPAN_KEY] = span - environ[_ENVIRON_TOKEN] = token - - -def _teardown_request(exc): - if _excluded_urls.url_disabled(flask.request.url): - return - - activation = flask.request.environ.get(_ENVIRON_ACTIVATION_KEY) - if not activation: - _logger.warning( - "Flask environ's OpenTelemetry activation missing" - "at _teardown_flask_request(%s)", - exc, - ) - return - - if exc is None: - activation.__exit__(None, None, None) - else: - activation.__exit__( - type(exc), exc, getattr(exc, "__traceback__", None) - ) - context.detach(flask.request.environ.get(_ENVIRON_TOKEN)) - - -class _InstrumentedFlask(flask.Flask): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self._original_wsgi_ = self.wsgi_app - self.wsgi_app = _rewrapped_app(self.wsgi_app) - - self.before_request(_before_request) - self.teardown_request(_teardown_request) - - -class FlaskInstrumentor(BaseInstrumentor): - # pylint: disable=protected-access,attribute-defined-outside-init - """An instrumentor for flask.Flask - - See `BaseInstrumentor` - """ - - def _instrument(self, **kwargs): - self._original_flask = flask.Flask - flask.Flask = _InstrumentedFlask - - def instrument_app(self, app): # pylint: disable=no-self-use - if not hasattr(app, "_is_instrumented"): - app._is_instrumented = False - - if not app._is_instrumented: - app._original_wsgi_app = app.wsgi_app - app.wsgi_app = _rewrapped_app(app.wsgi_app) - - app.before_request(_before_request) - app.teardown_request(_teardown_request) - app._is_instrumented = True - else: - _logger.warning( - "Attempting to instrument Flask app while already instrumented" - ) - - def _uninstrument(self, **kwargs): - flask.Flask = self._original_flask - - def uninstrument_app(self, app): # pylint: disable=no-self-use - if not hasattr(app, "_is_instrumented"): - app._is_instrumented = False - - if app._is_instrumented: - app.wsgi_app = app._original_wsgi_app - - # FIXME add support for other Flask blueprints that are not None - app.before_request_funcs[None].remove(_before_request) - app.teardown_request_funcs[None].remove(_teardown_request) - del app._original_wsgi_app - - app._is_instrumented = False - else: - _logger.warning( - "Attempting to uninstrument Flask " - "app while already uninstrumented" - ) diff --git a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/version.py b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-flask/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/base_test.py b/instrumentation/opentelemetry-instrumentation-flask/tests/base_test.py deleted file mode 100644 index c2bc646e1ba..00000000000 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/base_test.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from werkzeug.test import Client -from werkzeug.wrappers import BaseResponse - -from opentelemetry.configuration import Configuration - - -class InstrumentationTest: - def setUp(self): # pylint: disable=invalid-name - super().setUp() # pylint: disable=no-member - Configuration._reset() # pylint: disable=protected-access - - @staticmethod - def _hello_endpoint(helloid): - if helloid == 500: - raise ValueError(":-(") - return "Hello: " + str(helloid) - - def _common_initialization(self): - def excluded_endpoint(): - return "excluded" - - def excluded2_endpoint(): - return "excluded2" - - # pylint: disable=no-member - self.app.route("/hello/")(self._hello_endpoint) - self.app.route("/excluded/")(self._hello_endpoint) - self.app.route("/excluded")(excluded_endpoint) - self.app.route("/excluded2")(excluded2_endpoint) - - # pylint: disable=attribute-defined-outside-init - self.client = Client(self.app, BaseResponse) diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_automatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_automatic.py deleted file mode 100644 index d081bed9ee8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_automatic.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import flask -from werkzeug.test import Client -from werkzeug.wrappers import BaseResponse - -from opentelemetry.instrumentation.flask import FlaskInstrumentor -from opentelemetry.test.test_base import TestBase -from opentelemetry.test.wsgitestutil import WsgiTestBase - -# pylint: disable=import-error -from .base_test import InstrumentationTest - - -class TestAutomatic(InstrumentationTest, TestBase, WsgiTestBase): - def setUp(self): - super().setUp() - - FlaskInstrumentor().instrument() - - self.app = flask.Flask(__name__) - - self._common_initialization() - - def tearDown(self): - super().tearDown() - with self.disable_logging(): - FlaskInstrumentor().uninstrument() - - def test_uninstrument(self): - # pylint: disable=access-member-before-definition - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - FlaskInstrumentor().uninstrument() - self.app = flask.Flask(__name__) - - self.app.route("/hello/")(self._hello_endpoint) - # pylint: disable=attribute-defined-outside-init - self.client = Client(self.app, BaseResponse) - - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py deleted file mode 100644 index a907890523c..00000000000 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest.mock import Mock, patch - -from flask import Flask, request - -from opentelemetry import trace -from opentelemetry.instrumentation.flask import FlaskInstrumentor -from opentelemetry.test.test_base import TestBase -from opentelemetry.test.wsgitestutil import WsgiTestBase -from opentelemetry.util import ExcludeList - -# pylint: disable=import-error -from .base_test import InstrumentationTest - - -def expected_attributes(override_attributes): - default_attributes = { - "component": "http", - "http.method": "GET", - "http.server_name": "localhost", - "http.scheme": "http", - "host.port": 80, - "http.host": "localhost", - "http.target": "/", - "http.flavor": "1.1", - "http.status_text": "OK", - "http.status_code": 200, - } - for key, val in override_attributes.items(): - default_attributes[key] = val - return default_attributes - - -class TestProgrammatic(InstrumentationTest, TestBase, WsgiTestBase): - def setUp(self): - super().setUp() - - self.app = Flask(__name__) - - FlaskInstrumentor().instrument_app(self.app) - - self._common_initialization() - - def tearDown(self): - super().tearDown() - with self.disable_logging(): - FlaskInstrumentor().uninstrument_app(self.app) - - def test_uninstrument(self): - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - FlaskInstrumentor().uninstrument_app(self.app) - - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - # pylint: disable=no-member - def test_only_strings_in_environ(self): - """ - Some WSGI servers (such as Gunicorn) expect keys in the environ object - to be strings - - OpenTelemetry should adhere to this convention. - """ - nonstring_keys = set() - - def assert_environ(): - for key in request.environ: - if not isinstance(key, str): - nonstring_keys.add(key) - return "hi" - - self.app.route("/assert_environ")(assert_environ) - self.client.get("/assert_environ") - self.assertEqual(nonstring_keys, set()) - - def test_simple(self): - expected_attrs = expected_attributes( - {"http.target": "/hello/123", "http.route": "/hello/"} - ) - self.client.get("/hello/123") - - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, "/hello/") - self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) - self.assertEqual(span_list[0].attributes, expected_attrs) - - def test_not_recording(self): - mock_tracer = Mock() - mock_span = Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = mock_span - with patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - self.client.get("/hello/123") - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_404(self): - expected_attrs = expected_attributes( - { - "http.method": "POST", - "http.target": "/bye", - "http.status_text": "NOT FOUND", - "http.status_code": 404, - } - ) - - resp = self.client.post("/bye") - self.assertEqual(404, resp.status_code) - resp.close() - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, "HTTP POST") - self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) - self.assertEqual(span_list[0].attributes, expected_attrs) - - def test_internal_error(self): - expected_attrs = expected_attributes( - { - "http.target": "/hello/500", - "http.route": "/hello/", - "http.status_text": "INTERNAL SERVER ERROR", - "http.status_code": 500, - } - ) - resp = self.client.get("/hello/500") - self.assertEqual(500, resp.status_code) - resp.close() - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, "/hello/") - self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) - self.assertEqual(span_list[0].attributes, expected_attrs) - - @patch( - "opentelemetry.instrumentation.flask._excluded_urls", - ExcludeList(["http://localhost/excluded_arg/123", "excluded_noarg"]), - ) - def test_exclude_lists(self): - self.client.get("/excluded_arg/123") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 0) - - self.client.get("/excluded_arg/125") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - self.client.get("/excluded_noarg") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - self.client.get("/excluded_noarg2") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-grpc/CHANGELOG.md deleted file mode 100644 index ed6889dcb75..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/CHANGELOG.md +++ /dev/null @@ -1,43 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-grpc - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) - -## Version 0.11b0 - -Released 2020-07-28 - -- Add status code to gRPC client spans - ([896](https://github.com/open-telemetry/opentelemetry-python/pull/896)) -- Add gRPC client and server instrumentors - ([788](https://github.com/open-telemetry/opentelemetry-python/pull/788)) - -- Add metric recording (bytes in/out, errors, latency) to gRPC client - -## 0.8b0 - -Released 2020-05-27 - -- lint: version of grpc causes lint issues - ([#696](https://github.com/open-telemetry/opentelemetry-python/pull/696)) - -## 0.6b0 - -Released 2020-03-30 - -- Add gRPC integration - ([#476](https://github.com/open-telemetry/opentelemetry-python/pull/476)) -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-grpc/LICENSE b/instrumentation/opentelemetry-instrumentation-grpc/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-grpc/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-grpc/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-grpc/README.rst b/instrumentation/opentelemetry-instrumentation-grpc/README.rst deleted file mode 100644 index 176bdf1a39b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/README.rst +++ /dev/null @@ -1,18 +0,0 @@ -OpenTelemetry gRPC Integration -============================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-grpc.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-grpc/ - -Client and server interceptors for `gRPC Python`_. - -.. _gRPC Python: https://grpc.github.io/grpc/python/grpc.html - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-grpc diff --git a/instrumentation/opentelemetry-instrumentation-grpc/setup.cfg b/instrumentation/opentelemetry-instrumentation-grpc/setup.cfg deleted file mode 100644 index da31572e02c..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/setup.cfg +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -[metadata] -name = opentelemetry-instrumentation-grpc -description = OpenTelemetry gRPC instrumentation -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/master/instrumentation/opentelemetry-instrumentation-grpc -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-sdk == 0.16.dev0 - grpcio ~= 1.27 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - opentelemetry-sdk == 0.16.dev0 - protobuf == 3.12.2 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - grpc_client = opentelemetry.instrumentation.grpc:GrpcInstrumentorClient - grpc_server = opentelemetry.instrumentation.grpc:GrpcInstrumentorServer diff --git a/instrumentation/opentelemetry-instrumentation-grpc/setup.py b/instrumentation/opentelemetry-instrumentation-grpc/setup.py deleted file mode 100644 index 87c720aea23..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "grpc", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py deleted file mode 100644 index 776e29e8e20..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py +++ /dev/null @@ -1,248 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pylint:disable=no-name-in-module -# pylint:disable=relative-beyond-top-level -# pylint:disable=import-error -# pylint:disable=no-self-use -""" -Usage Client ------------- -.. code-block:: python - - import logging - - import grpc - - from opentelemetry import trace - from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient, client_interceptor - from opentelemetry.sdk.trace import TracerProvider - from opentelemetry.sdk.trace.export import ( - ConsoleSpanExporter, - SimpleExportSpanProcessor, - ) - - from opentelemetry import metrics - from opentelemetry.sdk.metrics import MeterProvider - from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter - - try: - from .gen import helloworld_pb2, helloworld_pb2_grpc - except ImportError: - from gen import helloworld_pb2, helloworld_pb2_grpc - - trace.set_tracer_provider(TracerProvider()) - trace.get_tracer_provider().add_span_processor( - SimpleExportSpanProcessor(ConsoleSpanExporter()) - ) - - # Set meter provider to opentelemetry-sdk's MeterProvider - metrics.set_meter_provider(MeterProvider()) - - # Optional - export GRPC specific metrics (latency, bytes in/out, errors) by passing an exporter - instrumentor = GrpcInstrumentorClient(exporter=ConsoleMetricsExporter(), interval=10) - instrumentor.instrument() - - def run(): - with grpc.insecure_channel("localhost:50051") as channel: - - stub = helloworld_pb2_grpc.GreeterStub(channel) - response = stub.SayHello(helloworld_pb2.HelloRequest(name="YOU")) - - print("Greeter client received: " + response.message) - - - if __name__ == "__main__": - logging.basicConfig() - run() - -Usage Server ------------- -.. code-block:: python - - import logging - from concurrent import futures - - import grpc - - from opentelemetry import trace - from opentelemetry.instrumentation.grpc import GrpcInstrumentorServer - from opentelemetry.sdk.trace import TracerProvider - from opentelemetry.sdk.trace.export import ( - ConsoleSpanExporter, - SimpleExportSpanProcessor, - ) - - try: - from .gen import helloworld_pb2, helloworld_pb2_grpc - except ImportError: - from gen import helloworld_pb2, helloworld_pb2_grpc - - trace.set_tracer_provider(TracerProvider()) - trace.get_tracer_provider().add_span_processor( - SimpleExportSpanProcessor(ConsoleSpanExporter()) - ) - - grpc_server_instrumentor = GrpcInstrumentorServer() - grpc_server_instrumentor.instrument() - - class Greeter(helloworld_pb2_grpc.GreeterServicer): - def SayHello(self, request, context): - return helloworld_pb2.HelloReply(message="Hello, %s!" % request.name) - - - def serve(): - - server = grpc.server(futures.ThreadPoolExecutor()) - - helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) - server.add_insecure_port("[::]:50051") - server.start() - server.wait_for_termination() - - - if __name__ == "__main__": - logging.basicConfig() - serve() - -You can also add the instrumentor manually, rather than using -:py:class:`~opentelemetry.instrumentation.grpc.GrpcInstrumentorServer`: - -.. code-block:: python - - from opentelemetry.instrumentation.grpc import server_interceptor - - server = grpc.server(futures.ThreadPoolExecutor(), - interceptors = [server_interceptor()]) - -""" -from functools import partial - -import grpc -from wrapt import wrap_function_wrapper as _wrap - -from opentelemetry import trace -from opentelemetry.instrumentation.grpc.grpcext import intercept_channel -from opentelemetry.instrumentation.grpc.version import __version__ -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.utils import unwrap - -# pylint:disable=import-outside-toplevel -# pylint:disable=import-self -# pylint:disable=unused-argument -# isort:skip - - -class GrpcInstrumentorServer(BaseInstrumentor): - """ - Globally instrument the grpc server. - - Usage:: - - grpc_server_instrumentor = GrpcInstrumentorServer() - grpc_server_instrumentor.instrument() - - """ - - # pylint:disable=attribute-defined-outside-init - - def _instrument(self, **kwargs): - self._original_func = grpc.server - - def server(*args, **kwargs): - if "interceptors" in kwargs: - # add our interceptor as the first - kwargs["interceptors"].insert(0, server_interceptor()) - else: - kwargs["interceptors"] = [server_interceptor()] - return self._original_func(*args, **kwargs) - - grpc.server = server - - def _uninstrument(self, **kwargs): - grpc.server = self._original_func - - -class GrpcInstrumentorClient(BaseInstrumentor): - def _instrument(self, **kwargs): - exporter = kwargs.get("exporter", None) - interval = kwargs.get("interval", 30) - if kwargs.get("channel_type") == "secure": - _wrap( - "grpc", - "secure_channel", - partial(self.wrapper_fn, exporter, interval), - ) - - else: - _wrap( - "grpc", - "insecure_channel", - partial(self.wrapper_fn, exporter, interval), - ) - - def _uninstrument(self, **kwargs): - if kwargs.get("channel_type") == "secure": - unwrap(grpc, "secure_channel") - - else: - unwrap(grpc, "insecure_channel") - - def wrapper_fn( - self, exporter, interval, original_func, instance, args, kwargs - ): - channel = original_func(*args, **kwargs) - tracer_provider = kwargs.get("tracer_provider") - return intercept_channel( - channel, - client_interceptor( - tracer_provider=tracer_provider, - exporter=exporter, - interval=interval, - ), - ) - - -def client_interceptor(tracer_provider=None, exporter=None, interval=30): - """Create a gRPC client channel interceptor. - - Args: - tracer: The tracer to use to create client-side spans. - exporter: The exporter that will receive client metrics - interval: Time between every export call - - Returns: - An invocation-side interceptor object. - """ - from . import _client - - tracer = trace.get_tracer(__name__, __version__, tracer_provider) - - return _client.OpenTelemetryClientInterceptor(tracer, exporter, interval) - - -def server_interceptor(tracer_provider=None): - """Create a gRPC server interceptor. - - Args: - tracer: The tracer to use to create server-side spans. - - Returns: - A service-side interceptor object. - """ - from . import _server - - tracer = trace.get_tracer(__name__, __version__, tracer_provider) - - return _server.OpenTelemetryServerInterceptor(tracer) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py deleted file mode 100644 index f8a72931f90..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pylint:disable=relative-beyond-top-level -# pylint:disable=arguments-differ -# pylint:disable=no-member -# pylint:disable=signature-differs - -"""Implementation of the invocation-side open-telemetry interceptor.""" - -from collections import OrderedDict -from typing import MutableMapping - -import grpc - -from opentelemetry import metrics, propagators, trace -from opentelemetry.sdk.metrics.export.controller import PushController -from opentelemetry.trace.status import Status, StatusCode - -from . import grpcext -from ._utilities import RpcInfo, TimedMetricRecorder - - -class _GuardedSpan: - def __init__(self, span): - self.span = span - self.generated_span = None - self._engaged = True - - def __enter__(self): - self.generated_span = self.span.__enter__() - return self - - def __exit__(self, *args, **kwargs): - if self._engaged: - self.generated_span = None - return self.span.__exit__(*args, **kwargs) - return False - - def release(self): - self._engaged = False - return self.span - - -def _inject_span_context(metadata: MutableMapping[str, str]) -> None: - # pylint:disable=unused-argument - def append_metadata( - carrier: MutableMapping[str, str], key: str, value: str - ): - metadata[key] = value - - # Inject current active span from the context - propagators.inject(append_metadata, metadata) - - -def _make_future_done_callback(span, rpc_info, client_info, metrics_recorder): - def callback(response_future): - with span: - code = response_future.code() - if code != grpc.StatusCode.OK: - rpc_info.error = code - return - response = response_future.result() - rpc_info.response = response - if "ByteSize" in dir(response): - metrics_recorder.record_bytes_in( - response.ByteSize(), client_info.full_method - ) - - return callback - - -class OpenTelemetryClientInterceptor( - grpcext.UnaryClientInterceptor, grpcext.StreamClientInterceptor -): - def __init__(self, tracer, exporter, interval): - self._tracer = tracer - - self._meter = None - if exporter and interval: - self._meter = metrics.get_meter(__name__) - self.controller = PushController( - meter=self._meter, exporter=exporter, interval=interval - ) - self._metrics_recorder = TimedMetricRecorder(self._meter, "client") - - def _start_span(self, method): - return self._tracer.start_as_current_span( - name=method, kind=trace.SpanKind.CLIENT - ) - - # pylint:disable=no-self-use - def _trace_result(self, guarded_span, rpc_info, result, client_info): - # If the RPC is called asynchronously, release the guard and add a - # callback so that the span can be finished once the future is done. - if isinstance(result, grpc.Future): - result.add_done_callback( - _make_future_done_callback( - guarded_span.release(), - rpc_info, - client_info, - self._metrics_recorder, - ) - ) - return result - response = result - # Handle the case when the RPC is initiated via the with_call - # method and the result is a tuple with the first element as the - # response. - # http://www.grpc.io/grpc/python/grpc.html#grpc.UnaryUnaryMultiCallable.with_call - if isinstance(result, tuple): - response = result[0] - rpc_info.response = response - - if "ByteSize" in dir(response): - self._metrics_recorder.record_bytes_in( - response.ByteSize(), client_info.full_method - ) - return result - - def _start_guarded_span(self, *args, **kwargs): - return _GuardedSpan(self._start_span(*args, **kwargs)) - - def _bytes_out_iterator_wrapper(self, iterator, client_info): - for request in iterator: - if "ByteSize" in dir(request): - self._metrics_recorder.record_bytes_out( - request.ByteSize(), client_info.full_method - ) - yield request - - def intercept_unary(self, request, metadata, client_info, invoker): - if not metadata: - mutable_metadata = OrderedDict() - else: - mutable_metadata = OrderedDict(metadata) - - with self._start_guarded_span(client_info.full_method) as guarded_span: - with self._metrics_recorder.record_latency( - client_info.full_method - ): - _inject_span_context(mutable_metadata) - metadata = tuple(mutable_metadata.items()) - - # If protobuf is used, we can record the bytes in/out. Otherwise, we have no way - # to get the size of the request/response properly, so don't record anything - if "ByteSize" in dir(request): - self._metrics_recorder.record_bytes_out( - request.ByteSize(), client_info.full_method - ) - - rpc_info = RpcInfo( - full_method=client_info.full_method, - metadata=metadata, - timeout=client_info.timeout, - request=request, - ) - - try: - result = invoker(request, metadata) - except grpc.RpcError: - guarded_span.generated_span.set_status( - Status(StatusCode.ERROR) - ) - raise - - return self._trace_result( - guarded_span, rpc_info, result, client_info - ) - - # For RPCs that stream responses, the result can be a generator. To record - # the span across the generated responses and detect any errors, we wrap - # the result in a new generator that yields the response values. - def _intercept_server_stream( - self, request_or_iterator, metadata, client_info, invoker - ): - if not metadata: - mutable_metadata = OrderedDict() - else: - mutable_metadata = OrderedDict(metadata) - - with self._start_span(client_info.full_method) as span: - with self._metrics_recorder.record_latency( - client_info.full_method - ): - _inject_span_context(mutable_metadata) - metadata = tuple(mutable_metadata.items()) - rpc_info = RpcInfo( - full_method=client_info.full_method, - metadata=metadata, - timeout=client_info.timeout, - ) - - if client_info.is_client_stream: - rpc_info.request = request_or_iterator - request_or_iterator = self._bytes_out_iterator_wrapper( - request_or_iterator, client_info - ) - else: - if "ByteSize" in dir(request_or_iterator): - self._metrics_recorder.record_bytes_out( - request_or_iterator.ByteSize(), - client_info.full_method, - ) - - try: - result = invoker(request_or_iterator, metadata) - - # Rewrap the result stream into a generator, and record the bytes received - for response in result: - if "ByteSize" in dir(response): - self._metrics_recorder.record_bytes_in( - response.ByteSize(), client_info.full_method - ) - yield response - except grpc.RpcError: - span.set_status(Status(StatusCode.ERROR)) - raise - - def intercept_stream( - self, request_or_iterator, metadata, client_info, invoker - ): - if client_info.is_server_stream: - return self._intercept_server_stream( - request_or_iterator, metadata, client_info, invoker - ) - - if not metadata: - mutable_metadata = OrderedDict() - else: - mutable_metadata = OrderedDict(metadata) - - with self._start_guarded_span(client_info.full_method) as guarded_span: - with self._metrics_recorder.record_latency( - client_info.full_method - ): - _inject_span_context(mutable_metadata) - metadata = tuple(mutable_metadata.items()) - rpc_info = RpcInfo( - full_method=client_info.full_method, - metadata=metadata, - timeout=client_info.timeout, - request=request_or_iterator, - ) - - rpc_info.request = request_or_iterator - - request_or_iterator = self._bytes_out_iterator_wrapper( - request_or_iterator, client_info - ) - - try: - result = invoker(request_or_iterator, metadata) - except grpc.RpcError: - guarded_span.generated_span.set_status( - Status(StatusCode.ERROR) - ) - raise - - return self._trace_result( - guarded_span, rpc_info, result, client_info - ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_server.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_server.py deleted file mode 100644 index 087cf4f9ccb..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_server.py +++ /dev/null @@ -1,246 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pylint:disable=relative-beyond-top-level -# pylint:disable=arguments-differ -# pylint:disable=no-member -# pylint:disable=signature-differs - -""" -Implementation of the service-side open-telemetry interceptor. -""" - -import logging -from contextlib import contextmanager - -import grpc - -from opentelemetry import propagators, trace -from opentelemetry.context import attach, detach -from opentelemetry.trace.propagation.textmap import DictGetter -from opentelemetry.trace.status import Status, StatusCode - -logger = logging.getLogger(__name__) - - -# wrap an RPC call -# see https://github.com/grpc/grpc/issues/18191 -def _wrap_rpc_behavior(handler, continuation): - if handler is None: - return None - - if handler.request_streaming and handler.response_streaming: - behavior_fn = handler.stream_stream - handler_factory = grpc.stream_stream_rpc_method_handler - elif handler.request_streaming and not handler.response_streaming: - behavior_fn = handler.stream_unary - handler_factory = grpc.stream_unary_rpc_method_handler - elif not handler.request_streaming and handler.response_streaming: - behavior_fn = handler.unary_stream - handler_factory = grpc.unary_stream_rpc_method_handler - else: - behavior_fn = handler.unary_unary - handler_factory = grpc.unary_unary_rpc_method_handler - - return handler_factory( - continuation( - behavior_fn, handler.request_streaming, handler.response_streaming - ), - request_deserializer=handler.request_deserializer, - response_serializer=handler.response_serializer, - ) - - -# pylint:disable=abstract-method -class _OpenTelemetryServicerContext(grpc.ServicerContext): - def __init__(self, servicer_context, active_span): - self._servicer_context = servicer_context - self._active_span = active_span - self.code = grpc.StatusCode.OK - self.details = None - super().__init__() - - def is_active(self, *args, **kwargs): - return self._servicer_context.is_active(*args, **kwargs) - - def time_remaining(self, *args, **kwargs): - return self._servicer_context.time_remaining(*args, **kwargs) - - def cancel(self, *args, **kwargs): - return self._servicer_context.cancel(*args, **kwargs) - - def add_callback(self, *args, **kwargs): - return self._servicer_context.add_callback(*args, **kwargs) - - def disable_next_message_compression(self): - return self._service_context.disable_next_message_compression() - - def invocation_metadata(self, *args, **kwargs): - return self._servicer_context.invocation_metadata(*args, **kwargs) - - def peer(self): - return self._servicer_context.peer() - - def peer_identities(self): - return self._servicer_context.peer_identities() - - def peer_identity_key(self): - return self._servicer_context.peer_identity_key() - - def auth_context(self): - return self._servicer_context.auth_context() - - def set_compression(self, compression): - return self._servicer_context.set_compression(compression) - - def send_initial_metadata(self, *args, **kwargs): - return self._servicer_context.send_initial_metadata(*args, **kwargs) - - def set_trailing_metadata(self, *args, **kwargs): - return self._servicer_context.set_trailing_metadata(*args, **kwargs) - - def abort(self, code, details): - self.code = code - self.details = details - self._active_span.set_attribute("rpc.grpc.status_code", code.name) - self._active_span.set_status( - Status(status_code=StatusCode.ERROR, description=details) - ) - return self._servicer_context.abort(code, details) - - def abort_with_status(self, status): - return self._servicer_context.abort_with_status(status) - - def set_code(self, code): - self.code = code - # use details if we already have it, otherwise the status description - details = self.details or code.value[1] - self._active_span.set_attribute("rpc.grpc.status_code", code.name) - self._active_span.set_status( - Status(status_code=StatusCode.ERROR, description=details) - ) - return self._servicer_context.set_code(code) - - def set_details(self, details): - self.details = details - self._active_span.set_status( - Status(status_code=StatusCode.ERROR, description=details) - ) - return self._servicer_context.set_details(details) - - -# pylint:disable=abstract-method -# pylint:disable=no-self-use -# pylint:disable=unused-argument -class OpenTelemetryServerInterceptor(grpc.ServerInterceptor): - """ - A gRPC server interceptor, to add OpenTelemetry. - - Usage:: - - tracer = some OpenTelemetry tracer - - interceptors = [ - OpenTelemetryServerInterceptor(tracer), - ] - - server = grpc.server( - futures.ThreadPoolExecutor(max_workers=concurrency), - interceptors = interceptors) - - """ - - def __init__(self, tracer): - self._tracer = tracer - self._carrier_getter = DictGetter() - - @contextmanager - def _set_remote_context(self, servicer_context): - metadata = servicer_context.invocation_metadata() - if metadata: - md_dict = {md.key: md.value for md in metadata} - ctx = propagators.extract(self._carrier_getter, md_dict) - token = attach(ctx) - try: - yield - finally: - detach(token) - else: - yield - - def _start_span(self, handler_call_details, context): - - attributes = { - "rpc.method": handler_call_details.method, - "rpc.system": "grpc", - "rpc.grpc.status_code": grpc.StatusCode.OK, - } - - metadata = dict(context.invocation_metadata()) - if "user-agent" in metadata: - attributes["rpc.user_agent"] = metadata["user-agent"] - - # Split up the peer to keep with how other telemetry sources - # do it. This looks like: - # * ipv6:[::1]:57284 - # * ipv4:127.0.0.1:57284 - # * ipv4:10.2.1.1:57284,127.0.0.1:57284 - # - try: - host, port = ( - context.peer().split(",")[0].split(":", 1)[1].rsplit(":", 1) - ) - - # other telemetry sources convert this, so we will too - if host in ("[::1]", "127.0.0.1"): - host = "localhost" - - attributes.update({"net.peer.name": host, "net.peer.port": port}) - except IndexError: - logger.warning("Failed to parse peer address '%s'", context.peer()) - - return self._tracer.start_as_current_span( - name=handler_call_details.method, - kind=trace.SpanKind.SERVER, - attributes=attributes, - ) - - def intercept_service(self, continuation, handler_call_details): - def telemetry_wrapper(behavior, request_streaming, response_streaming): - def telemetry_interceptor(request_or_iterator, context): - - with self._set_remote_context(context): - with self._start_span( - handler_call_details, context - ) as span: - # wrap the context - context = _OpenTelemetryServicerContext(context, span) - - # And now we run the actual RPC. - try: - return behavior(request_or_iterator, context) - except Exception as error: - # Bare exceptions are likely to be gRPC aborts, which - # we handle in our context wrapper. - # Here, we're interested in uncaught exceptions. - # pylint:disable=unidiomatic-typecheck - if type(error) != Exception: - span.record_exception(error) - raise error - - return telemetry_interceptor - - return _wrap_rpc_behavior( - continuation(handler_call_details), telemetry_wrapper - ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_utilities.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_utilities.py deleted file mode 100644 index 8cf1f957c31..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_utilities.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Internal utilities.""" - -from contextlib import contextmanager -from time import time - -import grpc - - -class RpcInfo: - def __init__( - self, - full_method=None, - metadata=None, - timeout=None, - request=None, - response=None, - error=None, - ): - self.full_method = full_method - self.metadata = metadata - self.timeout = timeout - self.request = request - self.response = response - self.error = error - - -class TimedMetricRecorder: - def __init__(self, meter, span_kind): - self._meter = meter - service_name = "grpcio" - self._span_kind = span_kind - - if self._meter: - self._duration = self._meter.create_valuerecorder( - name="{}/{}/duration".format(service_name, span_kind), - description="Duration of grpc requests to the server", - unit="ms", - value_type=float, - ) - self._error_count = self._meter.create_counter( - name="{}/{}/errors".format(service_name, span_kind), - description="Number of errors that were returned from the server", - unit="1", - value_type=int, - ) - self._bytes_in = self._meter.create_counter( - name="{}/{}/bytes_in".format(service_name, span_kind), - description="Number of bytes received from the server", - unit="by", - value_type=int, - ) - self._bytes_out = self._meter.create_counter( - name="{}/{}/bytes_out".format(service_name, span_kind), - description="Number of bytes sent out through gRPC", - unit="by", - value_type=int, - ) - - def record_bytes_in(self, bytes_in, method): - if self._meter: - labels = {"method": method} - self._bytes_in.add(bytes_in, labels) - - def record_bytes_out(self, bytes_out, method): - if self._meter: - labels = {"method": method} - self._bytes_out.add(bytes_out, labels) - - @contextmanager - def record_latency(self, method): - start_time = time() - labels = {"method": method, "status_code": grpc.StatusCode.OK} - try: - yield labels - except grpc.RpcError as exc: - if self._meter: - # pylint: disable=no-member - labels["status_code"] = exc.code() - self._error_count.add(1, labels) - labels["error"] = True - raise - finally: - if self._meter: - if "error" not in labels: - labels["error"] = False - elapsed_time = (time() - start_time) * 1000 - self._duration.record(elapsed_time, labels) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/__init__.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/__init__.py deleted file mode 100644 index d5e2549bab8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/__init__.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pylint:disable=import-outside-toplevel -# pylint:disable=import-self -# pylint:disable=no-name-in-module - -import abc - - -class UnaryClientInfo(abc.ABC): - """Consists of various information about a unary RPC on the - invocation-side. - - Attributes: - full_method: A string of the full RPC method, i.e., - /package.service/method. - timeout: The length of time in seconds to wait for the computation to - terminate or be cancelled, or None if this method should block until - the computation is terminated or is cancelled no matter how long that - takes. - """ - - -class StreamClientInfo(abc.ABC): - """Consists of various information about a stream RPC on the - invocation-side. - - Attributes: - full_method: A string of the full RPC method, i.e., - /package.service/method. - is_client_stream: Indicates whether the RPC is client-streaming. - is_server_stream: Indicates whether the RPC is server-streaming. - timeout: The length of time in seconds to wait for the computation to - terminate or be cancelled, or None if this method should block until - the computation is terminated or is cancelled no matter how long that - takes. - """ - - -class UnaryClientInterceptor(abc.ABC): - """Affords intercepting unary-unary RPCs on the invocation-side.""" - - @abc.abstractmethod - def intercept_unary(self, request, metadata, client_info, invoker): - """Intercepts unary-unary RPCs on the invocation-side. - - Args: - request: The request value for the RPC. - metadata: Optional :term:`metadata` to be transmitted to the - service-side of the RPC. - client_info: A UnaryClientInfo containing various information about - the RPC. - invoker: The handler to complete the RPC on the client. It is the - interceptor's responsibility to call it. - - Returns: - The result from calling invoker(request, metadata). - """ - raise NotImplementedError() - - -class StreamClientInterceptor(abc.ABC): - """Affords intercepting stream RPCs on the invocation-side.""" - - @abc.abstractmethod - def intercept_stream( - self, request_or_iterator, metadata, client_info, invoker - ): - """Intercepts stream RPCs on the invocation-side. - - Args: - request_or_iterator: The request value for the RPC if - `client_info.is_client_stream` is `false`; otherwise, an iterator of - request values. - metadata: Optional :term:`metadata` to be transmitted to the service-side - of the RPC. - client_info: A StreamClientInfo containing various information about - the RPC. - invoker: The handler to complete the RPC on the client. It is the - interceptor's responsibility to call it. - - Returns: - The result from calling invoker(metadata). - """ - raise NotImplementedError() - - -def intercept_channel(channel, *interceptors): - """Creates an intercepted channel. - - Args: - channel: A Channel. - interceptors: Zero or more UnaryClientInterceptors or - StreamClientInterceptors - - Returns: - A Channel. - - Raises: - TypeError: If an interceptor derives from neither UnaryClientInterceptor - nor StreamClientInterceptor. - """ - from . import _interceptor - - return _interceptor.intercept_channel(channel, *interceptors) - - -__all__ = ( - "UnaryClientInterceptor", - "StreamClientInfo", - "StreamClientInterceptor", - "intercept_channel", -) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py deleted file mode 100644 index b9f74fff805..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py +++ /dev/null @@ -1,254 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pylint:disable=relative-beyond-top-level -# pylint:disable=arguments-differ -# pylint:disable=no-member -# pylint:disable=signature-differs - -"""Implementation of gRPC Python interceptors.""" - - -import collections - -import grpc - -from .. import grpcext - - -class _UnaryClientInfo( - collections.namedtuple("_UnaryClientInfo", ("full_method", "timeout")) -): - pass - - -class _StreamClientInfo( - collections.namedtuple( - "_StreamClientInfo", - ("full_method", "is_client_stream", "is_server_stream", "timeout"), - ) -): - pass - - -class _InterceptorUnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable): - def __init__(self, method, base_callable, interceptor): - self._method = method - self._base_callable = base_callable - self._interceptor = interceptor - - def __call__(self, request, timeout=None, metadata=None, credentials=None): - def invoker(request, metadata): - return self._base_callable(request, timeout, metadata, credentials) - - client_info = _UnaryClientInfo(self._method, timeout) - return self._interceptor.intercept_unary( - request, metadata, client_info, invoker - ) - - def with_call( - self, request, timeout=None, metadata=None, credentials=None - ): - def invoker(request, metadata): - return self._base_callable.with_call( - request, timeout, metadata, credentials - ) - - client_info = _UnaryClientInfo(self._method, timeout) - return self._interceptor.intercept_unary( - request, metadata, client_info, invoker - ) - - def future(self, request, timeout=None, metadata=None, credentials=None): - def invoker(request, metadata): - return self._base_callable.future( - request, timeout, metadata, credentials - ) - - client_info = _UnaryClientInfo(self._method, timeout) - return self._interceptor.intercept_unary( - request, metadata, client_info, invoker - ) - - -class _InterceptorUnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable): - def __init__(self, method, base_callable, interceptor): - self._method = method - self._base_callable = base_callable - self._interceptor = interceptor - - def __call__(self, request, timeout=None, metadata=None, credentials=None): - def invoker(request, metadata): - return self._base_callable(request, timeout, metadata, credentials) - - client_info = _StreamClientInfo(self._method, False, True, timeout) - return self._interceptor.intercept_stream( - request, metadata, client_info, invoker - ) - - -class _InterceptorStreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable): - def __init__(self, method, base_callable, interceptor): - self._method = method - self._base_callable = base_callable - self._interceptor = interceptor - - def __call__( - self, request_iterator, timeout=None, metadata=None, credentials=None - ): - def invoker(request_iterator, metadata): - return self._base_callable( - request_iterator, timeout, metadata, credentials - ) - - client_info = _StreamClientInfo(self._method, True, False, timeout) - return self._interceptor.intercept_stream( - request_iterator, metadata, client_info, invoker - ) - - def with_call( - self, request_iterator, timeout=None, metadata=None, credentials=None - ): - def invoker(request_iterator, metadata): - return self._base_callable.with_call( - request_iterator, timeout, metadata, credentials - ) - - client_info = _StreamClientInfo(self._method, True, False, timeout) - return self._interceptor.intercept_stream( - request_iterator, metadata, client_info, invoker - ) - - def future( - self, request_iterator, timeout=None, metadata=None, credentials=None - ): - def invoker(request_iterator, metadata): - return self._base_callable.future( - request_iterator, timeout, metadata, credentials - ) - - client_info = _StreamClientInfo(self._method, True, False, timeout) - return self._interceptor.intercept_stream( - request_iterator, metadata, client_info, invoker - ) - - -class _InterceptorStreamStreamMultiCallable(grpc.StreamStreamMultiCallable): - def __init__(self, method, base_callable, interceptor): - self._method = method - self._base_callable = base_callable - self._interceptor = interceptor - - def __call__( - self, request_iterator, timeout=None, metadata=None, credentials=None - ): - def invoker(request_iterator, metadata): - return self._base_callable( - request_iterator, timeout, metadata, credentials - ) - - client_info = _StreamClientInfo(self._method, True, True, timeout) - return self._interceptor.intercept_stream( - request_iterator, metadata, client_info, invoker - ) - - -class _InterceptorChannel(grpc.Channel): - def __init__(self, channel, interceptor): - self._channel = channel - self._interceptor = interceptor - - def subscribe(self, *args, **kwargs): - self._channel.subscribe(*args, **kwargs) - - def unsubscribe(self, *args, **kwargs): - self._channel.unsubscribe(*args, **kwargs) - - def unary_unary( - self, method, request_serializer=None, response_deserializer=None - ): - base_callable = self._channel.unary_unary( - method, request_serializer, response_deserializer - ) - if isinstance(self._interceptor, grpcext.UnaryClientInterceptor): - return _InterceptorUnaryUnaryMultiCallable( - method, base_callable, self._interceptor - ) - return base_callable - - def unary_stream( - self, method, request_serializer=None, response_deserializer=None - ): - base_callable = self._channel.unary_stream( - method, request_serializer, response_deserializer - ) - if isinstance(self._interceptor, grpcext.StreamClientInterceptor): - return _InterceptorUnaryStreamMultiCallable( - method, base_callable, self._interceptor - ) - return base_callable - - def stream_unary( - self, method, request_serializer=None, response_deserializer=None - ): - base_callable = self._channel.stream_unary( - method, request_serializer, response_deserializer - ) - if isinstance(self._interceptor, grpcext.StreamClientInterceptor): - return _InterceptorStreamUnaryMultiCallable( - method, base_callable, self._interceptor - ) - return base_callable - - def stream_stream( - self, method, request_serializer=None, response_deserializer=None - ): - base_callable = self._channel.stream_stream( - method, request_serializer, response_deserializer - ) - if isinstance(self._interceptor, grpcext.StreamClientInterceptor): - return _InterceptorStreamStreamMultiCallable( - method, base_callable, self._interceptor - ) - return base_callable - - def close(self): - if not hasattr(self._channel, "close"): - raise RuntimeError( - "close() is not supported with the installed version of grpcio" - ) - self._channel.close() - - def __enter__(self): - """Enters the runtime context related to the channel object.""" - raise NotImplementedError() - - def __exit__(self, exc_type, exc_val, exc_tb): - """Exits the runtime context related to the channel object.""" - raise NotImplementedError() - - -def intercept_channel(channel, *interceptors): - result = channel - for interceptor in interceptors: - if not isinstance( - interceptor, grpcext.UnaryClientInterceptor - ) and not isinstance(interceptor, grpcext.StreamClientInterceptor): - raise TypeError( - "interceptor must be either a " - "grpcext.UnaryClientInterceptor or a " - "grpcext.StreamClientInterceptor" - ) - result = _InterceptorChannel(result, interceptor) - return result diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/version.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/__init__.py deleted file mode 100644 index b0a6f428417..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/_client.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/_client.py deleted file mode 100644 index 43310b5f651..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/_client.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .protobuf.test_server_pb2 import Request - -CLIENT_ID = 1 - - -def simple_method(stub, error=False): - request = Request( - client_id=CLIENT_ID, request_data="error" if error else "data" - ) - stub.SimpleMethod(request) - - -def client_streaming_method(stub, error=False): - # create a generator - def request_messages(): - for _ in range(5): - request = Request( - client_id=CLIENT_ID, request_data="error" if error else "data" - ) - yield request - - stub.ClientStreamingMethod(request_messages()) - - -def server_streaming_method(stub, error=False): - request = Request( - client_id=CLIENT_ID, request_data="error" if error else "data" - ) - response_iterator = stub.ServerStreamingMethod(request) - list(response_iterator) - - -def bidirectional_streaming_method(stub, error=False): - def request_messages(): - for _ in range(5): - request = Request( - client_id=CLIENT_ID, request_data="error" if error else "data" - ) - yield request - - response_iterator = stub.BidirectionalStreamingMethod(request_messages()) - - list(response_iterator) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/_server.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/_server.py deleted file mode 100644 index a4e1c266b8a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/_server.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from concurrent import futures - -import grpc - -from .protobuf import test_server_pb2, test_server_pb2_grpc - -SERVER_ID = 1 - - -class TestServer(test_server_pb2_grpc.GRPCTestServerServicer): - def SimpleMethod(self, request, context): - if request.request_data == "error": - context.set_code(grpc.StatusCode.INVALID_ARGUMENT) - return test_server_pb2.Response() - response = test_server_pb2.Response( - server_id=SERVER_ID, response_data="data" - ) - return response - - def ClientStreamingMethod(self, request_iterator, context): - data = list(request_iterator) - if data[0].request_data == "error": - context.set_code(grpc.StatusCode.INVALID_ARGUMENT) - return test_server_pb2.Response() - response = test_server_pb2.Response( - server_id=SERVER_ID, response_data="data" - ) - return response - - def ServerStreamingMethod(self, request, context): - if request.request_data == "error": - - context.abort( - code=grpc.StatusCode.INVALID_ARGUMENT, - details="server stream error", - ) - return test_server_pb2.Response() - - # create a generator - def response_messages(): - for _ in range(5): - response = test_server_pb2.Response( - server_id=SERVER_ID, response_data="data" - ) - yield response - - return response_messages() - - def BidirectionalStreamingMethod(self, request_iterator, context): - data = list(request_iterator) - if data[0].request_data == "error": - context.abort( - code=grpc.StatusCode.INVALID_ARGUMENT, - details="bidirectional error", - ) - return - - for _ in range(5): - yield test_server_pb2.Response( - server_id=SERVER_ID, response_data="data" - ) - - -def create_test_server(port): - server = grpc.server(futures.ThreadPoolExecutor(max_workers=1)) - - test_server_pb2_grpc.add_GRPCTestServerServicer_to_server( - TestServer(), server - ) - - server.add_insecure_port("localhost:{}".format(port)) - - return server diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server.proto b/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server.proto deleted file mode 100644 index 790a7675de0..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server.proto +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019 gRPC 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. -syntax = "proto3"; - -message Request { - int64 client_id = 1; - string request_data = 2; -} - -message Response { - int64 server_id = 1; - string response_data = 2; -} - -service GRPCTestServer { - rpc SimpleMethod (Request) returns (Response); - - rpc ClientStreamingMethod (stream Request) returns (Response); - - rpc ServerStreamingMethod (Request) returns (stream Response); - - rpc BidirectionalStreamingMethod (stream Request) returns (stream Response); -} diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server_pb2.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server_pb2.py deleted file mode 100644 index 735206f8505..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server_pb2.py +++ /dev/null @@ -1,215 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: test_server.proto - -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor.FileDescriptor( - name="test_server.proto", - package="", - syntax="proto3", - serialized_options=None, - serialized_pb=b'\n\x11test_server.proto"2\n\x07Request\x12\x11\n\tclient_id\x18\x01 \x01(\x03\x12\x14\n\x0crequest_data\x18\x02 \x01(\t"4\n\x08Response\x12\x11\n\tserver_id\x18\x01 \x01(\x03\x12\x15\n\rresponse_data\x18\x02 \x01(\t2\xce\x01\n\x0eGRPCTestServer\x12#\n\x0cSimpleMethod\x12\x08.Request\x1a\t.Response\x12.\n\x15\x43lientStreamingMethod\x12\x08.Request\x1a\t.Response(\x01\x12.\n\x15ServerStreamingMethod\x12\x08.Request\x1a\t.Response0\x01\x12\x37\n\x1c\x42idirectionalStreamingMethod\x12\x08.Request\x1a\t.Response(\x01\x30\x01\x62\x06proto3', -) - - -_REQUEST = _descriptor.Descriptor( - name="Request", - full_name="Request", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="client_id", - full_name="Request.client_id", - index=0, - number=1, - type=3, - cpp_type=2, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="request_data", - full_name="Request.request_data", - index=1, - number=2, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=21, - serialized_end=71, -) - - -_RESPONSE = _descriptor.Descriptor( - name="Response", - full_name="Response", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="server_id", - full_name="Response.server_id", - index=0, - number=1, - type=3, - cpp_type=2, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="response_data", - full_name="Response.response_data", - index=1, - number=2, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=73, - serialized_end=125, -) - -DESCRIPTOR.message_types_by_name["Request"] = _REQUEST -DESCRIPTOR.message_types_by_name["Response"] = _RESPONSE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -Request = _reflection.GeneratedProtocolMessageType( - "Request", - (_message.Message,), - { - "DESCRIPTOR": _REQUEST, - "__module__": "test_server_pb2" - # @@protoc_insertion_point(class_scope:Request) - }, -) -_sym_db.RegisterMessage(Request) - -Response = _reflection.GeneratedProtocolMessageType( - "Response", - (_message.Message,), - { - "DESCRIPTOR": _RESPONSE, - "__module__": "test_server_pb2" - # @@protoc_insertion_point(class_scope:Response) - }, -) -_sym_db.RegisterMessage(Response) - - -_GRPCTESTSERVER = _descriptor.ServiceDescriptor( - name="GRPCTestServer", - full_name="GRPCTestServer", - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=128, - serialized_end=334, - methods=[ - _descriptor.MethodDescriptor( - name="SimpleMethod", - full_name="GRPCTestServer.SimpleMethod", - index=0, - containing_service=None, - input_type=_REQUEST, - output_type=_RESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name="ClientStreamingMethod", - full_name="GRPCTestServer.ClientStreamingMethod", - index=1, - containing_service=None, - input_type=_REQUEST, - output_type=_RESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name="ServerStreamingMethod", - full_name="GRPCTestServer.ServerStreamingMethod", - index=2, - containing_service=None, - input_type=_REQUEST, - output_type=_RESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name="BidirectionalStreamingMethod", - full_name="GRPCTestServer.BidirectionalStreamingMethod", - index=3, - containing_service=None, - input_type=_REQUEST, - output_type=_RESPONSE, - serialized_options=None, - ), - ], -) -_sym_db.RegisterServiceDescriptor(_GRPCTESTSERVER) - -DESCRIPTOR.services_by_name["GRPCTestServer"] = _GRPCTESTSERVER - -# @@protoc_insertion_point(module_scope) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server_pb2_grpc.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server_pb2_grpc.py deleted file mode 100644 index d0a6fd5184f..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/protobuf/test_server_pb2_grpc.py +++ /dev/null @@ -1,205 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - -from tests.protobuf import test_server_pb2 as test__server__pb2 - - -class GRPCTestServerStub(object): - """Missing associated documentation comment in .proto file""" - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.SimpleMethod = channel.unary_unary( - "/GRPCTestServer/SimpleMethod", - request_serializer=test__server__pb2.Request.SerializeToString, - response_deserializer=test__server__pb2.Response.FromString, - ) - self.ClientStreamingMethod = channel.stream_unary( - "/GRPCTestServer/ClientStreamingMethod", - request_serializer=test__server__pb2.Request.SerializeToString, - response_deserializer=test__server__pb2.Response.FromString, - ) - self.ServerStreamingMethod = channel.unary_stream( - "/GRPCTestServer/ServerStreamingMethod", - request_serializer=test__server__pb2.Request.SerializeToString, - response_deserializer=test__server__pb2.Response.FromString, - ) - self.BidirectionalStreamingMethod = channel.stream_stream( - "/GRPCTestServer/BidirectionalStreamingMethod", - request_serializer=test__server__pb2.Request.SerializeToString, - response_deserializer=test__server__pb2.Response.FromString, - ) - - -class GRPCTestServerServicer(object): - """Missing associated documentation comment in .proto file""" - - def SimpleMethod(self, request, context): - """Missing associated documentation comment in .proto file""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def ClientStreamingMethod(self, request_iterator, context): - """Missing associated documentation comment in .proto file""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def ServerStreamingMethod(self, request, context): - """Missing associated documentation comment in .proto file""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def BidirectionalStreamingMethod(self, request_iterator, context): - """Missing associated documentation comment in .proto file""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - -def add_GRPCTestServerServicer_to_server(servicer, server): - rpc_method_handlers = { - "SimpleMethod": grpc.unary_unary_rpc_method_handler( - servicer.SimpleMethod, - request_deserializer=test__server__pb2.Request.FromString, - response_serializer=test__server__pb2.Response.SerializeToString, - ), - "ClientStreamingMethod": grpc.stream_unary_rpc_method_handler( - servicer.ClientStreamingMethod, - request_deserializer=test__server__pb2.Request.FromString, - response_serializer=test__server__pb2.Response.SerializeToString, - ), - "ServerStreamingMethod": grpc.unary_stream_rpc_method_handler( - servicer.ServerStreamingMethod, - request_deserializer=test__server__pb2.Request.FromString, - response_serializer=test__server__pb2.Response.SerializeToString, - ), - "BidirectionalStreamingMethod": grpc.stream_stream_rpc_method_handler( - servicer.BidirectionalStreamingMethod, - request_deserializer=test__server__pb2.Request.FromString, - response_serializer=test__server__pb2.Response.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - "GRPCTestServer", rpc_method_handlers - ) - server.add_generic_rpc_handlers((generic_handler,)) - - -# This class is part of an EXPERIMENTAL API. -class GRPCTestServer(object): - """Missing associated documentation comment in .proto file""" - - @staticmethod - def SimpleMethod( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/GRPCTestServer/SimpleMethod", - test__server__pb2.Request.SerializeToString, - test__server__pb2.Response.FromString, - options, - channel_credentials, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - ) - - @staticmethod - def ClientStreamingMethod( - request_iterator, - target, - options=(), - channel_credentials=None, - call_credentials=None, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.stream_unary( - request_iterator, - target, - "/GRPCTestServer/ClientStreamingMethod", - test__server__pb2.Request.SerializeToString, - test__server__pb2.Response.FromString, - options, - channel_credentials, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - ) - - @staticmethod - def ServerStreamingMethod( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_stream( - request, - target, - "/GRPCTestServer/ServerStreamingMethod", - test__server__pb2.Request.SerializeToString, - test__server__pb2.Response.FromString, - options, - channel_credentials, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - ) - - @staticmethod - def BidirectionalStreamingMethod( - request_iterator, - target, - options=(), - channel_credentials=None, - call_credentials=None, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.stream_stream( - request_iterator, - target, - "/GRPCTestServer/BidirectionalStreamingMethod", - test__server__pb2.Request.SerializeToString, - test__server__pb2.Response.FromString, - options, - channel_credentials, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py deleted file mode 100644 index f351c6fdd46..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py +++ /dev/null @@ -1,295 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import grpc - -import opentelemetry.instrumentation.grpc -from opentelemetry import trace -from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient -from opentelemetry.sdk.metrics.export.aggregate import ( - MinMaxSumCountAggregator, - SumAggregator, -) -from opentelemetry.test.test_base import TestBase -from tests.protobuf import test_server_pb2_grpc - -from ._client import ( - bidirectional_streaming_method, - client_streaming_method, - server_streaming_method, - simple_method, -) -from ._server import create_test_server - - -class TestClientProto(TestBase): - def setUp(self): - super().setUp() - GrpcInstrumentorClient().instrument( - exporter=self.memory_metrics_exporter - ) - self.server = create_test_server(25565) - self.server.start() - self.channel = grpc.insecure_channel("localhost:25565") - self._stub = test_server_pb2_grpc.GRPCTestServerStub(self.channel) - - def tearDown(self): - super().tearDown() - GrpcInstrumentorClient().uninstrument() - self.memory_metrics_exporter.clear() - self.server.stop(None) - self.channel.close() - - def _verify_success_records(self, num_bytes_out, num_bytes_in, method): - # pylint: disable=protected-access,no-member - self.channel._interceptor.controller.tick() - records = self.memory_metrics_exporter.get_exported_metrics() - self.assertEqual(len(records), 3) - - bytes_out = None - bytes_in = None - duration = None - - for record in records: - if record.instrument.name == "grpcio/client/duration": - duration = record - elif record.instrument.name == "grpcio/client/bytes_out": - bytes_out = record - elif record.instrument.name == "grpcio/client/bytes_in": - bytes_in = record - - self.assertIsNotNone(bytes_out) - self.assertEqual(bytes_out.instrument.name, "grpcio/client/bytes_out") - self.assertEqual(bytes_out.labels, (("method", method),)) - - self.assertIsNotNone(bytes_in) - self.assertEqual(bytes_in.instrument.name, "grpcio/client/bytes_in") - self.assertEqual(bytes_in.labels, (("method", method),)) - - self.assertIsNotNone(duration) - self.assertEqual(duration.instrument.name, "grpcio/client/duration") - self.assertEqual( - duration.labels, - ( - ("error", False), - ("method", method), - ("status_code", grpc.StatusCode.OK), - ), - ) - - self.assertEqual(type(bytes_out.aggregator), SumAggregator) - self.assertEqual(type(bytes_in.aggregator), SumAggregator) - self.assertEqual(type(duration.aggregator), MinMaxSumCountAggregator) - - self.assertEqual(bytes_out.aggregator.checkpoint, num_bytes_out) - self.assertEqual(bytes_in.aggregator.checkpoint, num_bytes_in) - - self.assertEqual(duration.aggregator.checkpoint.count, 1) - self.assertGreaterEqual(duration.aggregator.checkpoint.sum, 0) - - def test_unary_unary(self): - simple_method(self._stub) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - - self.assertEqual(span.name, "/GRPCTestServer/SimpleMethod") - self.assertIs(span.kind, trace.SpanKind.CLIENT) - - # Check version and name in span's instrumentation info - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.grpc - ) - - self._verify_success_records(8, 8, "/GRPCTestServer/SimpleMethod") - - def test_unary_stream(self): - server_streaming_method(self._stub) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - - self.assertEqual(span.name, "/GRPCTestServer/ServerStreamingMethod") - self.assertIs(span.kind, trace.SpanKind.CLIENT) - - # Check version and name in span's instrumentation info - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.grpc - ) - - self._verify_success_records( - 8, 40, "/GRPCTestServer/ServerStreamingMethod" - ) - - def test_stream_unary(self): - client_streaming_method(self._stub) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - - self.assertEqual(span.name, "/GRPCTestServer/ClientStreamingMethod") - self.assertIs(span.kind, trace.SpanKind.CLIENT) - - # Check version and name in span's instrumentation info - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.grpc - ) - - self._verify_success_records( - 40, 8, "/GRPCTestServer/ClientStreamingMethod" - ) - - def test_stream_stream(self): - bidirectional_streaming_method(self._stub) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - - self.assertEqual( - span.name, "/GRPCTestServer/BidirectionalStreamingMethod" - ) - self.assertIs(span.kind, trace.SpanKind.CLIENT) - - # Check version and name in span's instrumentation info - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.grpc - ) - - self._verify_success_records( - 40, 40, "/GRPCTestServer/BidirectionalStreamingMethod" - ) - - def _verify_error_records(self, method): - # pylint: disable=protected-access,no-member - self.channel._interceptor.controller.tick() - records = self.memory_metrics_exporter.get_exported_metrics() - self.assertEqual(len(records), 3) - - bytes_out = None - errors = None - duration = None - - for record in records: - if record.instrument.name == "grpcio/client/duration": - duration = record - elif record.instrument.name == "grpcio/client/bytes_out": - bytes_out = record - elif record.instrument.name == "grpcio/client/errors": - errors = record - - self.assertIsNotNone(bytes_out) - self.assertIsNotNone(errors) - self.assertIsNotNone(duration) - - self.assertEqual(errors.instrument.name, "grpcio/client/errors") - self.assertEqual( - errors.labels, - ( - ("method", method), - ("status_code", grpc.StatusCode.INVALID_ARGUMENT), - ), - ) - self.assertEqual(errors.aggregator.checkpoint, 1) - - self.assertEqual( - duration.labels, - ( - ("error", True), - ("method", method), - ("status_code", grpc.StatusCode.INVALID_ARGUMENT), - ), - ) - - def test_error_simple(self): - with self.assertRaises(grpc.RpcError): - simple_method(self._stub, error=True) - - self._verify_error_records("/GRPCTestServer/SimpleMethod") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertIs( - span.status.status_code, trace.status.StatusCode.ERROR, - ) - - def test_error_stream_unary(self): - with self.assertRaises(grpc.RpcError): - client_streaming_method(self._stub, error=True) - - self._verify_error_records("/GRPCTestServer/ClientStreamingMethod") - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertIs( - span.status.status_code, trace.status.StatusCode.ERROR, - ) - - def test_error_unary_stream(self): - with self.assertRaises(grpc.RpcError): - server_streaming_method(self._stub, error=True) - - self._verify_error_records("/GRPCTestServer/ServerStreamingMethod") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertIs( - span.status.status_code, trace.status.StatusCode.ERROR, - ) - - def test_error_stream_stream(self): - with self.assertRaises(grpc.RpcError): - bidirectional_streaming_method(self._stub, error=True) - - self._verify_error_records( - "/GRPCTestServer/BidirectionalStreamingMethod" - ) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertIs( - span.status.status_code, trace.status.StatusCode.ERROR, - ) - - -class TestClientNoMetrics(TestBase): - def setUp(self): - super().setUp() - GrpcInstrumentorClient().instrument() - self.server = create_test_server(25565) - self.server.start() - self.channel = grpc.insecure_channel("localhost:25565") - self._stub = test_server_pb2_grpc.GRPCTestServerStub(self.channel) - - def tearDown(self): - super().tearDown() - GrpcInstrumentorClient().uninstrument() - self.memory_metrics_exporter.clear() - self.server.stop(None) - - def test_unary_unary(self): - simple_method(self._stub) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - - self.assertEqual(span.name, "/GRPCTestServer/SimpleMethod") - self.assertIs(span.kind, trace.SpanKind.CLIENT) - - # Check version and name in span's instrumentation info - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.grpc - ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_server_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_server_interceptor.py deleted file mode 100644 index 13b535d8414..00000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_server_interceptor.py +++ /dev/null @@ -1,295 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pylint:disable=unused-argument -# pylint:disable=no-self-use - -import threading -from concurrent import futures - -import grpc - -import opentelemetry.instrumentation.grpc -from opentelemetry import trace -from opentelemetry.instrumentation.grpc import ( - GrpcInstrumentorServer, - server_interceptor, -) -from opentelemetry.sdk import trace as trace_sdk -from opentelemetry.test.test_base import TestBase - - -class UnaryUnaryMethodHandler(grpc.RpcMethodHandler): - def __init__(self, handler): - self.request_streaming = False - self.response_streaming = False - self.request_deserializer = None - self.response_serializer = None - self.unary_unary = handler - self.unary_stream = None - self.stream_unary = None - self.stream_stream = None - - -class UnaryUnaryRpcHandler(grpc.GenericRpcHandler): - def __init__(self, handler): - self._unary_unary_handler = handler - - def service(self, handler_call_details): - return UnaryUnaryMethodHandler(self._unary_unary_handler) - - -class TestOpenTelemetryServerInterceptor(TestBase): - def test_instrumentor(self): - def handler(request, context): - return b"" - - grpc_server_instrumentor = GrpcInstrumentorServer() - grpc_server_instrumentor.instrument() - server = grpc.server( - futures.ThreadPoolExecutor(max_workers=1), - options=(("grpc.so_reuseport", 0),), - ) - - server.add_generic_rpc_handlers((UnaryUnaryRpcHandler(handler),)) - - port = server.add_insecure_port("[::]:0") - channel = grpc.insecure_channel("localhost:{:d}".format(port)) - - try: - server.start() - channel.unary_unary("test")(b"test") - finally: - server.stop(None) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertEqual(span.name, "test") - self.assertIs(span.kind, trace.SpanKind.SERVER) - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.grpc - ) - grpc_server_instrumentor.uninstrument() - - def test_uninstrument(self): - def handler(request, context): - return b"" - - grpc_server_instrumentor = GrpcInstrumentorServer() - grpc_server_instrumentor.instrument() - grpc_server_instrumentor.uninstrument() - server = grpc.server( - futures.ThreadPoolExecutor(max_workers=1), - options=(("grpc.so_reuseport", 0),), - ) - - server.add_generic_rpc_handlers((UnaryUnaryRpcHandler(handler),)) - - port = server.add_insecure_port("[::]:0") - channel = grpc.insecure_channel("localhost:{:d}".format(port)) - - try: - server.start() - channel.unary_unary("test")(b"test") - finally: - server.stop(None) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 0) - - def test_create_span(self): - """Check that the interceptor wraps calls with spans server-side.""" - - # Intercept gRPC calls... - interceptor = server_interceptor() - - # No-op RPC handler - def handler(request, context): - return b"" - - server = grpc.server( - futures.ThreadPoolExecutor(max_workers=1), - options=(("grpc.so_reuseport", 0),), - interceptors=[interceptor], - ) - - server.add_generic_rpc_handlers((UnaryUnaryRpcHandler(handler),)) - - port = server.add_insecure_port("[::]:0") - channel = grpc.insecure_channel("localhost:{:d}".format(port)) - - try: - server.start() - channel.unary_unary("")(b"") - finally: - server.stop(None) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - self.assertEqual(span.name, "") - self.assertIs(span.kind, trace.SpanKind.SERVER) - - # Check version and name in span's instrumentation info - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.grpc - ) - - def test_span_lifetime(self): - """Check that the span is active for the duration of the call.""" - - interceptor = server_interceptor() - - # To capture the current span at the time the handler is called - active_span_in_handler = None - - def handler(request, context): - nonlocal active_span_in_handler - active_span_in_handler = trace.get_current_span() - return b"" - - server = grpc.server( - futures.ThreadPoolExecutor(max_workers=1), - options=(("grpc.so_reuseport", 0),), - interceptors=[interceptor], - ) - server.add_generic_rpc_handlers((UnaryUnaryRpcHandler(handler),)) - - port = server.add_insecure_port("[::]:0") - channel = grpc.insecure_channel("localhost:{:d}".format(port)) - - active_span_before_call = trace.get_current_span() - try: - server.start() - channel.unary_unary("")(b"") - finally: - server.stop(None) - active_span_after_call = trace.get_current_span() - - self.assertEqual(active_span_before_call, trace.INVALID_SPAN) - self.assertEqual(active_span_after_call, trace.INVALID_SPAN) - self.assertIsInstance(active_span_in_handler, trace_sdk.Span) - self.assertIsNone(active_span_in_handler.parent) - - def test_sequential_server_spans(self): - """Check that sequential RPCs get separate server spans.""" - - interceptor = server_interceptor() - - # Capture the currently active span in each thread - active_spans_in_handler = [] - - def handler(request, context): - active_spans_in_handler.append(trace.get_current_span()) - return b"" - - server = grpc.server( - futures.ThreadPoolExecutor(max_workers=1), - options=(("grpc.so_reuseport", 0),), - interceptors=[interceptor], - ) - server.add_generic_rpc_handlers((UnaryUnaryRpcHandler(handler),)) - - port = server.add_insecure_port("[::]:0") - channel = grpc.insecure_channel("localhost:{:d}".format(port)) - - try: - server.start() - channel.unary_unary("")(b"") - channel.unary_unary("")(b"") - finally: - server.stop(None) - - self.assertEqual(len(active_spans_in_handler), 2) - # pylint:disable=unbalanced-tuple-unpacking - span1, span2 = active_spans_in_handler - # Spans should belong to separate traces, and each should be a root - # span - self.assertNotEqual(span1.context.span_id, span2.context.span_id) - self.assertNotEqual(span1.context.trace_id, span2.context.trace_id) - self.assertIsNone(span1.parent) - self.assertIsNone(span1.parent) - - def test_concurrent_server_spans(self): - """Check that concurrent RPC calls don't interfere with each other. - - This is the same check as test_sequential_server_spans except that the - RPCs are concurrent. Two handlers are invoked at the same time on two - separate threads. Each one should see a different active span and - context. - """ - - interceptor = server_interceptor() - - # Capture the currently active span in each thread - active_spans_in_handler = [] - latch = get_latch(2) - - def handler(request, context): - latch() - active_spans_in_handler.append(trace.get_current_span()) - return b"" - - server = grpc.server( - futures.ThreadPoolExecutor(max_workers=2), - options=(("grpc.so_reuseport", 0),), - interceptors=[interceptor], - ) - server.add_generic_rpc_handlers((UnaryUnaryRpcHandler(handler),)) - - port = server.add_insecure_port("[::]:0") - channel = grpc.insecure_channel("localhost:{:d}".format(port)) - - try: - server.start() - # Interleave calls so spans are active on each thread at the same - # time - with futures.ThreadPoolExecutor(max_workers=2) as tpe: - f1 = tpe.submit(channel.unary_unary(""), b"") - f2 = tpe.submit(channel.unary_unary(""), b"") - futures.wait((f1, f2)) - finally: - server.stop(None) - - self.assertEqual(len(active_spans_in_handler), 2) - # pylint:disable=unbalanced-tuple-unpacking - span1, span2 = active_spans_in_handler - # Spans should belong to separate traces, and each should be a root - # span - self.assertNotEqual(span1.context.span_id, span2.context.span_id) - self.assertNotEqual(span1.context.trace_id, span2.context.trace_id) - self.assertIsNone(span1.parent) - self.assertIsNone(span1.parent) - - -def get_latch(num): - """Get a countdown latch function for use in n threads.""" - cv = threading.Condition() - count = 0 - - def countdown_latch(): - """Block until n-1 other threads have called.""" - nonlocal count - cv.acquire() - count += 1 - cv.notify() - cv.release() - cv.acquire() - while count < num: - cv.wait() - cv.release() - - return countdown_latch diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-jinja2/CHANGELOG.md deleted file mode 100644 index b1f9ba50e11..00000000000 --- a/instrumentation/opentelemetry-instrumentation-jinja2/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-jinja2 - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) - -## 0.7b1 - -Released 2020-05-12 - -- Add jinja2 instrumentation ([#643](https://github.com/open-telemetry/opentelemetry-python/pull/643)) diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/LICENSE b/instrumentation/opentelemetry-instrumentation-jinja2/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-jinja2/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-jinja2/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-jinja2/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-jinja2/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-jinja2/README.rst b/instrumentation/opentelemetry-instrumentation-jinja2/README.rst deleted file mode 100644 index c74faeb32e1..00000000000 --- a/instrumentation/opentelemetry-instrumentation-jinja2/README.rst +++ /dev/null @@ -1,21 +0,0 @@ -OpenTelemetry jinja2 integration -================================ - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-jinja2.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-jinja2/ - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-jinja2 - - -References ----------- - -* `OpenTelemetry jinja2 integration `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/setup.cfg b/instrumentation/opentelemetry-instrumentation-jinja2/setup.cfg deleted file mode 100644 index 08f46ae1058..00000000000 --- a/instrumentation/opentelemetry-instrumentation-jinja2/setup.cfg +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-jinja2 -description = OpenTelemetry jinja2 instrumentation -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/master/instrumentation/opentelemetry-instrumentation-jinja2 -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - jinja2~=2.7 - wrapt >= 1.0.0, < 2.0.0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - jinja2 = opentelemetry.instrumentation.jinja2:Jinja2Instrumentor diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/setup.py b/instrumentation/opentelemetry-instrumentation-jinja2/setup.py deleted file mode 100644 index e9d484e1ebc..00000000000 --- a/instrumentation/opentelemetry-instrumentation-jinja2/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "jinja2", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry/instrumentation/jinja2/__init__.py b/instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry/instrumentation/jinja2/__init__.py deleted file mode 100644 index 63f23ae79b4..00000000000 --- a/instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry/instrumentation/jinja2/__init__.py +++ /dev/null @@ -1,147 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" - -Usage ------ - -The OpenTelemetry ``jinja2`` integration traces templates loading, compilation -and rendering. - -Usage ------ - -.. code-block:: python - - from jinja2 import Environment, FileSystemLoader - from opentelemetry.instrumentation.jinja2 import Jinja2Instrumentor - from opentelemetry import trace - from opentelemetry.trace import TracerProvider - - trace.set_tracer_provider(TracerProvider()) - - Jinja2Instrumentor().instrument() - - env = Environment(loader=FileSystemLoader("templates")) - template = env.get_template("mytemplate.html") - -API ---- -""" -# pylint: disable=no-value-for-parameter - -import logging - -import jinja2 -from wrapt import ObjectProxy -from wrapt import wrap_function_wrapper as _wrap - -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.jinja2.version import __version__ -from opentelemetry.instrumentation.utils import unwrap -from opentelemetry.trace import SpanKind, get_tracer -from opentelemetry.trace.status import Status, StatusCode - -logger = logging.getLogger(__name__) - -ATTRIBUTE_JINJA2_TEMPLATE_NAME = "jinja2.template_name" -ATTRIBUTE_JINJA2_TEMPLATE_PATH = "jinja2.template_path" -DEFAULT_TEMPLATE_NAME = "" - - -def _with_tracer_wrapper(func): - """Helper for providing tracer for wrapper functions. - """ - - def _with_tracer(tracer): - def wrapper(wrapped, instance, args, kwargs): - return func(tracer, wrapped, instance, args, kwargs) - - return wrapper - - return _with_tracer - - -@_with_tracer_wrapper -def _wrap_render(tracer, wrapped, instance, args, kwargs): - """Wrap `Template.render()` or `Template.generate()` - """ - with tracer.start_as_current_span( - "jinja2.render", kind=SpanKind.INTERNAL, - ) as span: - if span.is_recording(): - template_name = instance.name or DEFAULT_TEMPLATE_NAME - span.set_attribute(ATTRIBUTE_JINJA2_TEMPLATE_NAME, template_name) - return wrapped(*args, **kwargs) - - -@_with_tracer_wrapper -def _wrap_compile(tracer, wrapped, _, args, kwargs): - with tracer.start_as_current_span( - "jinja2.compile", kind=SpanKind.INTERNAL, - ) as span: - if span.is_recording(): - template_name = ( - args[1] - if len(args) > 1 - else kwargs.get("name", DEFAULT_TEMPLATE_NAME) - ) - span.set_attribute(ATTRIBUTE_JINJA2_TEMPLATE_NAME, template_name) - return wrapped(*args, **kwargs) - - -@_with_tracer_wrapper -def _wrap_load_template(tracer, wrapped, _, args, kwargs): - with tracer.start_as_current_span( - "jinja2.load", kind=SpanKind.INTERNAL, - ) as span: - if span.is_recording(): - template_name = kwargs.get("name", args[0]) - span.set_attribute(ATTRIBUTE_JINJA2_TEMPLATE_NAME, template_name) - template = None - try: - template = wrapped(*args, **kwargs) - return template - finally: - if template and span.is_recording(): - span.set_attribute( - ATTRIBUTE_JINJA2_TEMPLATE_PATH, template.filename - ) - - -class Jinja2Instrumentor(BaseInstrumentor): - """An instrumentor for jinja2 - - See `BaseInstrumentor` - """ - - def _instrument(self, **kwargs): - tracer_provider = kwargs.get("tracer_provider") - tracer = get_tracer(__name__, __version__, tracer_provider) - - _wrap(jinja2, "environment.Template.render", _wrap_render(tracer)) - _wrap(jinja2, "environment.Template.generate", _wrap_render(tracer)) - _wrap(jinja2, "environment.Environment.compile", _wrap_compile(tracer)) - _wrap( - jinja2, - "environment.Environment._load_template", - _wrap_load_template(tracer), - ) - - def _uninstrument(self, **kwargs): - unwrap(jinja2.Template, "render") - unwrap(jinja2.Template, "generate") - unwrap(jinja2.Environment, "compile") - unwrap(jinja2.Environment, "_load_template") diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry/instrumentation/jinja2/version.py b/instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry/instrumentation/jinja2/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry/instrumentation/jinja2/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-jinja2/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/tests/templates/base.html b/instrumentation/opentelemetry-instrumentation-jinja2/tests/templates/base.html deleted file mode 100644 index 05490d0c021..00000000000 --- a/instrumentation/opentelemetry-instrumentation-jinja2/tests/templates/base.html +++ /dev/null @@ -1 +0,0 @@ -Message: {% block content %}{% endblock %} diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/tests/templates/template.html b/instrumentation/opentelemetry-instrumentation-jinja2/tests/templates/template.html deleted file mode 100644 index ab281824151..00000000000 --- a/instrumentation/opentelemetry-instrumentation-jinja2/tests/templates/template.html +++ /dev/null @@ -1,2 +0,0 @@ -{% extends 'base.html' %} -{% block content %}Hello {{name}}!{% endblock %} diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/tests/test_jinja2.py b/instrumentation/opentelemetry-instrumentation-jinja2/tests/test_jinja2.py deleted file mode 100644 index 5de1d598cbc..00000000000 --- a/instrumentation/opentelemetry-instrumentation-jinja2/tests/test_jinja2.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -from unittest import mock - -import jinja2 - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.jinja2 import Jinja2Instrumentor -from opentelemetry.test.test_base import TestBase -from opentelemetry.trace import get_tracer - -TEST_DIR = os.path.dirname(os.path.realpath(__file__)) -TMPL_DIR = os.path.join(TEST_DIR, "templates") - - -class TestJinja2Instrumentor(TestBase): - def setUp(self): - super().setUp() - Jinja2Instrumentor().instrument() - # prevent cache effects when using Template('code...') - # pylint: disable=protected-access - jinja2.environment._spontaneous_environments.clear() - self.tracer = get_tracer(__name__) - - def tearDown(self): - super().tearDown() - Jinja2Instrumentor().uninstrument() - - def test_render_inline_template_with_root(self): - with self.tracer.start_as_current_span("root"): - template = jinja2.environment.Template("Hello {{name}}!") - self.assertEqual(template.render(name="Jinja"), "Hello Jinja!") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 3) - - # pylint:disable=unbalanced-tuple-unpacking - render, template, root = spans[:3] - - self.assertIs(render.parent, root.get_span_context()) - self.assertIs(template.parent, root.get_span_context()) - self.assertIsNone(root.parent) - - def test_render_not_recording(self): - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = mock_span - with mock.patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - jinja2.environment.Template("Hello {{name}}!") - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_render_inline_template(self): - template = jinja2.environment.Template("Hello {{name}}!") - self.assertEqual(template.render(name="Jinja"), "Hello Jinja!") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 2) - - # pylint:disable=unbalanced-tuple-unpacking - template, render = spans - - self.assertEqual(template.name, "jinja2.compile") - self.assertIs(template.kind, trace_api.SpanKind.INTERNAL) - self.assertEqual( - template.attributes, {"jinja2.template_name": ""}, - ) - - self.assertEqual(render.name, "jinja2.render") - self.assertIs(render.kind, trace_api.SpanKind.INTERNAL) - self.assertEqual( - render.attributes, {"jinja2.template_name": ""}, - ) - - def test_generate_inline_template_with_root(self): - with self.tracer.start_as_current_span("root"): - template = jinja2.environment.Template("Hello {{name}}!") - self.assertEqual( - "".join(template.generate(name="Jinja")), "Hello Jinja!" - ) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 3) - - # pylint:disable=unbalanced-tuple-unpacking - template, generate, root = spans - - self.assertIs(generate.parent, root.get_span_context()) - self.assertIs(template.parent, root.get_span_context()) - self.assertIsNone(root.parent) - - def test_generate_inline_template(self): - template = jinja2.environment.Template("Hello {{name}}!") - self.assertEqual( - "".join(template.generate(name="Jinja")), "Hello Jinja!" - ) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 2) - - # pylint:disable=unbalanced-tuple-unpacking - template, generate = spans[:2] - - self.assertEqual(template.name, "jinja2.compile") - self.assertIs(template.kind, trace_api.SpanKind.INTERNAL) - self.assertEqual( - template.attributes, {"jinja2.template_name": ""}, - ) - - self.assertEqual(generate.name, "jinja2.render") - self.assertIs(generate.kind, trace_api.SpanKind.INTERNAL) - self.assertEqual( - generate.attributes, {"jinja2.template_name": ""}, - ) - - def test_file_template_with_root(self): - with self.tracer.start_as_current_span("root"): - loader = jinja2.loaders.FileSystemLoader(TMPL_DIR) - env = jinja2.Environment(loader=loader) - template = env.get_template("template.html") - self.assertEqual( - template.render(name="Jinja"), "Message: Hello Jinja!" - ) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 6) - - # pylint:disable=unbalanced-tuple-unpacking - compile2, load2, compile1, load1, render, root = spans - - self.assertIs(compile2.parent, load2.get_span_context()) - self.assertIs(load2.parent, root.get_span_context()) - self.assertIs(compile1.parent, load1.get_span_context()) - self.assertIs(load1.parent, render.get_span_context()) - self.assertIs(render.parent, root.get_span_context()) - self.assertIsNone(root.parent) - - def test_file_template(self): - loader = jinja2.loaders.FileSystemLoader(TMPL_DIR) - env = jinja2.Environment(loader=loader) - template = env.get_template("template.html") - self.assertEqual( - template.render(name="Jinja"), "Message: Hello Jinja!" - ) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 5) - - # pylint:disable=unbalanced-tuple-unpacking - compile2, load2, compile1, load1, render = spans - - self.assertEqual(compile2.name, "jinja2.compile") - self.assertEqual(load2.name, "jinja2.load") - self.assertEqual(compile1.name, "jinja2.compile") - self.assertEqual(load1.name, "jinja2.load") - self.assertEqual(render.name, "jinja2.render") - - self.assertEqual( - compile2.attributes, {"jinja2.template_name": "template.html"}, - ) - self.assertEqual( - load2.attributes, - { - "jinja2.template_name": "template.html", - "jinja2.template_path": os.path.join( - TMPL_DIR, "template.html" - ), - }, - ) - self.assertEqual( - compile1.attributes, {"jinja2.template_name": "base.html"}, - ) - self.assertEqual( - load1.attributes, - { - "jinja2.template_name": "base.html", - "jinja2.template_path": os.path.join(TMPL_DIR, "base.html"), - }, - ) - self.assertEqual( - render.attributes, {"jinja2.template_name": "template.html"}, - ) - - def test_uninstrumented(self): - Jinja2Instrumentor().uninstrument() - - jinja2.environment.Template("Hello {{name}}!") - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 0) - - Jinja2Instrumentor().instrument() diff --git a/instrumentation/opentelemetry-instrumentation-mysql/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-mysql/CHANGELOG.md deleted file mode 100644 index 83563772122..00000000000 --- a/instrumentation/opentelemetry-instrumentation-mysql/CHANGELOG.md +++ /dev/null @@ -1,36 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-mysql - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) - -## Version 0.11b0 - -Released 2020-07-28 - -- bugfix: Fix auto-instrumentation entry point for mysql - ([#858](https://github.com/open-telemetry/opentelemetry-python/pull/858)) - -## 0.7b1 - -Released 2020-05-12 - -- Implement instrumentor interface ([#654](https://github.com/open-telemetry/opentelemetry-python/pull/654)) - -## 0.4a0 - -Released 2020-02-21 - -- Initial release \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-mysql/LICENSE b/instrumentation/opentelemetry-instrumentation-mysql/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-mysql/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-mysql/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-mysql/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-mysql/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-mysql/README.rst b/instrumentation/opentelemetry-instrumentation-mysql/README.rst deleted file mode 100644 index 9558f64bd9b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-mysql/README.rst +++ /dev/null @@ -1,25 +0,0 @@ -OpenTelemetry MySQL Instrumentation -=================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-mysql.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-mysql/ - -Instrumentation with MySQL that supports the mysql-connector library and is -specified to trace_integration using 'MySQL'. - - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-mysql - - -References ----------- -* `OpenTelemetry MySQL Instrumentation `_ -* `OpenTelemetry Project `_ - diff --git a/instrumentation/opentelemetry-instrumentation-mysql/setup.cfg b/instrumentation/opentelemetry-instrumentation-mysql/setup.cfg deleted file mode 100644 index ff9984a0c57..00000000000 --- a/instrumentation/opentelemetry-instrumentation-mysql/setup.cfg +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-mysql -description = OpenTelemetry MySQL instrumentation -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/master/instrumentation/opentelemetry-instrumentation-mysql -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation-dbapi == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - mysql-connector-python ~= 8.0 - wrapt >= 1.0.0, < 2.0.0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - mysql = opentelemetry.instrumentation.mysql:MySQLInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-mysql/setup.py b/instrumentation/opentelemetry-instrumentation-mysql/setup.py deleted file mode 100644 index 955f75d71e0..00000000000 --- a/instrumentation/opentelemetry-instrumentation-mysql/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "mysql", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/__init__.py b/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/__init__.py deleted file mode 100644 index 1cbd240cc59..00000000000 --- a/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/__init__.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -MySQL instrumentation supporting `mysql-connector`_, it can be enabled by -using ``MySQLInstrumentor``. - -.. _mysql-connector: https://pypi.org/project/mysql-connector/ - -Usage ------ - -.. code:: python - - import mysql.connector - from opentelemetry import trace - from opentelemetry.trace import TracerProvider - from opentelemetry.instrumentation.mysql import MySQLInstrumentor - - trace.set_tracer_provider(TracerProvider()) - - MySQLInstrumentor().instrument() - - cnx = mysql.connector.connect(database="MySQL_Database") - cursor = cnx.cursor() - cursor.execute("INSERT INTO test (testField) VALUES (123)" - cursor.close() - cnx.close() - -API ---- -""" - -import mysql.connector - -from opentelemetry.instrumentation import dbapi -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.mysql.version import __version__ -from opentelemetry.trace import get_tracer - - -class MySQLInstrumentor(BaseInstrumentor): - _CONNECTION_ATTRIBUTES = { - "database": "database", - "port": "server_port", - "host": "server_host", - "user": "user", - } - - _DATABASE_COMPONENT = "mysql" - _DATABASE_TYPE = "sql" - - def _instrument(self, **kwargs): - """Integrate with MySQL Connector/Python library. - https://dev.mysql.com/doc/connector-python/en/ - """ - tracer_provider = kwargs.get("tracer_provider") - - dbapi.wrap_connect( - __name__, - mysql.connector, - "connect", - self._DATABASE_COMPONENT, - self._DATABASE_TYPE, - self._CONNECTION_ATTRIBUTES, - version=__version__, - tracer_provider=tracer_provider, - ) - - def _uninstrument(self, **kwargs): - """"Disable MySQL instrumentation""" - dbapi.unwrap_connect(mysql.connector, "connect") - - # pylint:disable=no-self-use - def instrument_connection(self, connection): - """Enable instrumentation in a MySQL connection. - - Args: - connection: The connection to instrument. - - Returns: - An instrumented connection. - """ - tracer = get_tracer(__name__, __version__) - - return dbapi.instrument_connection( - tracer, - connection, - self._DATABASE_COMPONENT, - self._DATABASE_TYPE, - self._CONNECTION_ATTRIBUTES, - ) - - def uninstrument_connection(self, connection): - """Disable instrumentation in a MySQL connection. - - Args: - connection: The connection to uninstrument. - - Returns: - An uninstrumented connection. - """ - return dbapi.uninstrument_connection(connection) diff --git a/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/version.py b/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-mysql/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-mysql/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-mysql/tests/test_mysql_integration.py b/instrumentation/opentelemetry-instrumentation-mysql/tests/test_mysql_integration.py deleted file mode 100644 index 6c114d969f0..00000000000 --- a/instrumentation/opentelemetry-instrumentation-mysql/tests/test_mysql_integration.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest import mock - -import mysql.connector - -import opentelemetry.instrumentation.mysql -from opentelemetry.instrumentation.mysql import MySQLInstrumentor -from opentelemetry.sdk import resources -from opentelemetry.test.test_base import TestBase - - -class TestMysqlIntegration(TestBase): - def tearDown(self): - super().tearDown() - with self.disable_logging(): - MySQLInstrumentor().uninstrument() - - @mock.patch("mysql.connector.connect") - # pylint: disable=unused-argument - def test_instrumentor(self, mock_connect): - MySQLInstrumentor().instrument() - - cnx = mysql.connector.connect(database="test") - cursor = cnx.cursor() - query = "SELECT * FROM test" - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - # Check version and name in span's instrumentation info - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.mysql - ) - - # check that no spans are generated after uninstrumen - MySQLInstrumentor().uninstrument() - - cnx = mysql.connector.connect(database="test") - cursor = cnx.cursor() - query = "SELECT * FROM test" - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - @mock.patch("mysql.connector.connect") - # pylint: disable=unused-argument - def test_custom_tracer_provider(self, mock_connect): - resource = resources.Resource.create({}) - result = self.create_tracer_provider(resource=resource) - tracer_provider, exporter = result - - MySQLInstrumentor().instrument(tracer_provider=tracer_provider) - cnx = mysql.connector.connect(database="test") - cursor = cnx.cursor() - query = "SELECT * FROM test" - cursor.execute(query) - - span_list = exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - span = span_list[0] - - self.assertIs(span.resource, resource) - - @mock.patch("mysql.connector.connect") - # pylint: disable=unused-argument - def test_instrument_connection(self, mock_connect): - cnx = mysql.connector.connect(database="test") - query = "SELECT * FROM test" - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 0) - - cnx = MySQLInstrumentor().instrument_connection(cnx) - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - @mock.patch("mysql.connector.connect") - # pylint: disable=unused-argument - def test_uninstrument_connection(self, mock_connect): - MySQLInstrumentor().instrument() - cnx = mysql.connector.connect(database="test") - query = "SELECT * FROM test" - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - cnx = MySQLInstrumentor().uninstrument_connection(cnx) - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-opentracing-shim/CHANGELOG.md deleted file mode 100644 index be243d95847..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/CHANGELOG.md +++ /dev/null @@ -1,30 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change reference names to opentelemetry-instrumentation-opentracing-shim - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) - -## 0.3a0 - -Released 2019-12-11 - -- Implement extract and inject support for HTTP_HEADERS and TEXT_MAP formats - ([#256](https://github.com/open-telemetry/opentelemetry-python/pull/256)) - -## 0.2a0 - -Released 2019-10-29 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/LICENSE b/instrumentation/opentelemetry-instrumentation-opentracing-shim/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-opentracing-shim/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-opentracing-shim/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-opentracing-shim/README.rst b/instrumentation/opentelemetry-instrumentation-opentracing-shim/README.rst deleted file mode 100644 index 7a8413ef59d..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/README.rst +++ /dev/null @@ -1,20 +0,0 @@ -OpenTracing Shim for OpenTelemetry -================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-opentracing-shim.svg - :target: https://pypi.org/project/opentelemetry-opentracing-shim/ - -Installation ------------- - -:: - - pip install opentelemetry-opentracing-shim - -References ----------- - -* `OpenTracing Shim for OpenTelemetry `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/setup.cfg b/instrumentation/opentelemetry-instrumentation-opentracing-shim/setup.cfg deleted file mode 100644 index 33cad7b9351..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/setup.cfg +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-opentracing-shim -description = OpenTracing Shim for OpenTelemetry -long_description = file: README.rst -long_description_content_type = text/x-rst -author = OpenTelemetry Authors -author_email = cncf-opentelemetry-contributors@lists.cncf.io -url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-opentracing-shim -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - Deprecated >= 1.2.6 - opentracing ~= 2.0 - opentelemetry-api == 0.16.dev0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - opentracing ~= 2.2.0 - -[options.packages.find] -where = src diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/setup.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/setup.py deleted file mode 100644 index f5d71a86b56..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "opentracing_shim", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/__init__.py deleted file mode 100644 index 00a0b8d0cb8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/__init__.py +++ /dev/null @@ -1,723 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -The OpenTelemetry OpenTracing shim is a library which allows an easy migration -from OpenTracing to OpenTelemetry. - -The shim consists of a set of classes which implement the OpenTracing Python -API while using OpenTelemetry constructs behind the scenes. Its purpose is to -allow applications which are already instrumented using OpenTracing to start -using OpenTelemetry with a minimal effort, without having to rewrite large -portions of the codebase. - -To use the shim, a :class:`TracerShim` instance is created and then used as if -it were an "ordinary" OpenTracing :class:`opentracing.Tracer`, as in the -following example:: - - import time - - from opentelemetry import trace - from opentelemetry.sdk.trace import TracerProvider - from opentelemetry.instrumentation.opentracing_shim import create_tracer - - # Define which OpenTelemetry Tracer provider implementation to use. - trace.set_tracer_provider(TracerProvider()) - - # Create an OpenTelemetry Tracer. - otel_tracer = trace.get_tracer(__name__) - - # Create an OpenTracing shim. - shim = create_tracer(otel_tracer) - - with shim.start_active_span("ProcessHTTPRequest"): - print("Processing HTTP request") - # Sleeping to mock real work. - time.sleep(0.1) - with shim.start_active_span("GetDataFromDB"): - print("Getting data from DB") - # Sleeping to mock real work. - time.sleep(0.2) - -Note: - While the OpenTracing Python API represents time values as the number of - **seconds** since the epoch expressed as :obj:`float` values, the - OpenTelemetry Python API represents time values as the number of - **nanoseconds** since the epoch expressed as :obj:`int` values. This fact - requires the OpenTracing shim to convert time values back and forth between - the two representations, which involves floating point arithmetic. - - Due to the way computers represent floating point values in hardware, - representation of decimal floating point values in binary-based hardware is - imprecise by definition. - - The above results in **slight imprecisions** in time values passed to the - shim via the OpenTracing API when comparing the value passed to the shim - and the value stored in the OpenTelemetry :class:`opentelemetry.trace.Span` - object behind the scenes. **This is not a bug in this library or in - Python**. Rather, this is a generic problem which stems from the fact that - not every decimal floating point number can be correctly represented in - binary, and therefore affects other libraries and programming languages as - well. More information about this problem can be found in the - `Floating Point Arithmetic\\: Issues and Limitations`_ section of the - Python documentation. - - While testing this library, the aforementioned imprecisions were observed - to be of *less than a microsecond*. - -API ---- -.. _Floating Point Arithmetic\\: Issues and Limitations: - https://docs.python.org/3/tutorial/floatingpoint.html -""" - -# TODO: make pylint use 3p opentracing module for type inference -# pylint:disable=no-member - -import logging -from typing import Optional, TypeVar, Union - -from deprecated import deprecated -from opentracing import ( - Format, - Scope, - ScopeManager, - Span, - SpanContext, - Tracer, - UnsupportedFormatException, -) - -from opentelemetry import propagators -from opentelemetry.baggage import get_baggage, set_baggage -from opentelemetry.context import Context, attach, detach, get_value, set_value -from opentelemetry.instrumentation.opentracing_shim import util -from opentelemetry.instrumentation.opentracing_shim.version import __version__ -from opentelemetry.trace import INVALID_SPAN_CONTEXT, DefaultSpan, Link -from opentelemetry.trace import SpanContext as OtelSpanContext -from opentelemetry.trace import Tracer as OtelTracer -from opentelemetry.trace import ( - TracerProvider, - get_current_span, - set_span_in_context, -) -from opentelemetry.trace.propagation.textmap import DictGetter -from opentelemetry.util.types import Attributes - -ValueT = TypeVar("ValueT", int, float, bool, str) -logger = logging.getLogger(__name__) - - -def create_tracer(otel_tracer_provider: TracerProvider) -> "TracerShim": - """Creates a :class:`TracerShim` object from the provided OpenTelemetry - :class:`opentelemetry.trace.TracerProvider`. - - The returned :class:`TracerShim` is an implementation of - :class:`opentracing.Tracer` using OpenTelemetry under the hood. - - Args: - otel_tracer_provider: A tracer from this provider will be used to - perform the actual tracing when user code is instrumented using the - OpenTracing API. - - Returns: - The created :class:`TracerShim`. - """ - - return TracerShim(otel_tracer_provider.get_tracer(__name__, __version__)) - - -class SpanContextShim(SpanContext): - """Implements :class:`opentracing.SpanContext` by wrapping a - :class:`opentelemetry.trace.SpanContext` object. - - Args: - otel_context: A :class:`opentelemetry.trace.SpanContext` to be used for - constructing the :class:`SpanContextShim`. - """ - - def __init__(self, otel_context: OtelSpanContext): - self._otel_context = otel_context - # Context is being used here since it must be immutable. - self._baggage = Context() - - def unwrap(self) -> OtelSpanContext: - """Returns the wrapped :class:`opentelemetry.trace.SpanContext` - object. - - Returns: - The :class:`opentelemetry.trace.SpanContext` object wrapped by this - :class:`SpanContextShim`. - """ - - return self._otel_context - - @property - def baggage(self) -> Context: - """Returns the ``baggage`` associated with this object""" - - return self._baggage - - -class SpanShim(Span): - """Wraps a :class:`opentelemetry.trace.Span` object. - - Args: - tracer: The :class:`opentracing.Tracer` that created this `SpanShim`. - context: A :class:`SpanContextShim` which contains the context for this - :class:`SpanShim`. - span: A :class:`opentelemetry.trace.Span` to wrap. - """ - - def __init__(self, tracer, context: SpanContextShim, span): - super().__init__(tracer, context) - self._otel_span = span - - def unwrap(self): - """Returns the wrapped :class:`opentelemetry.trace.Span` object. - - Returns: - The :class:`opentelemetry.trace.Span` object wrapped by this - :class:`SpanShim`. - """ - - return self._otel_span - - def set_operation_name(self, operation_name: str) -> "SpanShim": - """Updates the name of the wrapped OpenTelemetry span. - - Args: - operation_name: The new name to be used for the underlying - :class:`opentelemetry.trace.Span` object. - - Returns: - Returns this :class:`SpanShim` instance to allow call chaining. - """ - - self._otel_span.update_name(operation_name) - return self - - def finish(self, finish_time: float = None): - """Ends the OpenTelemetry span wrapped by this :class:`SpanShim`. - - If *finish_time* is provided, the time value is converted to the - OpenTelemetry time format (number of nanoseconds since the epoch, - expressed as an integer) and passed on to the OpenTelemetry tracer when - ending the OpenTelemetry span. If *finish_time* isn't provided, it is - up to the OpenTelemetry tracer implementation to generate a timestamp - when ending the span. - - Args: - finish_time: A value that represents the finish time expressed as - the number of seconds since the epoch as returned by - :func:`time.time()`. - """ - - end_time = finish_time - if end_time is not None: - end_time = util.time_seconds_to_ns(finish_time) - self._otel_span.end(end_time=end_time) - - def set_tag(self, key: str, value: ValueT) -> "SpanShim": - """Sets an OpenTelemetry attribute on the wrapped OpenTelemetry span. - - Args: - key: A tag key. - value: A tag value. - - Returns: - Returns this :class:`SpanShim` instance to allow call chaining. - """ - - self._otel_span.set_attribute(key, value) - return self - - def log_kv( - self, key_values: Attributes, timestamp: float = None - ) -> "SpanShim": - """Logs an event for the wrapped OpenTelemetry span. - - Note: - The OpenTracing API defines the values of *key_values* to be of any - type. However, the OpenTelemetry API requires that the values be - any one of the types defined in - ``opentelemetry.trace.util.Attributes`` therefore, only these types - are supported as values. - - Args: - key_values: A dictionary as specified in - ``opentelemetry.trace.util.Attributes``. - timestamp: Timestamp of the OpenTelemetry event, will be generated - automatically if omitted. - - Returns: - Returns this :class:`SpanShim` instance to allow call chaining. - """ - - if timestamp is not None: - event_timestamp = util.time_seconds_to_ns(timestamp) - else: - event_timestamp = None - - event_name = util.event_name_from_kv(key_values) - self._otel_span.add_event(event_name, key_values, event_timestamp) - return self - - @deprecated(reason="This method is deprecated in favor of log_kv") - def log(self, **kwargs): - super().log(**kwargs) - - @deprecated(reason="This method is deprecated in favor of log_kv") - def log_event(self, event, payload=None): - super().log_event(event, payload=payload) - - def set_baggage_item(self, key: str, value: str): - """Stores a Baggage item in the span as a key/value - pair. - - Args: - key: A tag key. - value: A tag value. - """ - # pylint: disable=protected-access - self._context._baggage = set_baggage( - key, value, context=self._context._baggage - ) - - def get_baggage_item(self, key: str) -> Optional[object]: - """Retrieves value of the baggage item with the given key. - - Args: - key: A tag key. - Returns: - Returns this :class:`SpanShim` instance to allow call chaining. - """ - # pylint: disable=protected-access - return get_baggage(key, context=self._context._baggage) - - -class ScopeShim(Scope): - """A `ScopeShim` wraps the OpenTelemetry functionality related to span - activation/deactivation while using OpenTracing :class:`opentracing.Scope` - objects for presentation. - - Unlike other classes in this package, the `ScopeShim` class doesn't wrap an - OpenTelemetry class because OpenTelemetry doesn't have the notion of - "scope" (though it *does* have similar functionality). - - There are two ways to construct a `ScopeShim` object: using the default - initializer and using the :meth:`from_context_manager()` class method. - - It is necessary to have both ways for constructing `ScopeShim` objects - because in some cases we need to create the object from an OpenTelemetry - `opentelemetry.trace.Span` context manager (as returned by - :meth:`opentelemetry.trace.Tracer.use_span`), in which case our only way of - retrieving a `opentelemetry.trace.Span` object is by calling the - ``__enter__()`` method on the context manager, which makes the span active - in the OpenTelemetry tracer; whereas in other cases we need to accept a - `SpanShim` object and wrap it in a `ScopeShim`. The former is used mainly - when the instrumentation code retrieves the currently-active span using - `ScopeManagerShim.active`. The latter is mainly used when the - instrumentation code activates a span using - :meth:`ScopeManagerShim.activate`. - - Args: - manager: The :class:`ScopeManagerShim` that created this - :class:`ScopeShim`. - span: The :class:`SpanShim` this :class:`ScopeShim` controls. - span_cm: A Python context manager which yields an OpenTelemetry - `opentelemetry.trace.Span` from its ``__enter__()`` method. Used - by :meth:`from_context_manager` to store the context manager as - an attribute so that it can later be closed by calling its - ``__exit__()`` method. Defaults to `None`. - """ - - def __init__( - self, manager: "ScopeManagerShim", span: SpanShim, span_cm=None - ): - super().__init__(manager, span) - self._span_cm = span_cm - self._token = attach(set_value("scope_shim", self)) - - # TODO: Change type of `manager` argument to `opentracing.ScopeManager`? We - # need to get rid of `manager.tracer` for this. - @classmethod - def from_context_manager(cls, manager: "ScopeManagerShim", span_cm): - """Constructs a :class:`ScopeShim` from an OpenTelemetry - `opentelemetry.trace.Span` context - manager. - - The method extracts a `opentelemetry.trace.Span` object from the - context manager by calling the context manager's ``__enter__()`` - method. This causes the span to start in the OpenTelemetry tracer. - - Example usage:: - - span = otel_tracer.start_span("TestSpan") - span_cm = otel_tracer.use_span(span) - scope_shim = ScopeShim.from_context_manager( - scope_manager_shim, - span_cm=span_cm, - ) - - Args: - manager: The :class:`ScopeManagerShim` that created this - :class:`ScopeShim`. - span_cm: A context manager as returned by - :meth:`opentelemetry.trace.Tracer.use_span`. - """ - - otel_span = span_cm.__enter__() - span_context = SpanContextShim(otel_span.get_span_context()) - span = SpanShim(manager.tracer, span_context, otel_span) - return cls(manager, span, span_cm) - - def close(self): - """Closes the `ScopeShim`. If the `ScopeShim` was created from a - context manager, calling this method sets the active span in the - OpenTelemetry tracer back to the span which was active before this - `ScopeShim` was created. In addition, if the span represented by this - `ScopeShim` was activated with the *finish_on_close* argument set to - `True`, calling this method will end the span. - - Warning: - In the current state of the implementation it is possible to create - a `ScopeShim` directly from a `SpanShim`, that is - without using - :meth:`from_context_manager()`. For that reason we need to be able - to end the span represented by the `ScopeShim` in this case, too. - Please note that closing a `ScopeShim` created this way (for - example as returned by :meth:`ScopeManagerShim.active`) **always - ends the associated span**, regardless of the value passed in - *finish_on_close* when activating the span. - """ - - detach(self._token) - - if self._span_cm is not None: - # We don't have error information to pass to `__exit__()` so we - # pass `None` in all arguments. If the OpenTelemetry tracer - # implementation requires this information, the `__exit__()` method - # on `opentracing.Scope` should be overridden and modified to pass - # the relevant values to this `close()` method. - self._span_cm.__exit__(None, None, None) - else: - self._span.unwrap().end() - - -class ScopeManagerShim(ScopeManager): - """Implements :class:`opentracing.ScopeManager` by setting and getting the - active `opentelemetry.trace.Span` in the OpenTelemetry tracer. - - This class keeps a reference to a :class:`TracerShim` as an attribute. This - reference is used to communicate with the OpenTelemetry tracer. It is - necessary to have a reference to the :class:`TracerShim` rather than the - :class:`opentelemetry.trace.Tracer` wrapped by it because when constructing - a :class:`SpanShim` we need to pass a reference to a - :class:`opentracing.Tracer`. - - Args: - tracer: A :class:`TracerShim` to use for setting and getting active - span state. - """ - - def __init__(self, tracer: "TracerShim"): - # The only thing the ``__init__()``` method on the base class does is - # initialize `self._noop_span` and `self._noop_scope` with no-op - # objects. Therefore, it doesn't seem useful to call it. - # pylint: disable=super-init-not-called - self._tracer = tracer - - def activate(self, span: SpanShim, finish_on_close: bool) -> "ScopeShim": - """Activates a :class:`SpanShim` and returns a :class:`ScopeShim` which - represents the active span. - - Args: - span: A :class:`SpanShim` to be activated. - finish_on_close(:obj:`bool`): Determines whether the OpenTelemetry - span should be ended when the returned :class:`ScopeShim` is - closed. - - Returns: - A :class:`ScopeShim` representing the activated span. - """ - - span_cm = self._tracer.unwrap().use_span( - span.unwrap(), end_on_exit=finish_on_close - ) - return ScopeShim.from_context_manager(self, span_cm=span_cm) - - @property - def active(self) -> "ScopeShim": - """Returns a :class:`ScopeShim` object representing the - currently-active span in the OpenTelemetry tracer. - - Returns: - A :class:`ScopeShim` representing the active span in the - OpenTelemetry tracer, or `None` if no span is currently active. - - Warning: - Calling :meth:`ScopeShim.close` on the :class:`ScopeShim` returned - by this property **always ends the corresponding span**, regardless - of the *finish_on_close* value used when activating the span. This - is a limitation of the current implementation of the OpenTracing - shim and is likely to be handled in future versions. - """ - - span = get_current_span() - if span.get_span_context() == INVALID_SPAN_CONTEXT: - return None - - try: - return get_value("scope_shim") - except KeyError: - span_context = SpanContextShim(span.get_span_context()) - wrapped_span = SpanShim(self._tracer, span_context, span) - return ScopeShim(self, span=wrapped_span) - - @property - def tracer(self) -> "TracerShim": - """Returns the :class:`TracerShim` reference used by this - :class:`ScopeManagerShim` for setting and getting the active span from - the OpenTelemetry tracer. - - Returns: - The :class:`TracerShim` used for setting and getting the active - span. - - Warning: - This property is *not* a part of the OpenTracing API. It used - internally by the current implementation of the OpenTracing shim - and will likely be removed in future versions. - """ - - return self._tracer - - -class TracerShim(Tracer): - """Wraps a :class:`opentelemetry.trace.Tracer` object. - - This wrapper class allows using an OpenTelemetry tracer as if it were an - OpenTracing tracer. It exposes the same methods as an "ordinary" - OpenTracing tracer, and uses OpenTelemetry transparently for performing the - actual tracing. - - This class depends on the *OpenTelemetry API*. Therefore, any - implementation of a :class:`opentelemetry.trace.Tracer` should work with - this class. - - Args: - tracer: A :class:`opentelemetry.trace.Tracer` to use for tracing. This - tracer will be invoked by the shim to create actual spans. - """ - - def __init__(self, tracer: OtelTracer): - super().__init__(scope_manager=ScopeManagerShim(self)) - self._otel_tracer = tracer - self._supported_formats = ( - Format.TEXT_MAP, - Format.HTTP_HEADERS, - ) - self._carrier_getter = DictGetter() - - def unwrap(self): - """Returns the :class:`opentelemetry.trace.Tracer` object that is - wrapped by this :class:`TracerShim` and used for actual tracing. - - Returns: - The :class:`opentelemetry.trace.Tracer` used for actual tracing. - """ - - return self._otel_tracer - - def start_active_span( - self, - operation_name: str, - child_of: Union[SpanShim, SpanContextShim] = None, - references: list = None, - tags: Attributes = None, - start_time: float = None, - ignore_active_span: bool = False, - finish_on_close: bool = True, - ) -> "ScopeShim": - """Starts and activates a span. In terms of functionality, this method - behaves exactly like the same method on a "regular" OpenTracing tracer. - See :meth:`opentracing.Tracer.start_active_span` for more details. - - Args: - operation_name: Name of the operation represented by - the new span from the perspective of the current service. - child_of: A :class:`SpanShim` or :class:`SpanContextShim` - representing the parent in a "child of" reference. If - specified, the *references* parameter must be omitted. - references: A list of :class:`opentracing.Reference` objects that - identify one or more parents of type :class:`SpanContextShim`. - tags: A dictionary of tags. - start_time: An explicit start time expressed as the number of - seconds since the epoch as returned by :func:`time.time()`. - ignore_active_span: Ignore the currently-active span in the - OpenTelemetry tracer and make the created span the root span of - a new trace. - finish_on_close: Determines whether the created span should end - automatically when closing the returned :class:`ScopeShim`. - - Returns: - A :class:`ScopeShim` that is already activated by the - :class:`ScopeManagerShim`. - """ - - current_span = get_current_span() - - if child_of is None and current_span is not INVALID_SPAN_CONTEXT: - child_of = SpanShim(None, None, current_span) - - span = self.start_span( - operation_name=operation_name, - child_of=child_of, - references=references, - tags=tags, - start_time=start_time, - ignore_active_span=ignore_active_span, - ) - return self._scope_manager.activate(span, finish_on_close) - - def start_span( - self, - operation_name: str = None, - child_of: Union[SpanShim, SpanContextShim] = None, - references: list = None, - tags: Attributes = None, - start_time: float = None, - ignore_active_span: bool = False, - ) -> SpanShim: - """Implements the ``start_span()`` method from the base class. - - Starts a span. In terms of functionality, this method behaves exactly - like the same method on a "regular" OpenTracing tracer. See - :meth:`opentracing.Tracer.start_span` for more details. - - Args: - operation_name: Name of the operation represented by the new span - from the perspective of the current service. - child_of: A :class:`SpanShim` or :class:`SpanContextShim` - representing the parent in a "child of" reference. If - specified, the *references* parameter must be omitted. - references: A list of :class:`opentracing.Reference` objects that - identify one or more parents of type :class:`SpanContextShim`. - tags: A dictionary of tags. - start_time: An explicit start time expressed as the number of - seconds since the epoch as returned by :func:`time.time()`. - ignore_active_span: Ignore the currently-active span in the - OpenTelemetry tracer and make the created span the root span of - a new trace. - - Returns: - An already-started :class:`SpanShim` instance. - """ - - # Use active span as parent when no explicit parent is specified. - if not ignore_active_span and not child_of: - child_of = self.active_span - - # Use the specified parent or the active span if possible. Otherwise, - # use a `None` parent, which triggers the creation of a new trace. - parent = child_of.unwrap() if child_of else None - if isinstance(parent, OtelSpanContext): - parent = DefaultSpan(parent) - - parent_span_context = set_span_in_context(parent) - - links = [] - if references: - for ref in references: - links.append(Link(ref.referenced_context.unwrap())) - - # The OpenTracing API expects time values to be `float` values which - # represent the number of seconds since the epoch. OpenTelemetry - # represents time values as nanoseconds since the epoch. - start_time_ns = start_time - if start_time_ns is not None: - start_time_ns = util.time_seconds_to_ns(start_time) - - span = self._otel_tracer.start_span( - operation_name, - context=parent_span_context, - links=links, - attributes=tags, - start_time=start_time_ns, - ) - - context = SpanContextShim(span.get_span_context()) - return SpanShim(self, context, span) - - def inject(self, span_context, format: object, carrier: object): - """Injects ``span_context`` into ``carrier``. - - See base class for more details. - - Args: - span_context: The ``opentracing.SpanContext`` to inject. - format: a Python object instance that represents a given - carrier format. `format` may be of any type, and `format` - equality is defined by Python ``==`` operator. - carrier: the format-specific carrier object to inject into - """ - - # pylint: disable=redefined-builtin - # This implementation does not perform the injecting by itself but - # uses the configured propagators in opentelemetry.propagators. - # TODO: Support Format.BINARY once it is supported in - # opentelemetry-python. - - if format not in self._supported_formats: - raise UnsupportedFormatException - - propagator = propagators.get_global_textmap() - - ctx = set_span_in_context(DefaultSpan(span_context.unwrap())) - propagator.inject(type(carrier).__setitem__, carrier, context=ctx) - - def extract(self, format: object, carrier: object): - """Returns an ``opentracing.SpanContext`` instance extracted from a - ``carrier``. - - See base class for more details. - - Args: - format: a Python object instance that represents a given - carrier format. ``format`` may be of any type, and ``format`` - equality is defined by python ``==`` operator. - carrier: the format-specific carrier object to extract from - - Returns: - An ``opentracing.SpanContext`` extracted from ``carrier`` or - ``None`` if no such ``SpanContext`` could be found. - """ - - # pylint: disable=redefined-builtin - # This implementation does not perform the extracing by itself but - # uses the configured propagators in opentelemetry.propagators. - # TODO: Support Format.BINARY once it is supported in - # opentelemetry-python. - if format not in self._supported_formats: - raise UnsupportedFormatException - - propagator = propagators.get_global_textmap() - ctx = propagator.extract(self._carrier_getter, carrier) - span = get_current_span(ctx) - if span is not None: - otel_context = span.get_span_context() - else: - otel_context = INVALID_SPAN_CONTEXT - - return SpanContextShim(otel_context) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/util.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/util.py deleted file mode 100644 index eb7d3d9acaa..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/util.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# A default event name to be used for logging events when a better event name -# can't be derived from the event's key-value pairs. -DEFAULT_EVENT_NAME = "log" - - -def time_seconds_to_ns(time_seconds): - """Converts a time value in seconds to a time value in nanoseconds. - - `time_seconds` is a `float` as returned by `time.time()` which represents - the number of seconds since the epoch. - - The returned value is an `int` representing the number of nanoseconds since - the epoch. - """ - - return int(time_seconds * 1e9) - - -def time_seconds_from_ns(time_nanoseconds): - """Converts a time value in nanoseconds to a time value in seconds. - - `time_nanoseconds` is an `int` representing the number of nanoseconds since - the epoch. - - The returned value is a `float` representing the number of seconds since - the epoch. - """ - - return time_nanoseconds / 1e9 - - -def event_name_from_kv(key_values): - """A helper function which returns an event name from the given dict, or a - default event name. - """ - - if key_values is None or "event" not in key_values: - return DEFAULT_EVENT_NAME - - return key_values["event"] diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/version.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/test_shim.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/test_shim.py deleted file mode 100644 index 151ca07ba9e..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/test_shim.py +++ /dev/null @@ -1,636 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# TODO: make pylint use 3p opentracing module for type inference -# pylint:disable=no-member - -import time -from unittest import TestCase -from unittest.mock import Mock - -import opentracing - -from opentelemetry import propagators, trace -from opentelemetry.instrumentation.opentracing_shim import ( - SpanContextShim, - SpanShim, - create_tracer, - util, -) -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.test.mock_textmap import ( - MockTextMapPropagator, - NOOPTextMapPropagator, -) - - -class TestShim(TestCase): - # pylint: disable=too-many-public-methods - - def setUp(self): - """Create an OpenTelemetry tracer and a shim before every test case.""" - trace.set_tracer_provider(TracerProvider()) - self.shim = create_tracer(trace.get_tracer_provider()) - - @classmethod - def setUpClass(cls): - # Save current propagator to be restored on teardown. - cls._previous_propagator = propagators.get_global_textmap() - - # Set mock propagator for testing. - propagators.set_global_textmap(MockTextMapPropagator()) - - @classmethod - def tearDownClass(cls): - # Restore previous propagator. - propagators.set_global_textmap(cls._previous_propagator) - - def test_shim_type(self): - # Verify shim is an OpenTracing tracer. - self.assertIsInstance(self.shim, opentracing.Tracer) - - def test_start_active_span(self): - """Test span creation and activation using `start_active_span()`.""" - - with self.shim.start_active_span("TestSpan0") as scope: - # Verify correct type of Scope and Span objects. - self.assertIsInstance(scope, opentracing.Scope) - self.assertIsInstance(scope.span, opentracing.Span) - - # Verify span is started. - self.assertIsNotNone(scope.span.unwrap().start_time) - - # Verify span is active. - self.assertEqual( - self.shim.active_span.context.unwrap(), - scope.span.context.unwrap(), - ) - # TODO: We can't check for equality of self.shim.active_span and - # scope.span because the same OpenTelemetry span is returned inside - # different SpanShim objects. A possible solution is described - # here: - # https://github.com/open-telemetry/opentelemetry-python/issues/161#issuecomment-534136274 - - # Verify span has ended. - self.assertIsNotNone(scope.span.unwrap().end_time) - - # Verify no span is active. - self.assertIsNone(self.shim.active_span) - - def test_start_span(self): - """Test span creation using `start_span()`.""" - - with self.shim.start_span("TestSpan1") as span: - # Verify correct type of Span object. - self.assertIsInstance(span, opentracing.Span) - - # Verify span is started. - self.assertIsNotNone(span.unwrap().start_time) - - # Verify `start_span()` does NOT make the span active. - self.assertIsNone(self.shim.active_span) - - # Verify span has ended. - self.assertIsNotNone(span.unwrap().end_time) - - def test_start_span_no_contextmanager(self): - """Test `start_span()` without a `with` statement.""" - - span = self.shim.start_span("TestSpan2") - - # Verify span is started. - self.assertIsNotNone(span.unwrap().start_time) - - # Verify `start_span()` does NOT make the span active. - self.assertIsNone(self.shim.active_span) - - span.finish() - - def test_explicit_span_finish(self): - """Test `finish()` method on `Span` objects.""" - - span = self.shim.start_span("TestSpan3") - - # Verify span hasn't ended. - self.assertIsNone(span.unwrap().end_time) - - span.finish() - - # Verify span has ended. - self.assertIsNotNone(span.unwrap().end_time) - - def test_explicit_start_time(self): - """Test `start_time` argument.""" - - now = time.time() - with self.shim.start_active_span("TestSpan4", start_time=now) as scope: - result = util.time_seconds_from_ns(scope.span.unwrap().start_time) - # Tolerate inaccuracies of less than a microsecond. See Note: - # https://open-telemetry.github.io/opentelemetry-python/opentelemetry.instrumentation.opentracing_shim.html - # TODO: This seems to work consistently, but we should find out the - # biggest possible loss of precision. - self.assertAlmostEqual(result, now, places=6) - - def test_explicit_end_time(self): - """Test `end_time` argument of `finish()` method.""" - - span = self.shim.start_span("TestSpan5") - now = time.time() - span.finish(now) - - end_time = util.time_seconds_from_ns(span.unwrap().end_time) - # Tolerate inaccuracies of less than a microsecond. See Note: - # https://open-telemetry.github.io/opentelemetry-python/opentelemetry.instrumentation.opentracing_shim.html - # TODO: This seems to work consistently, but we should find out the - # biggest possible loss of precision. - self.assertAlmostEqual(end_time, now, places=6) - - def test_explicit_span_activation(self): - """Test manual activation and deactivation of a span.""" - - span = self.shim.start_span("TestSpan6") - - # Verify no span is currently active. - self.assertIsNone(self.shim.active_span) - - with self.shim.scope_manager.activate( - span, finish_on_close=True - ) as scope: - # Verify span is active. - self.assertEqual( - self.shim.active_span.context.unwrap(), - scope.span.context.unwrap(), - ) - - # Verify no span is active. - self.assertIsNone(self.shim.active_span) - - def test_start_active_span_finish_on_close(self): - """Test `finish_on_close` argument of `start_active_span()`.""" - - with self.shim.start_active_span( - "TestSpan7", finish_on_close=True - ) as scope: - # Verify span hasn't ended. - self.assertIsNone(scope.span.unwrap().end_time) - - # Verify span has ended. - self.assertIsNotNone(scope.span.unwrap().end_time) - - with self.shim.start_active_span( - "TestSpan8", finish_on_close=False - ) as scope: - # Verify span hasn't ended. - self.assertIsNone(scope.span.unwrap().end_time) - - # Verify span hasn't ended after scope had been closed. - self.assertIsNone(scope.span.unwrap().end_time) - - scope.span.finish() - - def test_activate_finish_on_close(self): - """Test `finish_on_close` argument of `activate()`.""" - - span = self.shim.start_span("TestSpan9") - - with self.shim.scope_manager.activate( - span, finish_on_close=True - ) as scope: - # Verify span is active. - self.assertEqual( - self.shim.active_span.context.unwrap(), - scope.span.context.unwrap(), - ) - - # Verify span has ended. - self.assertIsNotNone(span.unwrap().end_time) - - span = self.shim.start_span("TestSpan10") - - with self.shim.scope_manager.activate( - span, finish_on_close=False - ) as scope: - # Verify span is active. - self.assertEqual( - self.shim.active_span.context.unwrap(), - scope.span.context.unwrap(), - ) - - # Verify span hasn't ended. - self.assertIsNone(span.unwrap().end_time) - - span.finish() - - def test_explicit_scope_close(self): - """Test `close()` method on `ScopeShim`.""" - - with self.shim.start_active_span("ParentSpan") as parent: - # Verify parent span is active. - self.assertEqual( - self.shim.active_span.context.unwrap(), - parent.span.context.unwrap(), - ) - - child = self.shim.start_active_span("ChildSpan") - - # Verify child span is active. - self.assertEqual( - self.shim.active_span.context.unwrap(), - child.span.context.unwrap(), - ) - - # Verify child span hasn't ended. - self.assertIsNone(child.span.unwrap().end_time) - - child.close() - - # Verify child span has ended. - self.assertIsNotNone(child.span.unwrap().end_time) - - # Verify parent span becomes active again. - self.assertEqual( - self.shim.active_span.context.unwrap(), - parent.span.context.unwrap(), - ) - - def test_parent_child_implicit(self): - """Test parent-child relationship and activation/deactivation of spans - without specifying the parent span upon creation. - """ - - with self.shim.start_active_span("ParentSpan") as parent: - # Verify parent span is the active span. - self.assertEqual( - self.shim.active_span.context.unwrap(), - parent.span.context.unwrap(), - ) - - with self.shim.start_active_span("ChildSpan") as child: - # Verify child span is the active span. - self.assertEqual( - self.shim.active_span.context.unwrap(), - child.span.context.unwrap(), - ) - - # Verify parent-child relationship. - parent_trace_id = ( - parent.span.unwrap().get_span_context().trace_id - ) - child_trace_id = ( - child.span.unwrap().get_span_context().trace_id - ) - - self.assertEqual(parent_trace_id, child_trace_id) - self.assertEqual( - child.span.unwrap().parent, - parent.span.unwrap().get_span_context(), - ) - - # Verify parent span becomes the active span again. - self.assertEqual( - self.shim.active_span.context.unwrap(), - parent.span.context.unwrap() - # TODO: Check equality of the spans themselves rather than - # their context once the SpanShim reconstruction problem has - # been addressed (see previous TODO). - ) - - # Verify there is no active span. - self.assertIsNone(self.shim.active_span) - - def test_parent_child_explicit_span(self): - """Test parent-child relationship of spans when specifying a `Span` - object as a parent upon creation. - """ - - with self.shim.start_span("ParentSpan") as parent: - with self.shim.start_active_span( - "ChildSpan", child_of=parent - ) as child: - parent_trace_id = parent.unwrap().get_span_context().trace_id - child_trace_id = ( - child.span.unwrap().get_span_context().trace_id - ) - - self.assertEqual(child_trace_id, parent_trace_id) - self.assertEqual( - child.span.unwrap().parent, - parent.unwrap().get_span_context(), - ) - - with self.shim.start_span("ParentSpan") as parent: - child = self.shim.start_span("ChildSpan", child_of=parent) - - parent_trace_id = parent.unwrap().get_span_context().trace_id - child_trace_id = child.unwrap().get_span_context().trace_id - - self.assertEqual(child_trace_id, parent_trace_id) - self.assertEqual( - child.unwrap().parent, parent.unwrap().get_span_context() - ) - - child.finish() - - def test_parent_child_explicit_span_context(self): - """Test parent-child relationship of spans when specifying a - `SpanContext` object as a parent upon creation. - """ - - with self.shim.start_span("ParentSpan") as parent: - with self.shim.start_active_span( - "ChildSpan", child_of=parent.context - ) as child: - parent_trace_id = parent.unwrap().get_span_context().trace_id - child_trace_id = ( - child.span.unwrap().get_span_context().trace_id - ) - - self.assertEqual(child_trace_id, parent_trace_id) - self.assertEqual( - child.span.unwrap().parent, parent.context.unwrap() - ) - - with self.shim.start_span("ParentSpan") as parent: - with self.shim.start_span( - "SpanWithContextParent", child_of=parent.context - ) as child: - parent_trace_id = parent.unwrap().get_span_context().trace_id - child_trace_id = child.unwrap().get_span_context().trace_id - - self.assertEqual(child_trace_id, parent_trace_id) - self.assertEqual( - child.unwrap().parent, parent.context.unwrap() - ) - - def test_references(self): - """Test span creation using the `references` argument.""" - - with self.shim.start_span("ParentSpan") as parent: - ref = opentracing.child_of(parent.context) - - with self.shim.start_active_span( - "ChildSpan", references=[ref] - ) as child: - self.assertEqual( - child.span.unwrap().links[0].context, - parent.context.unwrap(), - ) - - def test_set_operation_name(self): - """Test `set_operation_name()` method.""" - - with self.shim.start_active_span("TestName") as scope: - self.assertEqual(scope.span.unwrap().name, "TestName") - - scope.span.set_operation_name("NewName") - self.assertEqual(scope.span.unwrap().name, "NewName") - - def test_tags(self): - """Test tags behavior using the `tags` argument and the `set_tags()` - method. - """ - - tags = {"foo": "bar"} - with self.shim.start_active_span("TestSetTag", tags=tags) as scope: - scope.span.set_tag("baz", "qux") - - self.assertEqual(scope.span.unwrap().attributes["foo"], "bar") - self.assertEqual(scope.span.unwrap().attributes["baz"], "qux") - - def test_span_tracer(self): - """Test the `tracer` property on `Span` objects.""" - - with self.shim.start_active_span("TestSpan11") as scope: - self.assertEqual(scope.span.tracer, self.shim) - - def test_log_kv(self): - """Test the `log_kv()` method on `Span` objects.""" - - with self.shim.start_span("TestSpan12") as span: - span.log_kv({"foo": "bar"}) - self.assertEqual(span.unwrap().events[0].attributes["foo"], "bar") - # Verify timestamp was generated automatically. - self.assertIsNotNone(span.unwrap().events[0].timestamp) - - # Test explicit timestamp. - now = time.time() - span.log_kv({"foo": "bar"}, now) - result = util.time_seconds_from_ns( - span.unwrap().events[1].timestamp - ) - self.assertEqual(span.unwrap().events[1].attributes["foo"], "bar") - # Tolerate inaccuracies of less than a microsecond. See Note: - # https://open-telemetry.github.io/opentelemetry-python/instrumentation/opentracing_shim/opentracing_shim.html - # TODO: This seems to work consistently, but we should find out the - # biggest possible loss of precision. - self.assertAlmostEqual(result, now, places=6) - - def test_log(self): - """Test the deprecated `log` method on `Span` objects.""" - - with self.shim.start_span("TestSpan13") as span: - with self.assertWarns(DeprecationWarning): - span.log(event="foo", payload="bar") - - self.assertEqual(span.unwrap().events[0].attributes["event"], "foo") - self.assertEqual(span.unwrap().events[0].attributes["payload"], "bar") - self.assertIsNotNone(span.unwrap().events[0].timestamp) - - def test_log_event(self): - """Test the deprecated `log_event` method on `Span` objects.""" - - with self.shim.start_span("TestSpan14") as span: - with self.assertWarns(DeprecationWarning): - span.log_event("foo", "bar") - - self.assertEqual(span.unwrap().events[0].attributes["event"], "foo") - self.assertEqual(span.unwrap().events[0].attributes["payload"], "bar") - self.assertIsNotNone(span.unwrap().events[0].timestamp) - - def test_span_context(self): - """Test construction of `SpanContextShim` objects.""" - - otel_context = trace.SpanContext(1234, 5678, is_remote=False) - context = SpanContextShim(otel_context) - - self.assertIsInstance(context, opentracing.SpanContext) - self.assertEqual(context.unwrap().trace_id, 1234) - self.assertEqual(context.unwrap().span_id, 5678) - - def test_span_on_error(self): - """Verify error tag and logs are created on span when an exception is - raised. - """ - - # Raise an exception while a span is active. - with self.assertRaises(Exception): - with self.shim.start_active_span("TestName") as scope: - raise Exception - - # Verify exception details have been added to span. - self.assertEqual(scope.span.unwrap().attributes["error"], True) - - def test_inject_http_headers(self): - """Test `inject()` method for Format.HTTP_HEADERS.""" - - otel_context = trace.SpanContext( - trace_id=1220, span_id=7478, is_remote=False - ) - context = SpanContextShim(otel_context) - - headers = {} - self.shim.inject(context, opentracing.Format.HTTP_HEADERS, headers) - self.assertEqual( - headers[MockTextMapPropagator.TRACE_ID_KEY], str(1220) - ) - self.assertEqual(headers[MockTextMapPropagator.SPAN_ID_KEY], str(7478)) - - def test_inject_text_map(self): - """Test `inject()` method for Format.TEXT_MAP.""" - - otel_context = trace.SpanContext( - trace_id=1220, span_id=7478, is_remote=False - ) - context = SpanContextShim(otel_context) - - # Verify Format.TEXT_MAP - text_map = {} - self.shim.inject(context, opentracing.Format.TEXT_MAP, text_map) - self.assertEqual( - text_map[MockTextMapPropagator.TRACE_ID_KEY], str(1220) - ) - self.assertEqual( - text_map[MockTextMapPropagator.SPAN_ID_KEY], str(7478) - ) - - def test_inject_binary(self): - """Test `inject()` method for Format.BINARY.""" - - otel_context = trace.SpanContext( - trace_id=1220, span_id=7478, is_remote=False - ) - context = SpanContextShim(otel_context) - - # Verify exception for non supported binary format. - with self.assertRaises(opentracing.UnsupportedFormatException): - self.shim.inject(context, opentracing.Format.BINARY, bytearray()) - - def test_extract_http_headers(self): - """Test `extract()` method for Format.HTTP_HEADERS.""" - - carrier = { - MockTextMapPropagator.TRACE_ID_KEY: 1220, - MockTextMapPropagator.SPAN_ID_KEY: 7478, - } - - ctx = self.shim.extract(opentracing.Format.HTTP_HEADERS, carrier) - self.assertEqual(ctx.unwrap().trace_id, 1220) - self.assertEqual(ctx.unwrap().span_id, 7478) - - def test_extract_empty_context_returns_invalid_context(self): - """In the case where the propagator cannot extract a - SpanContext, extract should return and invalid span context. - """ - _old_propagator = propagators.get_global_textmap() - propagators.set_global_textmap(NOOPTextMapPropagator()) - try: - carrier = {} - - ctx = self.shim.extract(opentracing.Format.HTTP_HEADERS, carrier) - self.assertEqual(ctx.unwrap(), trace.INVALID_SPAN_CONTEXT) - finally: - propagators.set_global_textmap(_old_propagator) - - def test_extract_text_map(self): - """Test `extract()` method for Format.TEXT_MAP.""" - - carrier = { - MockTextMapPropagator.TRACE_ID_KEY: 1220, - MockTextMapPropagator.SPAN_ID_KEY: 7478, - } - - ctx = self.shim.extract(opentracing.Format.TEXT_MAP, carrier) - self.assertEqual(ctx.unwrap().trace_id, 1220) - self.assertEqual(ctx.unwrap().span_id, 7478) - - def test_extract_binary(self): - """Test `extract()` method for Format.BINARY.""" - - # Verify exception for non supported binary format. - with self.assertRaises(opentracing.UnsupportedFormatException): - self.shim.extract(opentracing.Format.BINARY, bytearray()) - - def test_baggage(self): - - span_context_shim = SpanContextShim( - trace.SpanContext(1234, 5678, is_remote=False) - ) - - baggage = span_context_shim.baggage - - with self.assertRaises(ValueError): - baggage[1] = 3 - - span_shim = SpanShim(Mock(), span_context_shim, Mock()) - - span_shim.set_baggage_item(1, 2) - - self.assertTrue(span_shim.get_baggage_item(1), 2) - - def test_active(self): - """Test that the active property and start_active_span return the same - object""" - - # Verify no span is currently active. - self.assertIsNone(self.shim.active_span) - - with self.shim.start_active_span("TestSpan15") as scope: - # Verify span is active. - self.assertEqual( - self.shim.active_span.context.unwrap(), - scope.span.context.unwrap(), - ) - - self.assertIs(self.shim.scope_manager.active, scope) - - # Verify no span is active. - self.assertIsNone(self.shim.active_span) - - def test_mixed_mode(self): - """Test that span parent-child relationship is kept between - OpenTelemetry and the OpenTracing shim""" - - span_shim = self.shim.start_span("TestSpan16") - - with self.shim.scope_manager.activate(span_shim, finish_on_close=True): - - with ( - TracerProvider() - .get_tracer(__name__) - .start_as_current_span("abc") - ) as opentelemetry_span: - - self.assertIs( - span_shim.unwrap().context, opentelemetry_span.parent, - ) - - with ( - TracerProvider().get_tracer(__name__).start_as_current_span("abc") - ) as opentelemetry_span: - - with self.shim.start_active_span("TestSpan17") as scope: - - self.assertIs( - scope.span.unwrap().parent, opentelemetry_span.context, - ) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/test_util.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/test_util.py deleted file mode 100644 index 806a8da609b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/test_util.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import time -import unittest - -from opentelemetry.instrumentation.opentracing_shim import util -from opentelemetry.util import time_ns - - -class TestUtil(unittest.TestCase): - def test_event_name_from_kv(self): - # Test basic behavior. - event_name = "send HTTP request" - res = util.event_name_from_kv({"event": event_name, "foo": "bar"}) - self.assertEqual(res, event_name) - - # Test None. - res = util.event_name_from_kv(None) - self.assertEqual(res, util.DEFAULT_EVENT_NAME) - - # Test empty dict. - res = util.event_name_from_kv({}) - self.assertEqual(res, util.DEFAULT_EVENT_NAME) - - # Test missing `event` field. - res = util.event_name_from_kv({"foo": "bar"}) - self.assertEqual(res, util.DEFAULT_EVENT_NAME) - - def test_time_seconds_to_ns(self): - time_seconds = time.time() - result = util.time_seconds_to_ns(time_seconds) - - self.assertEqual(result, int(time_seconds * 1e9)) - - def test_time_seconds_from_ns(self): - time_nanoseconds = time_ns() - result = util.time_seconds_from_ns(time_nanoseconds) - - self.assertEqual(result, time_nanoseconds / 1e9) - - def test_time_conversion_precision(self): - """Verify time conversion from seconds to nanoseconds and vice versa is - accurate enough. - """ - - time_seconds = 1570484241.9501917 - time_nanoseconds = util.time_seconds_to_ns(time_seconds) - result = util.time_seconds_from_ns(time_nanoseconds) - - # Tolerate inaccuracies of less than a microsecond. - # TODO: Put a link to an explanation in the docs. - # TODO: This seems to work consistently, but we should find out the - # biggest possible loss of precision. - self.assertAlmostEqual(result, time_seconds, places=6) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/README.rst b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/README.rst deleted file mode 100644 index ba7119cd68c..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/README.rst +++ /dev/null @@ -1,47 +0,0 @@ - -Testbed suite for the OpenTelemetry-OpenTracing Bridge -====================================================== - -Testbed suite designed to test the API changes. - -Build and test. ---------------- - -.. code-block:: sh - - tox -e py37-test-opentracing-shim - -Alternatively, due to the organization of the suite, it's possible to run directly the tests using ``py.test``\ : - -.. code-block:: sh - - py.test -s testbed/test_multiple_callbacks/test_threads.py - -Tested frameworks ------------------ - -Currently the examples cover ``threading`` and ``asyncio``. - -List of patterns ----------------- - - -* `Active Span replacement `_ - Start an isolated task and query for its results in another task/thread. -* `Client-Server `_ - Typical client-server example. -* `Common Request Handler `_ - One request handler for all requests. -* `Late Span finish `_ - Late parent ``Span`` finish. -* `Multiple callbacks `_ - Multiple callbacks spawned at the same time. -* `Nested callbacks `_ - One callback at a time, defined in a pipeline fashion. -* `Subtask Span propagation `_ - ``Span`` propagation for subtasks/coroutines. - -Adding new patterns -------------------- - -A new pattern is composed of a directory under *testbed* with the *test_* prefix, and containing the files for each platform, also with the *test_* prefix: - -.. code-block:: - - testbed/ - test_new_pattern/ - test_threads.py - test_asyncio.py diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/otel_ot_shim_tracer.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/otel_ot_shim_tracer.py deleted file mode 100644 index c12bbfa0291..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/otel_ot_shim_tracer.py +++ /dev/null @@ -1,26 +0,0 @@ -import opentelemetry.instrumentation.opentracing_shim as opentracingshim -from opentelemetry.sdk import trace -from opentelemetry.sdk.trace.export import SimpleExportSpanProcessor -from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( - InMemorySpanExporter, -) - - -class MockTracer(opentracingshim.TracerShim): - """Wrapper of `opentracingshim.TracerShim`. - - MockTracer extends `opentracingshim.TracerShim` by adding a in memory - span exporter that can be used to get the list of finished spans.""" - - def __init__(self): - tracer_provider = trace.TracerProvider() - oteltracer = tracer_provider.get_tracer(__name__) - super(MockTracer, self).__init__(oteltracer) - exporter = InMemorySpanExporter() - span_processor = SimpleExportSpanProcessor(exporter) - tracer_provider.add_span_processor(span_processor) - - self.exporter = exporter - - def finished_spans(self): - return self.exporter.get_finished_spans() diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_active_span_replacement/README.rst b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_active_span_replacement/README.rst deleted file mode 100644 index 6bb4d2f35c6..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_active_span_replacement/README.rst +++ /dev/null @@ -1,20 +0,0 @@ - -Active Span replacement example. -================================ - -This example shows a ``Span`` being created and then passed to an asynchronous task, which will temporary activate it to finish its processing, and further restore the previously active ``Span``. - -``threading`` implementation: - -.. code-block:: python - - # Create a new Span for this task - with self.tracer.start_active_span("task"): - - with self.tracer.scope_manager.activate(span, True): - # Simulate work strictly related to the initial Span - pass - - # Use the task span as parent of a new subtask - with self.tracer.start_active_span("subtask"): - pass diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_active_span_replacement/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_active_span_replacement/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_active_span_replacement/test_asyncio.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_active_span_replacement/test_asyncio.py deleted file mode 100644 index cb555dc1092..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_active_span_replacement/test_asyncio.py +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import print_function - -import asyncio - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import stop_loop_when - - -class TestAsyncio(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.loop = asyncio.get_event_loop() - - def test_main(self): - # Start an isolated task and query for its result -and finish it- - # in another task/thread - span = self.tracer.start_span("initial") - self.submit_another_task(span) - - stop_loop_when( - self.loop, - lambda: len(self.tracer.finished_spans()) >= 3, - timeout=5.0, - ) - self.loop.run_forever() - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 3) - self.assertNamesEqual(spans, ["initial", "subtask", "task"]) - - # task/subtask are part of the same trace, - # and subtask is a child of task - self.assertSameTrace(spans[1], spans[2]) - self.assertIsChildOf(spans[1], spans[2]) - - # initial task is not related in any way to those two tasks - self.assertNotSameTrace(spans[0], spans[1]) - self.assertEqual(spans[0].parent, None) - - async def task(self, span): - # Create a new Span for this task - with self.tracer.start_active_span("task"): - - with self.tracer.scope_manager.activate(span, True): - # Simulate work strictly related to the initial Span - pass - - # Use the task span as parent of a new subtask - with self.tracer.start_active_span("subtask"): - pass - - def submit_another_task(self, span): - self.loop.create_task(self.task(span)) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_active_span_replacement/test_threads.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_active_span_replacement/test_threads.py deleted file mode 100644 index e382d5d7167..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_active_span_replacement/test_threads.py +++ /dev/null @@ -1,50 +0,0 @@ -from __future__ import print_function - -from concurrent.futures import ThreadPoolExecutor - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase - - -class TestThreads(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - # use max_workers=3 as a general example even if only one would suffice - self.executor = ThreadPoolExecutor(max_workers=3) - - def test_main(self): - # Start an isolated task and query for its result -and finish it- - # in another task/thread - span = self.tracer.start_span("initial") - self.submit_another_task(span) - - self.executor.shutdown(True) - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 3) - self.assertNamesEqual(spans, ["initial", "subtask", "task"]) - - # task/subtask are part of the same trace, - # and subtask is a child of task - self.assertSameTrace(spans[1], spans[2]) - self.assertIsChildOf(spans[1], spans[2]) - - # initial task is not related in any way to those two tasks - self.assertNotSameTrace(spans[0], spans[1]) - self.assertEqual(spans[0].parent, None) - self.assertEqual(spans[2].parent, None) - - def task(self, span): - # Create a new Span for this task - with self.tracer.start_active_span("task"): - - with self.tracer.scope_manager.activate(span, True): - # Simulate work strictly related to the initial Span - pass - - # Use the task span as parent of a new subtask - with self.tracer.start_active_span("subtask"): - pass - - def submit_another_task(self, span): - self.executor.submit(self.task, span) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_client_server/README.rst b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_client_server/README.rst deleted file mode 100644 index 730fd9295da..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_client_server/README.rst +++ /dev/null @@ -1,19 +0,0 @@ - -Client-Server example. -====================== - -This example shows a ``Span`` created by a ``Client``, which will send a ``Message`` / ``SpanContext`` to a ``Server``, which will in turn extract such context and use it as parent of a new (server-side) ``Span``. - -``Client.send()`` is used to send messages and inject the ``SpanContext`` using the ``TEXT_MAP`` format, and ``Server.process()`` will process received messages and will extract the context used as parent. - -.. code-block:: python - - def send(self): - with self.tracer.start_active_span("send") as scope: - scope.span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) - - message = {} - self.tracer.inject(scope.span.context, - opentracing.Format.TEXT_MAP, - message) - self.queue.put(message) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_client_server/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_client_server/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_client_server/test_asyncio.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_client_server/test_asyncio.py deleted file mode 100644 index 5379584719a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_client_server/test_asyncio.py +++ /dev/null @@ -1,79 +0,0 @@ -from __future__ import print_function - -import asyncio - -import opentracing -from opentracing.ext import tags - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import get_logger, get_one_by_tag, stop_loop_when - -logger = get_logger(__name__) - - -class Server: - def __init__(self, *args, **kwargs): - tracer = kwargs.pop("tracer") - queue = kwargs.pop("queue") - super(Server, self).__init__(*args, **kwargs) - - self.tracer = tracer - self.queue = queue - - async def run(self): - value = await self.queue.get() - self.process(value) - - def process(self, message): - logger.info("Processing message in server") - - ctx = self.tracer.extract(opentracing.Format.TEXT_MAP, message) - with self.tracer.start_active_span("receive", child_of=ctx) as scope: - scope.span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_SERVER) - - -class Client: - def __init__(self, tracer, queue): - self.tracer = tracer - self.queue = queue - - async def send(self): - with self.tracer.start_active_span("send") as scope: - scope.span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) - - message = {} - self.tracer.inject( - scope.span.context, opentracing.Format.TEXT_MAP, message - ) - await self.queue.put(message) - - logger.info("Sent message from client") - - -class TestAsyncio(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.queue = asyncio.Queue() - self.loop = asyncio.get_event_loop() - self.server = Server(tracer=self.tracer, queue=self.queue) - - def test(self): - client = Client(self.tracer, self.queue) - self.loop.create_task(self.server.run()) - self.loop.create_task(client.send()) - - stop_loop_when( - self.loop, - lambda: len(self.tracer.finished_spans()) >= 2, - timeout=5.0, - ) - self.loop.run_forever() - - spans = self.tracer.finished_spans() - self.assertIsNotNone( - get_one_by_tag(spans, tags.SPAN_KIND, tags.SPAN_KIND_RPC_SERVER) - ) - self.assertIsNotNone( - get_one_by_tag(spans, tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) - ) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_client_server/test_threads.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_client_server/test_threads.py deleted file mode 100644 index 619781edecf..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_client_server/test_threads.py +++ /dev/null @@ -1,75 +0,0 @@ -from __future__ import print_function - -from queue import Queue -from threading import Thread - -import opentracing -from opentracing.ext import tags - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import await_until, get_logger, get_one_by_tag - -logger = get_logger(__name__) - - -class Server(Thread): - def __init__(self, *args, **kwargs): - tracer = kwargs.pop("tracer") - queue = kwargs.pop("queue") - super(Server, self).__init__(*args, **kwargs) - - self.daemon = True - self.tracer = tracer - self.queue = queue - - def run(self): - value = self.queue.get() - self.process(value) - - def process(self, message): - logger.info("Processing message in server") - - ctx = self.tracer.extract(opentracing.Format.TEXT_MAP, message) - with self.tracer.start_active_span("receive", child_of=ctx) as scope: - scope.span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_SERVER) - - -class Client: - def __init__(self, tracer, queue): - self.tracer = tracer - self.queue = queue - - def send(self): - with self.tracer.start_active_span("send") as scope: - scope.span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) - - message = {} - self.tracer.inject( - scope.span.context, opentracing.Format.TEXT_MAP, message - ) - self.queue.put(message) - - logger.info("Sent message from client") - - -class TestThreads(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.queue = Queue() - self.server = Server(tracer=self.tracer, queue=self.queue) - self.server.start() - - def test(self): - client = Client(self.tracer, self.queue) - client.send() - - await_until(lambda: len(self.tracer.finished_spans()) >= 2) - - spans = self.tracer.finished_spans() - self.assertIsNotNone( - get_one_by_tag(spans, tags.SPAN_KIND, tags.SPAN_KIND_RPC_SERVER) - ) - self.assertIsNotNone( - get_one_by_tag(spans, tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) - ) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/README.rst b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/README.rst deleted file mode 100644 index 1bcda539bbd..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/README.rst +++ /dev/null @@ -1,23 +0,0 @@ - -Common Request Handler example. -=============================== - -This example shows a ``Span`` used with ``RequestHandler``, which is used as a middleware (as in web frameworks) to manage a new ``Span`` per operation through its ``before_request()`` / ``after_response()`` methods. - -Implementation details: - - -* For ``threading``, no active ``Span`` is consumed as the tasks may be run concurrently on different threads, and an explicit ``SpanContext`` has to be saved to be used as parent. - -RequestHandler implementation: - -.. code-block:: python - - def before_request(self, request, request_context): - - # If we should ignore the active Span, use any passed SpanContext - # as the parent. Else, use the active one. - span = self.tracer.start_span("send", - child_of=self.context, - ignore_active_span=True) - diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/request_handler.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/request_handler.py deleted file mode 100644 index 47ff088025a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/request_handler.py +++ /dev/null @@ -1,38 +0,0 @@ -from __future__ import print_function - -from opentracing.ext import tags - -from ..utils import get_logger - -logger = get_logger(__name__) - - -class RequestHandler: - def __init__(self, tracer, context=None, ignore_active_span=True): - self.tracer = tracer - self.context = context - self.ignore_active_span = ignore_active_span - - def before_request(self, request, request_context): - logger.info("Before request %s", request) - - # If we should ignore the active Span, use any passed SpanContext - # as the parent. Else, use the active one. - if self.ignore_active_span: - span = self.tracer.start_span( - "send", child_of=self.context, ignore_active_span=True - ) - else: - span = self.tracer.start_span("send") - - span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) - - request_context["span"] = span - - def after_request(self, request, request_context): - # pylint: disable=no-self-use - logger.info("After request %s", request) - - span = request_context.get("span") - if span is not None: - span.finish() diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/test_asyncio.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/test_asyncio.py deleted file mode 100644 index b0216dd7560..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/test_asyncio.py +++ /dev/null @@ -1,136 +0,0 @@ -from __future__ import print_function - -import asyncio - -from opentracing.ext import tags - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import get_logger, get_one_by_operation_name, stop_loop_when -from .request_handler import RequestHandler - -logger = get_logger(__name__) - - -class Client: - def __init__(self, request_handler, loop): - self.request_handler = request_handler - self.loop = loop - - async def send_task(self, message): - request_context = {} - - async def before_handler(): - self.request_handler.before_request(message, request_context) - - async def after_handler(): - self.request_handler.after_request(message, request_context) - - await before_handler() - await after_handler() - - return "%s::response" % message - - def send(self, message): - return self.send_task(message) - - def send_sync(self, message): - return self.loop.run_until_complete(self.send_task(message)) - - -class TestAsyncio(OpenTelemetryTestCase): - """ - There is only one instance of 'RequestHandler' per 'Client'. Methods of - 'RequestHandler' are executed in different Tasks, and no Span propagation - among them is done automatically. - Therefore we cannot use current active span and activate span. - So one issue here is setting correct parent span. - """ - - def setUp(self): - self.tracer = MockTracer() - self.loop = asyncio.get_event_loop() - self.client = Client(RequestHandler(self.tracer), self.loop) - - def test_two_callbacks(self): - res_future1 = self.loop.create_task(self.client.send("message1")) - res_future2 = self.loop.create_task(self.client.send("message2")) - - stop_loop_when( - self.loop, - lambda: len(self.tracer.finished_spans()) >= 2, - timeout=5.0, - ) - self.loop.run_forever() - - self.assertEqual("message1::response", res_future1.result()) - self.assertEqual("message2::response", res_future2.result()) - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 2) - - for span in spans: - self.assertEqual( - span.attributes.get(tags.SPAN_KIND, None), - tags.SPAN_KIND_RPC_CLIENT, - ) - - self.assertNotSameTrace(spans[0], spans[1]) - self.assertIsNone(spans[0].parent) - self.assertIsNone(spans[1].parent) - - def test_parent_not_picked(self): - """Active parent should not be picked up by child.""" - - async def do_task(): - with self.tracer.start_active_span("parent"): - response = await self.client.send_task("no_parent") - self.assertEqual("no_parent::response", response) - - self.loop.run_until_complete(do_task()) - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 2) - - child_span = get_one_by_operation_name(spans, "send") - self.assertIsNotNone(child_span) - - parent_span = get_one_by_operation_name(spans, "parent") - self.assertIsNotNone(parent_span) - - # Here check that there is no parent-child relation. - self.assertIsNotChildOf(child_span, parent_span) - - def test_good_solution_to_set_parent(self): - """Asyncio and contextvars are integrated, in this case it is not needed - to activate current span by hand. - """ - - async def do_task(): - with self.tracer.start_active_span("parent"): - # Set ignore_active_span to False indicating that the - # framework will do it for us. - req_handler = RequestHandler( - self.tracer, ignore_active_span=False, - ) - client = Client(req_handler, self.loop) - response = await client.send_task("correct_parent") - - self.assertEqual("correct_parent::response", response) - - # Send second request, now there is no active parent, - # but it will be set, ups - response = await client.send_task("wrong_parent") - self.assertEqual("wrong_parent::response", response) - - self.loop.run_until_complete(do_task()) - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 3) - - spans = sorted(spans, key=lambda x: x.start_time) - parent_span = get_one_by_operation_name(spans, "parent") - self.assertIsNotNone(parent_span) - - self.assertIsChildOf(spans[1], parent_span) - self.assertIsNotChildOf(spans[2], parent_span) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/test_threads.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/test_threads.py deleted file mode 100644 index 4ab8b2a075e..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_common_request_handler/test_threads.py +++ /dev/null @@ -1,119 +0,0 @@ -from __future__ import print_function - -from concurrent.futures import ThreadPoolExecutor - -from opentracing.ext import tags - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import get_logger, get_one_by_operation_name -from .request_handler import RequestHandler - -logger = get_logger(__name__) - - -class Client: - def __init__(self, request_handler, executor): - self.request_handler = request_handler - self.executor = executor - - def send_task(self, message): - request_context = {} - - def before_handler(): - self.request_handler.before_request(message, request_context) - - def after_handler(): - self.request_handler.after_request(message, request_context) - - self.executor.submit(before_handler).result() - self.executor.submit(after_handler).result() - - return "%s::response" % message - - def send(self, message): - return self.executor.submit(self.send_task, message) - - def send_sync(self, message, timeout=5.0): - fut = self.executor.submit(self.send_task, message) - return fut.result(timeout=timeout) - - -class TestThreads(OpenTelemetryTestCase): - """ - There is only one instance of 'RequestHandler' per 'Client'. Methods of - 'RequestHandler' are executed concurrently in different threads which are - reused (executor). Therefore we cannot use current active span and - activate span. So one issue here is setting correct parent span. - """ - - def setUp(self): - self.tracer = MockTracer() - self.executor = ThreadPoolExecutor(max_workers=3) - self.client = Client(RequestHandler(self.tracer), self.executor) - - def test_two_callbacks(self): - response_future1 = self.client.send("message1") - response_future2 = self.client.send("message2") - - self.assertEqual("message1::response", response_future1.result(5.0)) - self.assertEqual("message2::response", response_future2.result(5.0)) - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 2) - - for span in spans: - self.assertEqual( - span.attributes.get(tags.SPAN_KIND, None), - tags.SPAN_KIND_RPC_CLIENT, - ) - - self.assertNotSameTrace(spans[0], spans[1]) - self.assertIsNone(spans[0].parent) - self.assertIsNone(spans[1].parent) - - def test_parent_not_picked(self): - """Active parent should not be picked up by child.""" - - with self.tracer.start_active_span("parent"): - response = self.client.send_sync("no_parent") - self.assertEqual("no_parent::response", response) - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 2) - - child_span = get_one_by_operation_name(spans, "send") - self.assertIsNotNone(child_span) - - parent_span = get_one_by_operation_name(spans, "parent") - self.assertIsNotNone(parent_span) - - # Here check that there is no parent-child relation. - self.assertIsNotChildOf(child_span, parent_span) - - def test_bad_solution_to_set_parent(self): - """Solution is bad because parent is per client and is not automatically - activated depending on the context. - """ - - with self.tracer.start_active_span("parent") as scope: - client = Client( - # Pass a span context to be used ad the parent. - RequestHandler(self.tracer, scope.span.context), - self.executor, - ) - response = client.send_sync("correct_parent") - self.assertEqual("correct_parent::response", response) - - response = client.send_sync("wrong_parent") - self.assertEqual("wrong_parent::response", response) - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 3) - - spans = sorted(spans, key=lambda x: x.start_time) - parent_span = get_one_by_operation_name(spans, "parent") - self.assertIsNotNone(parent_span) - - self.assertIsChildOf(spans[1], parent_span) - self.assertIsChildOf(spans[2], parent_span) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_late_span_finish/README.rst b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_late_span_finish/README.rst deleted file mode 100644 index 8c4ffd864ac..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_late_span_finish/README.rst +++ /dev/null @@ -1,18 +0,0 @@ - -Late Span finish example. -========================= - -This example shows a ``Span`` for a top-level operation, with independent, unknown lifetime, acting as parent of a few asynchronous subtasks (which must re-activate it but not finish it). - -.. code-block:: python - - # Fire away a few subtasks, passing a parent Span whose lifetime - # is not tied at all to the children. - def submit_subtasks(self, parent_span): - def task(name, interval): - with self.tracer.scope_manager.activate(parent_span, False): - with self.tracer.start_active_span(name): - time.sleep(interval) - - self.executor.submit(task, "task1", 0.1) - self.executor.submit(task, "task2", 0.3) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_late_span_finish/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_late_span_finish/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_late_span_finish/test_asyncio.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_late_span_finish/test_asyncio.py deleted file mode 100644 index 128073b056f..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_late_span_finish/test_asyncio.py +++ /dev/null @@ -1,51 +0,0 @@ -from __future__ import print_function - -import asyncio - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import get_logger, stop_loop_when - -logger = get_logger(__name__) - - -class TestAsyncio(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.loop = asyncio.get_event_loop() - - def test_main(self): - # Create a Span and use it as (explicit) parent of a pair of subtasks. - parent_span = self.tracer.start_span("parent") - self.submit_subtasks(parent_span) - - stop_loop_when( - self.loop, - lambda: len(self.tracer.finished_spans()) >= 2, - timeout=5.0, - ) - self.loop.run_forever() - - # Late-finish the parent Span now. - parent_span.finish() - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 3) - self.assertNamesEqual(spans, ["task1", "task2", "parent"]) - - for idx in range(2): - self.assertSameTrace(spans[idx], spans[-1]) - self.assertIsChildOf(spans[idx], spans[-1]) - self.assertTrue(spans[idx].end_time <= spans[-1].end_time) - - # Fire away a few subtasks, passing a parent Span whose lifetime - # is not tied at all to the children. - def submit_subtasks(self, parent_span): - async def task(name): - logger.info("Running %s", name) - with self.tracer.scope_manager.activate(parent_span, False): - with self.tracer.start_active_span(name): - await asyncio.sleep(0.1) - - self.loop.create_task(task("task1")) - self.loop.create_task(task("task2")) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_late_span_finish/test_threads.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_late_span_finish/test_threads.py deleted file mode 100644 index 5972eb8b924..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_late_span_finish/test_threads.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import print_function - -import time -from concurrent.futures import ThreadPoolExecutor - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase - - -class TestThreads(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.executor = ThreadPoolExecutor(max_workers=3) - - def test_main(self): - # Create a Span and use it as (explicit) parent of a pair of subtasks. - parent_span = self.tracer.start_span("parent") - self.submit_subtasks(parent_span) - - # Wait for the threadpool to be done. - self.executor.shutdown(True) - - # Late-finish the parent Span now. - parent_span.finish() - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 3) - self.assertNamesEqual(spans, ["task1", "task2", "parent"]) - - for idx in range(2): - self.assertSameTrace(spans[idx], spans[-1]) - self.assertIsChildOf(spans[idx], spans[-1]) - self.assertTrue(spans[idx].end_time <= spans[-1].end_time) - - # Fire away a few subtasks, passing a parent Span whose lifetime - # is not tied at all to the children. - def submit_subtasks(self, parent_span): - def task(name, interval): - with self.tracer.scope_manager.activate(parent_span, False): - with self.tracer.start_active_span(name): - time.sleep(interval) - - self.executor.submit(task, "task1", 0.1) - self.executor.submit(task, "task2", 0.3) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/README.rst b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/README.rst deleted file mode 100644 index 952d1ec51dd..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/README.rst +++ /dev/null @@ -1,19 +0,0 @@ - -Listener Response example. -========================== - -This example shows a ``Span`` created upon a message being sent to a ``Client``, and its handling along a related, **not shared** ``ResponseListener`` object with a ``on_response(self, response)`` method to finish it. - -.. code-block:: python - - def _task(self, message, listener): - res = "%s::response" % message - listener.on_response(res) - return res - - def send_sync(self, message): - span = self.tracer.start_span("send") - span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) - - listener = ResponseListener(span) - return self.executor.submit(self._task, message, listener).result() diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/response_listener.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/response_listener.py deleted file mode 100644 index dd143c20b8e..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/response_listener.py +++ /dev/null @@ -1,7 +0,0 @@ -class ResponseListener: - def __init__(self, span): - self.span = span - - def on_response(self, res): - del res - self.span.finish() diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/test_asyncio.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/test_asyncio.py deleted file mode 100644 index 085c0ea8134..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/test_asyncio.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import print_function - -import asyncio - -from opentracing.ext import tags - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import get_one_by_tag -from .response_listener import ResponseListener - - -class Client: - def __init__(self, tracer, loop): - self.tracer = tracer - self.loop = loop - - async def task(self, message, listener): - res = "%s::response" % message - listener.on_response(res) - return res - - def send_sync(self, message): - span = self.tracer.start_span("send") - span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) - - listener = ResponseListener(span) - return self.loop.run_until_complete(self.task(message, listener)) - - -class TestAsyncio(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.loop = asyncio.get_event_loop() - - def test_main(self): - client = Client(self.tracer, self.loop) - res = client.send_sync("message") - self.assertEqual(res, "message::response") - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 1) - - span = get_one_by_tag(spans, tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) - self.assertIsNotNone(span) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/test_threads.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/test_threads.py deleted file mode 100644 index 8f82e1fb158..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_listener_per_request/test_threads.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import print_function - -from concurrent.futures import ThreadPoolExecutor - -from opentracing.ext import tags - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import get_one_by_tag -from .response_listener import ResponseListener - - -class Client: - def __init__(self, tracer): - self.tracer = tracer - self.executor = ThreadPoolExecutor(max_workers=3) - - def _task(self, message, listener): - # pylint: disable=no-self-use - res = "%s::response" % message - listener.on_response(res) - return res - - def send_sync(self, message): - span = self.tracer.start_span("send") - span.set_tag(tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) - - listener = ResponseListener(span) - return self.executor.submit(self._task, message, listener).result() - - -class TestThreads(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - - def test_main(self): - client = Client(self.tracer) - res = client.send_sync("message") - self.assertEqual(res, "message::response") - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 1) - - span = get_one_by_tag(spans, tags.SPAN_KIND, tags.SPAN_KIND_RPC_CLIENT) - self.assertIsNotNone(span) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_multiple_callbacks/README.rst b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_multiple_callbacks/README.rst deleted file mode 100644 index 204f282cf23..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_multiple_callbacks/README.rst +++ /dev/null @@ -1,44 +0,0 @@ - -Multiple callbacks example. -=========================== - -This example shows a ``Span`` created for a top-level operation, covering a set of asynchronous operations (representing callbacks), and have this ``Span`` finished when **all** of them have been executed. - -``Client.send()`` is used to create a new asynchronous operation (callback), and in turn every operation both restores the active ``Span``, and creates a child ``Span`` (useful for measuring the performance of each callback). - -Implementation details: - - -* For ``threading``, a thread-safe counter is put in each ``Span`` to keep track of the pending callbacks, and call ``Span.finish()`` when the count becomes 0. -* For ``asyncio`` the children corotuines representing the subtasks are simply yielded over, so no counter is needed. - -``threading`` implementation: - -.. code-block:: python - - def task(self, interval, parent_span): - logger.info("Starting task") - - try: - scope = self.tracer.scope_manager.activate(parent_span, False) - with self.tracer.start_active_span("task"): - time.sleep(interval) - finally: - scope.close() - if parent_span._ref_count.decr() == 0: - parent_span.finish() - -``asyncio`` implementation: - -.. code-block:: python - - async def task(self, interval, parent_span): - logger.info("Starting task") - - with self.tracer.start_active_span("task"): - await asyncio.sleep(interval) - - # Invoke and yield over the corotuines. - with self.tracer.start_active_span("parent"): - tasks = self.submit_callbacks() - await asyncio.gather(*tasks) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_multiple_callbacks/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_multiple_callbacks/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_multiple_callbacks/test_asyncio.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_multiple_callbacks/test_asyncio.py deleted file mode 100644 index 36043d0a9bc..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_multiple_callbacks/test_asyncio.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import print_function - -import asyncio -import random - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import get_logger, stop_loop_when - -random.seed() -logger = get_logger(__name__) - - -class TestAsyncio(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.loop = asyncio.get_event_loop() - - def test_main(self): - # Need to run within a Task, as the scope manager depends - # on Task.current_task() - async def main_task(): - with self.tracer.start_active_span("parent"): - tasks = self.submit_callbacks() - await asyncio.gather(*tasks) - - self.loop.create_task(main_task()) - - stop_loop_when( - self.loop, - lambda: len(self.tracer.finished_spans()) >= 4, - timeout=5.0, - ) - self.loop.run_forever() - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 4) - self.assertNamesEqual(spans, ["task", "task", "task", "parent"]) - - for idx in range(3): - self.assertSameTrace(spans[idx], spans[-1]) - self.assertIsChildOf(spans[idx], spans[-1]) - - async def task(self, interval, parent_span): - logger.info("Starting task") - - with self.tracer.scope_manager.activate(parent_span, False): - with self.tracer.start_active_span("task"): - await asyncio.sleep(interval) - - def submit_callbacks(self): - parent_span = self.tracer.scope_manager.active.span - tasks = [] - for _ in range(3): - interval = 0.1 + random.randint(200, 500) * 0.001 - task = self.loop.create_task(self.task(interval, parent_span)) - tasks.append(task) - - return tasks diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_multiple_callbacks/test_threads.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_multiple_callbacks/test_threads.py deleted file mode 100644 index b24ae643e36..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_multiple_callbacks/test_threads.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import print_function - -import random -import time -from concurrent.futures import ThreadPoolExecutor - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import RefCount, get_logger - -random.seed() -logger = get_logger(__name__) - - -class TestThreads(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.executor = ThreadPoolExecutor(max_workers=3) - - def test_main(self): - try: - scope = self.tracer.start_active_span( - "parent", finish_on_close=False - ) - scope.span.ref_count = RefCount(1) - self.submit_callbacks(scope.span) - finally: - scope.close() - if scope.span.ref_count.decr() == 0: - scope.span.finish() - - self.executor.shutdown(True) - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 4) - self.assertNamesEqual(spans, ["task", "task", "task", "parent"]) - - for idx in range(3): - self.assertSameTrace(spans[idx], spans[-1]) - self.assertIsChildOf(spans[idx], spans[-1]) - - def task(self, interval, parent_span): - logger.info("Starting task") - - try: - scope = self.tracer.scope_manager.activate(parent_span, False) - with self.tracer.start_active_span("task"): - time.sleep(interval) - finally: - scope.close() - if parent_span.ref_count.decr() == 0: - parent_span.finish() - - def submit_callbacks(self, parent_span): - for _ in range(3): - parent_span.ref_count.incr() - self.executor.submit( - self.task, 0.1 + random.randint(200, 500) * 0.001, parent_span - ) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_nested_callbacks/README.rst b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_nested_callbacks/README.rst deleted file mode 100644 index c191431ccc4..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_nested_callbacks/README.rst +++ /dev/null @@ -1,47 +0,0 @@ - -Nested callbacks example. -========================= - -This example shows a ``Span`` for a top-level operation, and how it can be passed down on a list of nested callbacks (always one at a time), have it as the active one for each of them, and finished **only** when the last one executes. For Python, we have decided to do it in a **fire-and-forget** fashion. - -Implementation details: - - -* For ``threading``, the ``Span`` is manually activatted it in each corotuine/task. -* For ``asyncio``, the active ``Span`` is not activated down the chain as the ``Context`` automatically propagates it. - -``threading`` implementation: - -.. code-block:: python - - def submit(self): - span = self.tracer.scope_manager.active.span - - def task1(): - with self.tracer.scope_manager.activate(span, False): - span.set_tag("key1", "1") - - def task2(): - with self.tracer.scope_manager.activate(span, False): - span.set_tag("key2", "2") - ... - -``asyncio`` implementation: - -.. code-block:: python - - async def task1(): - span.set_tag("key1", "1") - - async def task2(): - span.set_tag("key2", "2") - - async def task3(): - span.set_tag("key3", "3") - span.finish() - - self.loop.create_task(task3()) - - self.loop.create_task(task2()) - - self.loop.create_task(task1()) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_nested_callbacks/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_nested_callbacks/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_nested_callbacks/test_asyncio.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_nested_callbacks/test_asyncio.py deleted file mode 100644 index 12eb4362770..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_nested_callbacks/test_asyncio.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import print_function - -import asyncio - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import stop_loop_when - - -class TestAsyncio(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.loop = asyncio.get_event_loop() - - def test_main(self): - # Start a Span and let the callback-chain - # finish it when the task is done - async def task(): - with self.tracer.start_active_span("one", finish_on_close=False): - self.submit() - - self.loop.create_task(task()) - - stop_loop_when( - self.loop, - lambda: len(self.tracer.finished_spans()) == 1, - timeout=5.0, - ) - self.loop.run_forever() - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 1) - self.assertEqual(spans[0].name, "one") - - for idx in range(1, 4): - self.assertEqual( - spans[0].attributes.get("key%s" % idx, None), str(idx) - ) - - def submit(self): - span = self.tracer.scope_manager.active.span - - async def task1(): - span.set_tag("key1", "1") - - async def task2(): - span.set_tag("key2", "2") - - async def task3(): - span.set_tag("key3", "3") - span.finish() - - self.loop.create_task(task3()) - - self.loop.create_task(task2()) - - self.loop.create_task(task1()) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_nested_callbacks/test_threads.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_nested_callbacks/test_threads.py deleted file mode 100644 index a1d35c35d88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_nested_callbacks/test_threads.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import print_function - -from concurrent.futures import ThreadPoolExecutor - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase -from ..utils import await_until - - -class TestThreads(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.executor = ThreadPoolExecutor(max_workers=3) - - def tearDown(self): - self.executor.shutdown(False) - - def test_main(self): - # Start a Span and let the callback-chain - # finish it when the task is done - with self.tracer.start_active_span("one", finish_on_close=False): - self.submit() - - # Cannot shutdown the executor and wait for the callbacks - # to be run, as in such case only the first will be executed, - # and the rest will get canceled. - await_until(lambda: len(self.tracer.finished_spans()) == 1, 5) - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 1) - self.assertEqual(spans[0].name, "one") - - for idx in range(1, 4): - self.assertEqual( - spans[0].attributes.get("key%s" % idx, None), str(idx) - ) - - def submit(self): - span = self.tracer.scope_manager.active.span - - def task1(): - with self.tracer.scope_manager.activate(span, False): - span.set_tag("key1", "1") - - def task2(): - with self.tracer.scope_manager.activate(span, False): - span.set_tag("key2", "2") - - def task3(): - with self.tracer.scope_manager.activate( - span, True - ): - span.set_tag("key3", "3") - - self.executor.submit(task3) - - self.executor.submit(task2) - - self.executor.submit(task1) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_subtask_span_propagation/README.rst b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_subtask_span_propagation/README.rst deleted file mode 100644 index eaeda8e6f81..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_subtask_span_propagation/README.rst +++ /dev/null @@ -1,42 +0,0 @@ - -Subtask Span propagation example. -================================= - -This example shows an active ``Span`` being simply propagated to the subtasks -either threads or coroutines-, and finished **by** the parent task. In real-life scenarios instrumentation libraries may help with ``Span`` propagation **if** not offered by default (see implementation details below), but we show here the case without such help. - -Implementation details: - -* For ``threading``, the ``Span`` is manually passed down the call chain, activating it in each corotuine/task. -* For ``asyncio``, the active ``Span`` is not passed nor activated down the chain as the ``Context`` automatically propagates it. - -``threading`` implementation: - -.. code-block:: python - - def parent_task(self, message): - with self.tracer.start_active_span("parent") as scope: - f = self.executor.submit(self.child_task, message, scope.span) - res = f.result() - - return res - - def child_task(self, message, span): - with self.tracer.scope_manager.activate(span, False): - with self.tracer.start_active_span("child"): - return "%s::response" % message - -``asyncio`` implementation: - -.. code-block:: python - - async def parent_task(self, message): # noqa - with self.tracer.start_active_span("parent"): - res = await self.child_task(message) - - return res - - async def child_task(self, message): - # No need to pass/activate the parent Span, as it stays in the context. - with self.tracer.start_active_span("child"): - return "%s::response" % message - diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_subtask_span_propagation/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_subtask_span_propagation/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_asyncio.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_asyncio.py deleted file mode 100644 index 6e544560704..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_asyncio.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import absolute_import, print_function - -import asyncio - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase - - -class TestAsyncio(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.loop = asyncio.get_event_loop() - - def test_main(self): - res = self.loop.run_until_complete(self.parent_task("message")) - self.assertEqual(res, "message::response") - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 2) - self.assertNamesEqual(spans, ["child", "parent"]) - self.assertIsChildOf(spans[0], spans[1]) - - async def parent_task(self, message): # noqa - with self.tracer.start_active_span("parent"): - res = await self.child_task(message) - - return res - - async def child_task(self, message): - # No need to pass/activate the parent Span, as it stays in the context. - with self.tracer.start_active_span("child"): - return "%s::response" % message diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_threads.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_threads.py deleted file mode 100644 index 1ba5f697cad..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/test_subtask_span_propagation/test_threads.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import absolute_import, print_function - -from concurrent.futures import ThreadPoolExecutor - -from ..otel_ot_shim_tracer import MockTracer -from ..testcase import OpenTelemetryTestCase - - -class TestThreads(OpenTelemetryTestCase): - def setUp(self): - self.tracer = MockTracer() - self.executor = ThreadPoolExecutor(max_workers=3) - - def test_main(self): - res = self.executor.submit(self.parent_task, "message").result() - self.assertEqual(res, "message::response") - - spans = self.tracer.finished_spans() - self.assertEqual(len(spans), 2) - self.assertNamesEqual(spans, ["child", "parent"]) - self.assertIsChildOf(spans[0], spans[1]) - - def parent_task(self, message): - with self.tracer.start_active_span("parent") as scope: - fut = self.executor.submit(self.child_task, message, scope.span) - res = fut.result() - - return res - - def child_task(self, message, span): - with self.tracer.scope_manager.activate(span, False): - with self.tracer.start_active_span("child"): - return "%s::response" % message diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/testcase.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/testcase.py deleted file mode 100644 index c1ce6ea5abd..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/testcase.py +++ /dev/null @@ -1,46 +0,0 @@ -import unittest - -import opentelemetry.trace as trace_api - - -# pylint: disable=C0103 -class OpenTelemetryTestCase(unittest.TestCase): - def assertSameTrace(self, spanA, spanB): - return self.assertEqual(spanA.context.trace_id, spanB.context.trace_id) - - def assertNotSameTrace(self, spanA, spanB): - return self.assertNotEqual( - spanA.context.trace_id, spanB.context.trace_id - ) - - def assertIsChildOf(self, spanA, spanB): - # spanA is child of spanB - self.assertIsNotNone(spanA.parent) - - ctxA = spanA.parent - if isinstance(spanA.parent, trace_api.Span): - ctxA = spanA.parent.context - - ctxB = spanB - if isinstance(ctxB, trace_api.Span): - ctxB = spanB.context - - return self.assertEqual(ctxA.span_id, ctxB.span_id) - - def assertIsNotChildOf(self, spanA, spanB): - # spanA is NOT child of spanB - if spanA.parent is None: - return - - ctxA = spanA.parent - if isinstance(spanA.parent, trace_api.Span): - ctxA = spanA.parent.context - - ctxB = spanB - if isinstance(ctxB, trace_api.Span): - ctxB = spanB.context - - self.assertNotEqual(ctxA.span_id, ctxB.span_id) - - def assertNamesEqual(self, spans, names): - self.assertEqual(list(map(lambda x: x.name, spans)), names) diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/utils.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/utils.py deleted file mode 100644 index a7b977f3d76..00000000000 --- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/testbed/utils.py +++ /dev/null @@ -1,78 +0,0 @@ -from __future__ import print_function - -import logging -import threading -import time - - -class RefCount: - """Thread-safe counter""" - - def __init__(self, count=1): - self._lock = threading.Lock() - self._count = count - - def incr(self): - with self._lock: - self._count += 1 - return self._count - - def decr(self): - with self._lock: - self._count -= 1 - return self._count - - -def await_until(func, timeout=5.0): - """Polls for func() to return True""" - end_time = time.time() + timeout - while time.time() < end_time and not func(): - time.sleep(0.01) - - -def stop_loop_when(loop, cond_func, timeout=5.0): - """ - Registers a periodic callback that stops the loop when cond_func() == True. - Compatible with both Tornado and asyncio. - """ - if cond_func() or timeout <= 0.0: - loop.stop() - return - - timeout -= 0.1 - loop.call_later(0.1, stop_loop_when, loop, cond_func, timeout) - - -def get_logger(name): - """Returns a logger with log level set to INFO""" - logging.basicConfig(level=logging.INFO) - return logging.getLogger(name) - - -def get_one_by_tag(spans, key, value): - """Return a single Span with a tag value/key from a list, - errors if more than one is found.""" - - found = [] - for span in spans: - if span.attributes.get(key) == value: - found.append(span) - - if len(found) > 1: - raise RuntimeError("Too many values") - - return found[0] if len(found) > 0 else None - - -def get_one_by_operation_name(spans, name): - """Return a single Span with a name from a list, - errors if more than one is found.""" - found = [] - for span in spans: - if span.name == name: - found.append(span) - - if len(found) > 1: - raise RuntimeError("Too many values") - - return found[0] if len(found) > 0 else None diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-psycopg2/CHANGELOG.md deleted file mode 100644 index 2aa477de2db..00000000000 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/CHANGELOG.md +++ /dev/null @@ -1,29 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-psycopg2 - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) - -## 0.8b0 - -Released 2020-05-27 - -- Implement instrumentor interface, enabling auto-instrumentation ([#694]https://github.com/open-telemetry/opentelemetry-python/pull/694) - -## 0.4a0 - -Released 2020-02-21 - -- Initial release \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/LICENSE b/instrumentation/opentelemetry-instrumentation-psycopg2/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-psycopg2/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-psycopg2/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-psycopg2/README.rst b/instrumentation/opentelemetry-instrumentation-psycopg2/README.rst deleted file mode 100644 index 3ab1025eaef..00000000000 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/README.rst +++ /dev/null @@ -1,20 +0,0 @@ -OpenTelemetry Psycopg Instrumentation -===================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-psycopg2.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-psycopg2/ - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-psycopg2 - - -References ----------- -* `OpenTelemetry Psycopg Instrumentation `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/setup.cfg b/instrumentation/opentelemetry-instrumentation-psycopg2/setup.cfg deleted file mode 100644 index db98bf1251d..00000000000 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/setup.cfg +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-psycopg2 -description = OpenTelemetry psycopg2 instrumentation -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/master/instrumentation/opentelemetry-instrumentation-psycopg2 -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation-dbapi == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - psycopg2-binary >= 2.7.3.1 - wrapt >= 1.0.0, < 2.0.0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - psycopg2 = opentelemetry.instrumentation.psycopg2:Psycopg2Instrumentor diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/setup.py b/instrumentation/opentelemetry-instrumentation-psycopg2/setup.py deleted file mode 100644 index 4bd0e16be54..00000000000 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "psycopg2", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py b/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py deleted file mode 100644 index 7782f6fe632..00000000000 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -The integration with PostgreSQL supports the `Psycopg`_ library, it can be enabled by -using ``Psycopg2Instrumentor``. - -.. _Psycopg: http://initd.org/psycopg/ - -Usage ------ - -.. code-block:: python - - import psycopg2 - from opentelemetry import trace - from opentelemetry.sdk.trace import TracerProvider - from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor - - trace.set_tracer_provider(TracerProvider()) - - Psycopg2Instrumentor().instrument() - - cnx = psycopg2.connect(database='Database') - cursor = cnx.cursor() - cursor.execute("INSERT INTO test (testField) VALUES (123)") - cursor.close() - cnx.close() - -API ---- -""" - -import psycopg2 - -from opentelemetry.instrumentation import dbapi -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.psycopg2.version import __version__ -from opentelemetry.trace import get_tracer - - -class Psycopg2Instrumentor(BaseInstrumentor): - _CONNECTION_ATTRIBUTES = { - "database": "info.dbname", - "port": "info.port", - "host": "info.host", - "user": "info.user", - } - - _DATABASE_COMPONENT = "postgresql" - _DATABASE_TYPE = "sql" - - def _instrument(self, **kwargs): - """Integrate with PostgreSQL Psycopg library. - Psycopg: http://initd.org/psycopg/ - """ - - tracer_provider = kwargs.get("tracer_provider") - - dbapi.wrap_connect( - __name__, - psycopg2, - "connect", - self._DATABASE_COMPONENT, - self._DATABASE_TYPE, - self._CONNECTION_ATTRIBUTES, - version=__version__, - tracer_provider=tracer_provider, - ) - - def _uninstrument(self, **kwargs): - """"Disable Psycopg2 instrumentation""" - dbapi.unwrap_connect(psycopg2, "connect") - - # pylint:disable=no-self-use - def instrument_connection(self, connection): - """Enable instrumentation in a Psycopg2 connection. - - Args: - connection: The connection to instrument. - - Returns: - An instrumented connection. - """ - tracer = get_tracer(__name__, __version__) - - return dbapi.instrument_connection( - tracer, - connection, - self._DATABASE_COMPONENT, - self._DATABASE_TYPE, - self._CONNECTION_ATTRIBUTES, - ) - - def uninstrument_connection(self, connection): - """Disable instrumentation in a Psycopg2 connection. - - Args: - connection: The connection to uninstrument. - - Returns: - An uninstrumented connection. - """ - return dbapi.uninstrument_connection(connection) diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/version.py b/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-psycopg2/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py b/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py deleted file mode 100644 index cb127c7a5ef..00000000000 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/tests/test_psycopg2_integration.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest import mock - -import psycopg2 - -import opentelemetry.instrumentation.psycopg2 -from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor -from opentelemetry.sdk import resources -from opentelemetry.test.test_base import TestBase - - -class TestPostgresqlIntegration(TestBase): - def tearDown(self): - super().tearDown() - with self.disable_logging(): - Psycopg2Instrumentor().uninstrument() - - @mock.patch("psycopg2.connect") - # pylint: disable=unused-argument - def test_instrumentor(self, mock_connect): - Psycopg2Instrumentor().instrument() - - cnx = psycopg2.connect(database="test") - - cursor = cnx.cursor() - - query = "SELECT * FROM test" - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - # Check version and name in span's instrumentation info - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.psycopg2 - ) - - # check that no spans are generated after uninstrument - Psycopg2Instrumentor().uninstrument() - - cnx = psycopg2.connect(database="test") - cursor = cnx.cursor() - query = "SELECT * FROM test" - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - @mock.patch("psycopg2.connect") - # pylint: disable=unused-argument - def test_not_recording(self, mock_connect): - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - Psycopg2Instrumentor().instrument() - with mock.patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - cnx = psycopg2.connect(database="test") - cursor = cnx.cursor() - query = "SELECT * FROM test" - cursor.execute(query) - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - Psycopg2Instrumentor().uninstrument() - - @mock.patch("psycopg2.connect") - # pylint: disable=unused-argument - def test_custom_tracer_provider(self, mock_connect): - resource = resources.Resource.create({}) - result = self.create_tracer_provider(resource=resource) - tracer_provider, exporter = result - - Psycopg2Instrumentor().instrument(tracer_provider=tracer_provider) - - cnx = psycopg2.connect(database="test") - cursor = cnx.cursor() - query = "SELECT * FROM test" - cursor.execute(query) - - spans_list = exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - self.assertIs(span.resource, resource) - - @mock.patch("psycopg2.connect") - # pylint: disable=unused-argument - def test_instrument_connection(self, mock_connect): - cnx = psycopg2.connect(database="test") - query = "SELECT * FROM test" - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 0) - - cnx = Psycopg2Instrumentor().instrument_connection(cnx) - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - @mock.patch("psycopg2.connect") - # pylint: disable=unused-argument - def test_uninstrument_connection(self, mock_connect): - Psycopg2Instrumentor().instrument() - cnx = psycopg2.connect(database="test") - query = "SELECT * FROM test" - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - cnx = Psycopg2Instrumentor().uninstrument_connection(cnx) - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-pymemcache/CHANGELOG.md deleted file mode 100644 index 5cff2cb6e62..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-pymemcache - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) - -## Version 0.10b0 - -Released 2020-06-23 - -- Initial release \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/LICENSE b/instrumentation/opentelemetry-instrumentation-pymemcache/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-pymemcache/MANIFEST.IN b/instrumentation/opentelemetry-instrumentation-pymemcache/MANIFEST.IN deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/MANIFEST.IN +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-pymemcache/README.rst b/instrumentation/opentelemetry-instrumentation-pymemcache/README.rst deleted file mode 100644 index f126f4246dc..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/README.rst +++ /dev/null @@ -1,20 +0,0 @@ -OpenTelemetry pymemcache Instrumentation -======================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-pymemcache.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-pymemcache/ - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-pymemcache - - -References ----------- -* `OpenTelemetry Pymemcache Instrumentation `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/setup.cfg b/instrumentation/opentelemetry-instrumentation-pymemcache/setup.cfg deleted file mode 100644 index c352ba903c7..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/setup.cfg +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-pymemcache -description = OpenTelemetry pymemcache instrumentation -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/instrumentation/opentelemetry-instrumentation-pymemcache -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - pymemcache ~= 1.3 - wrapt >= 1.0.0, < 2.0.0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - pymemcache = opentelemetry.instrumentation.pymemcache:PymemcacheInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/setup.py b/instrumentation/opentelemetry-instrumentation-pymemcache/setup.py deleted file mode 100644 index 46bf607933b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/setup.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "pymemcache", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/__init__.py b/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/__init__.py deleted file mode 100644 index a91d3b525a3..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/__init__.py +++ /dev/null @@ -1,199 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" - -Usage ------ - -The OpenTelemetry ``pymemcache`` integration traces pymemcache client operations - -Usage ------ - -.. code-block:: python - - from opentelemetry import trace - from opentelemetry.sdk.trace import TracerProvider - from opentelemetry.instrumentation.pymemcache import PymemcacheInstrumentor - trace.set_tracer_provider(TracerProvider()) - PymemcacheInstrumentor().instrument() - from pymemcache.client.base import Client - client = Client(('localhost', 11211)) - client.set('some_key', 'some_value') - -API ---- -""" -# pylint: disable=no-value-for-parameter - -import logging - -import pymemcache -from wrapt import ObjectProxy -from wrapt import wrap_function_wrapper as _wrap - -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.pymemcache.version import __version__ -from opentelemetry.instrumentation.utils import unwrap -from opentelemetry.trace import SpanKind, get_tracer - -logger = logging.getLogger(__name__) - -# Network attribute semantic convention here: -# https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/span-general.md#general-network-connection-attributes -_HOST = "net.peer.name" -_PORT = "net.peer.port" -# Database semantic conventions here: -# https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md -_DB = "db.type" -_URL = "db.url" - -_DEFAULT_SERVICE = "memcached" -_RAWCMD = "db.statement" -_CMD = "memcached.command" -COMMANDS = [ - "set", - "set_many", - "add", - "replace", - "append", - "prepend", - "cas", - "get", - "get_many", - "gets", - "gets_many", - "delete", - "delete_many", - "incr", - "decr", - "touch", - "stats", - "version", - "flush_all", - "quit", - "set_multi", - "get_multi", -] - - -def _set_connection_attributes(span, instance): - if not span.is_recording(): - return - for key, value in _get_address_attributes(instance).items(): - span.set_attribute(key, value) - - -def _with_tracer_wrapper(func): - """Helper for providing tracer for wrapper functions.""" - - def _with_tracer(tracer, cmd): - def wrapper(wrapped, instance, args, kwargs): - # prevent double wrapping - if hasattr(wrapped, "__wrapped__"): - return wrapped(*args, **kwargs) - - return func(tracer, cmd, wrapped, instance, args, kwargs) - - return wrapper - - return _with_tracer - - -@_with_tracer_wrapper -def _wrap_cmd(tracer, cmd, wrapped, instance, args, kwargs): - with tracer.start_as_current_span( - _CMD, kind=SpanKind.INTERNAL, attributes={} - ) as span: - try: - if span.is_recording(): - if not args: - vals = "" - else: - vals = _get_query_string(args[0]) - - query = "{}{}{}".format(cmd, " " if vals else "", vals) - span.set_attribute(_RAWCMD, query) - - _set_connection_attributes(span, instance) - except Exception as ex: # pylint: disable=broad-except - logger.warning( - "Failed to set attributes for pymemcache span %s", str(ex) - ) - - return wrapped(*args, **kwargs) - - -def _get_query_string(arg): - - """Return the query values given the first argument to a pymemcache command. - - If there are multiple query values, they are joined together - space-separated. - """ - keys = "" - - if isinstance(arg, dict): - arg = list(arg) - - if isinstance(arg, str): - keys = arg - elif isinstance(arg, bytes): - keys = arg.decode() - elif isinstance(arg, list) and len(arg) >= 1: - if isinstance(arg[0], str): - keys = " ".join(arg) - elif isinstance(arg[0], bytes): - keys = b" ".join(arg).decode() - - return keys - - -def _get_address_attributes(instance): - """Attempt to get host and port from Client instance.""" - address_attributes = {} - address_attributes[_DB] = "memcached" - - # client.base.Client contains server attribute which is either a host/port tuple, or unix socket path string - # https://github.com/pinterest/pymemcache/blob/f02ddf73a28c09256589b8afbb3ee50f1171cac7/pymemcache/client/base.py#L228 - if hasattr(instance, "server"): - if isinstance(instance.server, tuple): - host, port = instance.server - address_attributes[_HOST] = host - address_attributes[_PORT] = port - address_attributes[_URL] = "memcached://{}:{}".format(host, port) - elif isinstance(instance.server, str): - address_attributes[_URL] = "memcached://{}".format(instance.server) - - return address_attributes - - -class PymemcacheInstrumentor(BaseInstrumentor): - """An instrumentor for pymemcache See `BaseInstrumentor`""" - - def _instrument(self, **kwargs): - tracer_provider = kwargs.get("tracer_provider") - tracer = get_tracer(__name__, __version__, tracer_provider) - - for cmd in COMMANDS: - _wrap( - "pymemcache.client.base", - "Client.{}".format(cmd), - _wrap_cmd(tracer, cmd), - ) - - def _uninstrument(self, **kwargs): - for command in COMMANDS: - unwrap(pymemcache.client.base.Client, "{}".format(command)) diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/version.py b/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/src/opentelemetry/instrumentation/pymemcache/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-pymemcache/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py b/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py deleted file mode 100644 index 4efb50eb4f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py +++ /dev/null @@ -1,541 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from unittest import mock - -import pymemcache -from pymemcache.exceptions import ( - MemcacheClientError, - MemcacheIllegalInputError, - MemcacheServerError, - MemcacheUnknownCommandError, - MemcacheUnknownError, -) - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.pymemcache import PymemcacheInstrumentor -from opentelemetry.test.test_base import TestBase -from opentelemetry.trace import get_tracer - -from .utils import MockSocket, _str - -TEST_HOST = "localhost" -TEST_PORT = 117711 - - -class PymemcacheClientTestCase( - TestBase -): # pylint: disable=too-many-public-methods - """ Tests for a patched pymemcache.client.base.Client. """ - - def setUp(self): - super().setUp() - PymemcacheInstrumentor().instrument() - - # pylint: disable=protected-access - self.tracer = get_tracer(__name__) - - def tearDown(self): - super().tearDown() - PymemcacheInstrumentor().uninstrument() - - def make_client(self, mock_socket_values, **kwargs): - # pylint: disable=attribute-defined-outside-init - self.client = pymemcache.client.base.Client( - (TEST_HOST, TEST_PORT), **kwargs - ) - self.client.sock = MockSocket(list(mock_socket_values)) - return self.client - - def check_spans(self, spans, num_expected, queries_expected): - """A helper for validating basic span information.""" - self.assertEqual(num_expected, len(spans)) - - for span, query in zip(spans, queries_expected): - self.assertEqual(span.name, "memcached.command") - self.assertIs(span.kind, trace_api.SpanKind.INTERNAL) - self.assertEqual( - span.attributes["net.peer.name"], "{}".format(TEST_HOST) - ) - self.assertEqual(span.attributes["net.peer.port"], TEST_PORT) - self.assertEqual(span.attributes["db.type"], "memcached") - self.assertEqual( - span.attributes["db.url"], - "memcached://{}:{}".format(TEST_HOST, TEST_PORT), - ) - self.assertEqual(span.attributes["db.statement"], query) - - def test_set_success(self): - client = self.make_client([b"STORED\r\n"]) - result = client.set(b"key", b"value", noreply=False) - self.assertTrue(result) - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["set key"]) - - def test_set_not_recording(self): - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - with mock.patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - client = self.make_client([b"STORED\r\n"]) - result = client.set(b"key", b"value", noreply=False) - self.assertTrue(result) - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_get_many_none_found(self): - client = self.make_client([b"END\r\n"]) - result = client.get_many([b"key1", b"key2"]) - assert result == {} - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["get_many key1 key2"]) - - def test_get_multi_none_found(self): - client = self.make_client([b"END\r\n"]) - # alias for get_many - result = client.get_multi([b"key1", b"key2"]) - assert result == {} - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["get_multi key1 key2"]) - - def test_set_multi_success(self): - client = self.make_client([b"STORED\r\n"]) - # Alias for set_many, a convienance function that calls set for every key - result = client.set_multi({b"key": b"value"}, noreply=False) - self.assertTrue(result) - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 2, ["set key", "set_multi key"]) - - def test_delete_not_found(self): - client = self.make_client([b"NOT_FOUND\r\n"]) - result = client.delete(b"key", noreply=False) - assert result is False - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["delete key"]) - - def test_incr_found(self): - client = self.make_client([b"STORED\r\n", b"1\r\n"]) - client.set(b"key", 0, noreply=False) - result = client.incr(b"key", 1, noreply=False) - assert result == 1 - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 2, ["set key", "incr key"]) - - def test_get_found(self): - client = self.make_client( - [b"STORED\r\n", b"VALUE key 0 5\r\nvalue\r\nEND\r\n"] - ) - result = client.set(b"key", b"value", noreply=False) - result = client.get(b"key") - assert result == b"value" - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 2, ["set key", "get key"]) - - def test_decr_found(self): - client = self.make_client([b"STORED\r\n", b"1\r\n"]) - client.set(b"key", 2, noreply=False) - result = client.decr(b"key", 1, noreply=False) - assert result == 1 - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 2, ["set key", "decr key"]) - - def test_add_stored(self): - client = self.make_client([b"STORED\r", b"\n"]) - result = client.add(b"key", b"value", noreply=False) - self.assertTrue(result) - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["add key"]) - - def test_delete_many_found(self): - client = self.make_client([b"STORED\r", b"\n", b"DELETED\r\n"]) - result = client.add(b"key", b"value", noreply=False) - # a convienance function that calls delete for every key - result = client.delete_many([b"key"], noreply=False) - self.assertTrue(result) - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans( - spans, 3, ["add key", "delete key", "delete_many key"] - ) - - def test_set_many_success(self): - client = self.make_client([b"STORED\r\n"]) - # a convienance function that calls set for every key - result = client.set_many({b"key": b"value"}, noreply=False) - self.assertTrue(result) - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 2, ["set key", "set_many key"]) - - def test_set_get(self): - client = self.make_client( - [b"STORED\r\n", b"VALUE key 0 5\r\nvalue\r\nEND\r\n"] - ) - client.set(b"key", b"value", noreply=False) - result = client.get(b"key") - assert _str(result) == "value" - - spans = self.memory_exporter.get_finished_spans() - - self.assertEqual(len(spans), 2) - self.assertEqual( - spans[0].attributes["db.url"], - "memcached://{}:{}".format(TEST_HOST, TEST_PORT), - ) - - def test_append_stored(self): - client = self.make_client([b"STORED\r\n"]) - result = client.append(b"key", b"value", noreply=False) - self.assertTrue(result) - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["append key"]) - - def test_prepend_stored(self): - client = self.make_client([b"STORED\r\n"]) - result = client.prepend(b"key", b"value", noreply=False) - self.assertTrue(result) - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["prepend key"]) - - def test_cas_stored(self): - client = self.make_client([b"STORED\r\n"]) - result = client.cas(b"key", b"value", b"cas", noreply=False) - self.assertTrue(result) - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["cas key"]) - - def test_cas_exists(self): - client = self.make_client([b"EXISTS\r\n"]) - result = client.cas(b"key", b"value", b"cas", noreply=False) - assert result is False - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["cas key"]) - - def test_cas_not_found(self): - client = self.make_client([b"NOT_FOUND\r\n"]) - result = client.cas(b"key", b"value", b"cas", noreply=False) - assert result is None - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["cas key"]) - - def test_delete_exception(self): - client = self.make_client([Exception("fail")]) - - def _delete(): - client.delete(b"key", noreply=False) - - with self.assertRaises(Exception): - _delete() - - spans = self.memory_exporter.get_finished_spans() - - span = spans[0] - - self.assertFalse(span.status.is_ok) - - self.check_spans(spans, 1, ["delete key"]) - - def test_flush_all(self): - client = self.make_client([b"OK\r\n"]) - result = client.flush_all(noreply=False) - self.assertTrue(result) - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["flush_all"]) - - def test_incr_exception(self): - client = self.make_client([Exception("fail")]) - - def _incr(): - client.incr(b"key", 1) - - with self.assertRaises(Exception): - _incr() - - spans = self.memory_exporter.get_finished_spans() - - span = spans[0] - - self.assertFalse(span.status.is_ok) - - self.check_spans(spans, 1, ["incr key"]) - - def test_get_error(self): - client = self.make_client([b"ERROR\r\n"]) - - def _get(): - client.get(b"key") - - with self.assertRaises(MemcacheUnknownCommandError): - _get() - - spans = self.memory_exporter.get_finished_spans() - - span = spans[0] - - self.assertFalse(span.status.is_ok) - - self.check_spans(spans, 1, ["get key"]) - - def test_get_unknown_error(self): - client = self.make_client([b"foobarbaz\r\n"]) - - def _get(): - client.get(b"key") - - with self.assertRaises(MemcacheUnknownError): - _get() - - spans = self.memory_exporter.get_finished_spans() - - span = spans[0] - - self.assertFalse(span.status.is_ok) - - self.check_spans(spans, 1, ["get key"]) - - def test_gets_found(self): - client = self.make_client([b"VALUE key 0 5 10\r\nvalue\r\nEND\r\n"]) - result = client.gets(b"key") - assert result == (b"value", b"10") - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["gets key"]) - - def test_touch_not_found(self): - client = self.make_client([b"NOT_FOUND\r\n"]) - result = client.touch(b"key", noreply=False) - assert result is False - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["touch key"]) - - def test_set_client_error(self): - client = self.make_client([b"CLIENT_ERROR some message\r\n"]) - - def _set(): - client.set("key", "value", noreply=False) - - with self.assertRaises(MemcacheClientError): - _set() - - spans = self.memory_exporter.get_finished_spans() - - span = spans[0] - - self.assertFalse(span.status.is_ok) - - self.check_spans(spans, 1, ["set key"]) - - def test_set_server_error(self): - client = self.make_client([b"SERVER_ERROR some message\r\n"]) - - def _set(): - client.set(b"key", b"value", noreply=False) - - with self.assertRaises(MemcacheServerError): - _set() - - spans = self.memory_exporter.get_finished_spans() - - span = spans[0] - - self.assertFalse(span.status.is_ok) - - self.check_spans(spans, 1, ["set key"]) - - def test_set_key_with_space(self): - client = self.make_client([b""]) - - def _set(): - client.set(b"key has space", b"value", noreply=False) - - with self.assertRaises(MemcacheIllegalInputError): - _set() - - spans = self.memory_exporter.get_finished_spans() - - span = spans[0] - - self.assertFalse(span.status.is_ok) - - self.check_spans(spans, 1, ["set key has space"]) - - def test_quit(self): - client = self.make_client([]) - assert client.quit() is None - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["quit"]) - - def test_replace_not_stored(self): - client = self.make_client([b"NOT_STORED\r\n"]) - result = client.replace(b"key", b"value", noreply=False) - assert result is False - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["replace key"]) - - def test_version_success(self): - client = self.make_client( - [b"VERSION 1.2.3\r\n"], default_noreply=False - ) - result = client.version() - assert result == b"1.2.3" - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["version"]) - - def test_stats(self): - client = self.make_client([b"STAT fake_stats 1\r\n", b"END\r\n"]) - result = client.stats() - assert client.sock.send_bufs == [b"stats \r\n"] - assert result == {b"fake_stats": 1} - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 1, ["stats"]) - - def test_uninstrumented(self): - PymemcacheInstrumentor().uninstrument() - - client = self.make_client( - [b"STORED\r\n", b"VALUE key 0 5\r\nvalue\r\nEND\r\n"] - ) - client.set(b"key", b"value", noreply=False) - result = client.get(b"key") - assert _str(result) == "value" - - spans = self.memory_exporter.get_finished_spans() - - self.assertEqual(len(spans), 0) - - PymemcacheInstrumentor().instrument() - - -class PymemcacheHashClientTestCase(TestBase): - """ Tests for a patched pymemcache.client.hash.HashClient. """ - - def setUp(self): - super().setUp() - PymemcacheInstrumentor().instrument() - - # pylint: disable=protected-access - self.tracer = get_tracer(__name__) - - def tearDown(self): - super().tearDown() - PymemcacheInstrumentor().uninstrument() - - def make_client_pool( - self, hostname, mock_socket_values, serializer=None, **kwargs - ): # pylint: disable=no-self-use - mock_client = pymemcache.client.base.Client( - hostname, serializer=serializer, **kwargs - ) - mock_client.sock = MockSocket(mock_socket_values) - client = pymemcache.client.base.PooledClient( - hostname, serializer=serializer - ) - client.client_pool = pymemcache.pool.ObjectPool(lambda: mock_client) - return mock_client - - def make_client(self, *mock_socket_values, **kwargs): - current_port = TEST_PORT - - # pylint: disable=import-outside-toplevel - from pymemcache.client.hash import HashClient - - # pylint: disable=attribute-defined-outside-init - self.client = HashClient([], **kwargs) - ip = TEST_HOST - - for vals in mock_socket_values: - url_string = "{}:{}".format(ip, current_port) - clnt_pool = self.make_client_pool( - (ip, current_port), vals, **kwargs - ) - self.client.clients[url_string] = clnt_pool - self.client.hasher.add_node(url_string) - current_port += 1 - return self.client - - def check_spans(self, spans, num_expected, queries_expected): - """A helper for validating basic span information.""" - self.assertEqual(num_expected, len(spans)) - - for span, query in zip(spans, queries_expected): - self.assertEqual(span.name, "memcached.command") - self.assertIs(span.kind, trace_api.SpanKind.INTERNAL) - self.assertEqual( - span.attributes["net.peer.name"], "{}".format(TEST_HOST) - ) - self.assertEqual(span.attributes["net.peer.port"], TEST_PORT) - self.assertEqual(span.attributes["db.type"], "memcached") - self.assertEqual( - span.attributes["db.url"], - "memcached://{}:{}".format(TEST_HOST, TEST_PORT), - ) - self.assertEqual(span.attributes["db.statement"], query) - - def test_delete_many_found(self): - client = self.make_client([b"STORED\r", b"\n", b"DELETED\r\n"]) - result = client.add(b"key", b"value", noreply=False) - result = client.delete_many([b"key"], noreply=False) - self.assertTrue(result) - - spans = self.memory_exporter.get_finished_spans() - - self.check_spans(spans, 2, ["add key", "delete key"]) diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/tests/utils.py b/instrumentation/opentelemetry-instrumentation-pymemcache/tests/utils.py deleted file mode 100644 index 361fb6e68c6..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/tests/utils.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import collections -import socket - - -class MockSocket: - def __init__(self, recv_bufs, connect_failure=None): - self.recv_bufs = collections.deque(recv_bufs) - self.send_bufs = [] - self.closed = False - self.timeouts = [] - self.connect_failure = connect_failure - self.connections = [] - self.socket_options = [] - - def sendall(self, value): - self.send_bufs.append(value) - - def close(self): - self.closed = True - - def recv(self, size): # pylint: disable=unused-argument - value = self.recv_bufs.popleft() - if isinstance(value, Exception): - raise value - return value - - def settimeout(self, timeout): - self.timeouts.append(timeout) - - def connect(self, server): - if isinstance(self.connect_failure, Exception): - raise self.connect_failure - self.connections.append(server) - - def setsockopt(self, level, option, value): - self.socket_options.append((level, option, value)) - - -class MockSocketModule: - def __init__(self, connect_failure=None): - self.connect_failure = connect_failure - self.sockets = [] - - def socket(self): # noqa: A002 - soket = MockSocket([], connect_failure=self.connect_failure) - self.sockets.append(soket) - return soket - - def __getattr__(self, name): - return getattr(socket, name) - - -# Compatibility to get a string back from a request -def _str(string_input): - if isinstance(string_input, str): - return string_input - if isinstance(string_input, bytes): - return string_input.decode() - - return str(string_input) diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-pymongo/CHANGELOG.md deleted file mode 100644 index 30e36e00481..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymongo/CHANGELOG.md +++ /dev/null @@ -1,43 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.14b0 - -Released 2020-10-13 - -- Cast PyMongo commands as strings - ([#1132](https://github.com/open-telemetry/opentelemetry-python/pull/1132)) - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-pymongo - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) - -## 0.7b1 - -Released 2020-05-12 - -- Implement instrumentor interface ([#612](https://github.com/open-telemetry/opentelemetry-python/pull/612)) - -## 0.4a0 - -Released 2020-02-21 - -- Updating network connection attribute names - ([#350](https://github.com/open-telemetry/opentelemetry-python/pull/350)) - -## 0.3a0 - -Released 2019-12-11 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/LICENSE b/instrumentation/opentelemetry-instrumentation-pymongo/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymongo/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-pymongo/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-pymongo/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymongo/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-pymongo/README.rst b/instrumentation/opentelemetry-instrumentation-pymongo/README.rst deleted file mode 100644 index 7791810e97d..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymongo/README.rst +++ /dev/null @@ -1,21 +0,0 @@ -OpenTelemetry pymongo Instrumentation -===================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-pymongo.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-pymongo/ - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-pymongo - - -References ----------- -* `OpenTelemetry pymongo Instrumentation `_ -* `OpenTelemetry Project `_ - diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/setup.cfg b/instrumentation/opentelemetry-instrumentation-pymongo/setup.cfg deleted file mode 100644 index 6ccf6f1002e..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymongo/setup.cfg +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-pymongo -description = OpenTelemetry pymongo instrumentation -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/master/instrumentation/opentelemetry-instrumentation-pymongo -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - pymongo ~= 3.1 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - pymongo = opentelemetry.instrumentation.pymongo:PymongoInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/setup.py b/instrumentation/opentelemetry-instrumentation-pymongo/setup.py deleted file mode 100644 index 7b862ae2aa8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymongo/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "pymongo", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py deleted file mode 100644 index adc51b1c847..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -The integration with MongoDB supports the `pymongo`_ library, it can be -enabled using the ``PymongoInstrumentor``. - -.. _pymongo: https://pypi.org/project/pymongo - -Usage ------ - -.. code:: python - - from pymongo import MongoClient - from opentelemetry import trace - from opentelemetry.trace import TracerProvider - from opentelemetry.instrumentation.pymongo import PymongoInstrumentor - - trace.set_tracer_provider(TracerProvider()) - - PymongoInstrumentor().instrument() - client = MongoClient() - db = client["MongoDB_Database"] - collection = db["MongoDB_Collection"] - collection.find_one() - -API ---- -""" - -from pymongo import monitoring - -from opentelemetry import trace -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.pymongo.version import __version__ -from opentelemetry.trace import SpanKind, get_tracer -from opentelemetry.trace.status import Status, StatusCode - -DATABASE_TYPE = "mongodb" -COMMAND_ATTRIBUTES = ["filter", "sort", "skip", "limit", "pipeline"] - - -class CommandTracer(monitoring.CommandListener): - def __init__(self, tracer): - self._tracer = tracer - self._span_dict = {} - self.is_enabled = True - - def started(self, event: monitoring.CommandStartedEvent): - """ Method to handle a pymongo CommandStartedEvent """ - if not self.is_enabled: - return - command = event.command.get(event.command_name, "") - name = DATABASE_TYPE + "." + event.command_name - statement = event.command_name - if command: - name += "." + str(command) - statement += " " + str(command) - - try: - span = self._tracer.start_span(name, kind=SpanKind.CLIENT) - if span.is_recording(): - span.set_attribute("component", DATABASE_TYPE) - span.set_attribute("db.type", DATABASE_TYPE) - span.set_attribute("db.instance", event.database_name) - span.set_attribute("db.statement", statement) - if event.connection_id is not None: - span.set_attribute("net.peer.name", event.connection_id[0]) - span.set_attribute("net.peer.port", event.connection_id[1]) - - # pymongo specific, not specified by spec - span.set_attribute("db.mongo.operation_id", event.operation_id) - span.set_attribute("db.mongo.request_id", event.request_id) - - for attr in COMMAND_ATTRIBUTES: - _attr = event.command.get(attr) - if _attr is not None: - span.set_attribute("db.mongo." + attr, str(_attr)) - - # Add Span to dictionary - self._span_dict[_get_span_dict_key(event)] = span - except Exception as ex: # noqa pylint: disable=broad-except - if span is not None and span.is_recording(): - span.set_status(Status(StatusCode.ERROR, str(ex))) - span.end() - self._pop_span(event) - - def succeeded(self, event: monitoring.CommandSucceededEvent): - """ Method to handle a pymongo CommandSucceededEvent """ - if not self.is_enabled: - return - span = self._pop_span(event) - if span is None: - return - if span.is_recording(): - span.set_attribute( - "db.mongo.duration_micros", event.duration_micros - ) - span.end() - - def failed(self, event: monitoring.CommandFailedEvent): - """ Method to handle a pymongo CommandFailedEvent """ - if not self.is_enabled: - return - span = self._pop_span(event) - if span is None: - return - if span.is_recording(): - span.set_attribute( - "db.mongo.duration_micros", event.duration_micros - ) - span.set_status(Status(StatusCode.ERROR, event.failure)) - span.end() - - def _pop_span(self, event): - return self._span_dict.pop(_get_span_dict_key(event), None) - - -def _get_span_dict_key(event): - if event.connection_id is not None: - return (event.request_id, event.connection_id) - return event.request_id - - -class PymongoInstrumentor(BaseInstrumentor): - _commandtracer_instance = None # type CommandTracer - # The instrumentation for PyMongo is based on the event listener interface - # https://api.mongodb.com/python/current/api/pymongo/monitoring.html. - # This interface only allows to register listeners and does not provide - # an unregister API. In order to provide a mechanishm to disable - # instrumentation an enabled flag is implemented in CommandTracer, - # it's checked in the different listeners. - - def _instrument(self, **kwargs): - """Integrate with pymongo to trace it using event listener. - https://api.mongodb.com/python/current/api/pymongo/monitoring.html - - Args: - tracer_provider: The `TracerProvider` to use. If none is passed the - current configured one is used. - """ - - tracer_provider = kwargs.get("tracer_provider") - - # Create and register a CommandTracer only the first time - if self._commandtracer_instance is None: - tracer = get_tracer(__name__, __version__, tracer_provider) - - self._commandtracer_instance = CommandTracer(tracer) - monitoring.register(self._commandtracer_instance) - - # If already created, just enable it - self._commandtracer_instance.is_enabled = True - - def _uninstrument(self, **kwargs): - if self._commandtracer_instance is not None: - self._commandtracer_instance.is_enabled = False diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/version.py b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-pymongo/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py b/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py deleted file mode 100644 index a3bb7b2223c..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest import mock - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.pymongo import ( - CommandTracer, - PymongoInstrumentor, -) -from opentelemetry.test.test_base import TestBase - - -class TestPymongo(TestBase): - def setUp(self): - super().setUp() - self.tracer = self.tracer_provider.get_tracer(__name__) - - def test_pymongo_instrumentor(self): - mock_register = mock.Mock() - patch = mock.patch( - "pymongo.monitoring.register", side_effect=mock_register - ) - with patch: - PymongoInstrumentor().instrument() - - self.assertTrue(mock_register.called) - - def test_started(self): - command_attrs = { - "filter": "filter", - "sort": "sort", - "limit": "limit", - "pipeline": "pipeline", - "command_name": "find", - } - command_tracer = CommandTracer(self.tracer) - mock_event = MockEvent( - command_attrs, ("test.com", "1234"), "test_request_id" - ) - command_tracer.started(event=mock_event) - # the memory exporter can't be used here because the span isn't ended - # yet - # pylint: disable=protected-access - span = command_tracer._pop_span(mock_event) - self.assertIs(span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual(span.name, "mongodb.command_name.find") - self.assertEqual(span.attributes["component"], "mongodb") - self.assertEqual(span.attributes["db.type"], "mongodb") - self.assertEqual(span.attributes["db.instance"], "database_name") - self.assertEqual(span.attributes["db.statement"], "command_name find") - self.assertEqual(span.attributes["net.peer.name"], "test.com") - self.assertEqual(span.attributes["net.peer.port"], "1234") - self.assertEqual( - span.attributes["db.mongo.operation_id"], "operation_id" - ) - self.assertEqual( - span.attributes["db.mongo.request_id"], "test_request_id" - ) - - self.assertEqual(span.attributes["db.mongo.filter"], "filter") - self.assertEqual(span.attributes["db.mongo.sort"], "sort") - self.assertEqual(span.attributes["db.mongo.limit"], "limit") - self.assertEqual(span.attributes["db.mongo.pipeline"], "pipeline") - - def test_succeeded(self): - mock_event = MockEvent({}) - command_tracer = CommandTracer(self.tracer) - command_tracer.started(event=mock_event) - command_tracer.succeeded(event=mock_event) - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - self.assertEqual( - span.attributes["db.mongo.duration_micros"], "duration_micros" - ) - self.assertIs( - span.status.status_code, trace_api.status.StatusCode.UNSET - ) - self.assertIsNotNone(span.end_time) - - def test_not_recording(self): - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - mock_event = MockEvent({}) - command_tracer = CommandTracer(mock_tracer) - command_tracer.started(event=mock_event) - command_tracer.succeeded(event=mock_event) - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_failed(self): - mock_event = MockEvent({}) - command_tracer = CommandTracer(self.tracer) - command_tracer.started(event=mock_event) - command_tracer.failed(event=mock_event) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - self.assertEqual( - span.attributes["db.mongo.duration_micros"], "duration_micros" - ) - self.assertIs( - span.status.status_code, trace_api.status.StatusCode.ERROR, - ) - self.assertEqual(span.status.description, "failure") - self.assertIsNotNone(span.end_time) - - def test_multiple_commands(self): - first_mock_event = MockEvent({}, ("firstUrl", "123"), "first") - second_mock_event = MockEvent({}, ("secondUrl", "456"), "second") - command_tracer = CommandTracer(self.tracer) - command_tracer.started(event=first_mock_event) - command_tracer.started(event=second_mock_event) - command_tracer.succeeded(event=first_mock_event) - command_tracer.failed(event=second_mock_event) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 2) - first_span = spans_list[0] - second_span = spans_list[1] - - self.assertEqual(first_span.attributes["db.mongo.request_id"], "first") - self.assertIs( - first_span.status.status_code, trace_api.status.StatusCode.UNSET, - ) - self.assertEqual( - second_span.attributes["db.mongo.request_id"], "second" - ) - self.assertIs( - second_span.status.status_code, trace_api.status.StatusCode.ERROR, - ) - - def test_int_command(self): - command_attrs = { - "command_name": 123, - } - mock_event = MockEvent(command_attrs) - - command_tracer = CommandTracer(self.tracer) - command_tracer.started(event=mock_event) - command_tracer.succeeded(event=mock_event) - - spans_list = self.memory_exporter.get_finished_spans() - - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - self.assertEqual(span.name, "mongodb.command_name.123") - - -class MockCommand: - def __init__(self, command_attrs): - self.command_attrs = command_attrs - - def get(self, key, default=""): - return self.command_attrs.get(key, default) - - -class MockEvent: - def __init__(self, command_attrs, connection_id=None, request_id=""): - self.command = MockCommand(command_attrs) - self.connection_id = connection_id - self.request_id = request_id - - def __getattr__(self, item): - return item diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-pymysql/CHANGELOG.md deleted file mode 100644 index 3f78db57b96..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymysql/CHANGELOG.md +++ /dev/null @@ -1,30 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.14b0 - -Released 2020-10-13 - -- Bumped version from 0.9.3 to 0.10.1 - ([#1228](https://github.com/open-telemetry/opentelemetry-python/pull/1228)) - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-pymysql - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) - -## 0.7b1 - -Released 2020-05-12 - -- Initial release \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/README.rst b/instrumentation/opentelemetry-instrumentation-pymysql/README.rst deleted file mode 100644 index 0b566d2a94c..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymysql/README.rst +++ /dev/null @@ -1,20 +0,0 @@ -OpenTelemetry PyMySQL Instrumentation -===================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-pymysql.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-pymysql/ - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-pymysql - - -References ----------- -* `OpenTelemetry PyMySQL Instrumentation `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/setup.cfg b/instrumentation/opentelemetry-instrumentation-pymysql/setup.cfg deleted file mode 100644 index b84fe20d555..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymysql/setup.cfg +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-pymysql -description = OpenTelemetry PyMySQL instrumentation -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/master/instrumentation/opentelemetry-instrumentation-pymysql -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation-dbapi == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - PyMySQL ~= 0.10.1 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - pymysql = opentelemetry.instrumentation.pymysql:PyMySQLInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/setup.py b/instrumentation/opentelemetry-instrumentation-pymysql/setup.py deleted file mode 100644 index 74cd948fee8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymysql/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "pymysql", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py b/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py deleted file mode 100644 index 5d299505d69..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/__init__.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -The integration with PyMySQL supports the `PyMySQL`_ library and can be enabled -by using ``PyMySQLInstrumentor``. - -.. _PyMySQL: https://pypi.org/project/PyMySQL/ - -Usage ------ - -.. code:: python - - import pymysql - from opentelemetry import trace - from opentelemetry.instrumentation.pymysql import PyMySQLInstrumentor - from opentelemetry.sdk.trace import TracerProvider - - trace.set_tracer_provider(TracerProvider()) - - PyMySQLInstrumentor().instrument() - - cnx = pymysql.connect(database="MySQL_Database") - cursor = cnx.cursor() - cursor.execute("INSERT INTO test (testField) VALUES (123)" - cnx.commit() - cursor.close() - cnx.close() - -API ---- -""" - -import pymysql - -from opentelemetry.instrumentation import dbapi -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.pymysql.version import __version__ - - -class PyMySQLInstrumentor(BaseInstrumentor): - _CONNECTION_ATTRIBUTES = { - "database": "db", - "port": "port", - "host": "host", - "user": "user", - } - - _DATABASE_COMPONENT = "mysql" - _DATABASE_TYPE = "sql" - - def _instrument(self, **kwargs): - """Integrate with the PyMySQL library. - https://github.com/PyMySQL/PyMySQL/ - """ - tracer_provider = kwargs.get("tracer_provider") - - dbapi.wrap_connect( - __name__, - pymysql, - "connect", - self._DATABASE_COMPONENT, - self._DATABASE_TYPE, - self._CONNECTION_ATTRIBUTES, - version=__version__, - tracer_provider=tracer_provider, - ) - - def _uninstrument(self, **kwargs): - """"Disable PyMySQL instrumentation""" - dbapi.unwrap_connect(pymysql, "connect") - - # pylint:disable=no-self-use - def instrument_connection(self, connection): - """Enable instrumentation in a PyMySQL connection. - - Args: - connection: The connection to instrument. - - Returns: - An instrumented connection. - """ - - return dbapi.instrument_connection( - __name__, - connection, - self._DATABASE_COMPONENT, - self._DATABASE_TYPE, - self._CONNECTION_ATTRIBUTES, - version=__version__, - ) - - def uninstrument_connection(self, connection): - """Disable instrumentation in a PyMySQL connection. - - Args: - connection: The connection to uninstrument. - - Returns: - An uninstrumented connection. - """ - return dbapi.uninstrument_connection(connection) diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/version.py b/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymysql/src/opentelemetry/instrumentation/pymysql/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-pymysql/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/tests/test_pymysql_integration.py b/instrumentation/opentelemetry-instrumentation-pymysql/tests/test_pymysql_integration.py deleted file mode 100644 index 35c9f4d32b6..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pymysql/tests/test_pymysql_integration.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest import mock - -import pymysql - -import opentelemetry.instrumentation.pymysql -from opentelemetry.instrumentation.pymysql import PyMySQLInstrumentor -from opentelemetry.sdk import resources -from opentelemetry.test.test_base import TestBase - - -class TestPyMysqlIntegration(TestBase): - def tearDown(self): - super().tearDown() - with self.disable_logging(): - PyMySQLInstrumentor().uninstrument() - - @mock.patch("pymysql.connect") - # pylint: disable=unused-argument - def test_instrumentor(self, mock_connect): - PyMySQLInstrumentor().instrument() - - cnx = pymysql.connect(database="test") - cursor = cnx.cursor() - query = "SELECT * FROM test" - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - # Check version and name in span's instrumentation info - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.pymysql - ) - - # check that no spans are generated after uninstrument - PyMySQLInstrumentor().uninstrument() - - cnx = pymysql.connect(database="test") - cursor = cnx.cursor() - query = "SELECT * FROM test" - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - @mock.patch("pymysql.connect") - # pylint: disable=unused-argument - def test_custom_tracer_provider(self, mock_connect): - resource = resources.Resource.create({}) - result = self.create_tracer_provider(resource=resource) - tracer_provider, exporter = result - - PyMySQLInstrumentor().instrument(tracer_provider=tracer_provider) - - cnx = pymysql.connect(database="test") - cursor = cnx.cursor() - query = "SELECT * FROM test" - cursor.execute(query) - - spans_list = exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - span = spans_list[0] - - self.assertIs(span.resource, resource) - - @mock.patch("pymysql.connect") - # pylint: disable=unused-argument - def test_instrument_connection(self, mock_connect): - cnx = pymysql.connect(database="test") - query = "SELECT * FROM test" - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 0) - - cnx = PyMySQLInstrumentor().instrument_connection(cnx) - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - @mock.patch("pymysql.connect") - # pylint: disable=unused-argument - def test_uninstrument_connection(self, mock_connect): - PyMySQLInstrumentor().instrument() - cnx = pymysql.connect(database="test") - query = "SELECT * FROM test" - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) - - cnx = PyMySQLInstrumentor().uninstrument_connection(cnx) - cursor = cnx.cursor() - cursor.execute(query) - - spans_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans_list), 1) diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-pyramid/CHANGELOG.md deleted file mode 100644 index 6f78bfc16ae..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/CHANGELOG.md +++ /dev/null @@ -1,27 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-pyramid ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) - -## Version 0.11b0 - -- Use one general exclude list instead of two ([#872](https://github.com/open-telemetry/opentelemetry-python/pull/872)) - -## 0.9b0 - -Released 2020-06-10 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/LICENSE b/instrumentation/opentelemetry-instrumentation-pyramid/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-pyramid/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-pyramid/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-pyramid/README.rst b/instrumentation/opentelemetry-instrumentation-pyramid/README.rst deleted file mode 100644 index 931486773ae..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/README.rst +++ /dev/null @@ -1,32 +0,0 @@ -OpenTelemetry Pyramid Instrumentation -===================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-pyramid.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-pyramid/ - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-pyramid - -Exclude lists -************* -To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_PYRAMID_EXCLUDED_URLS`` with comma delimited regexes representing which URLs to exclude. - -For example, - -:: - - export OTEL_PYTHON_PYRAMID_EXCLUDED_URLS="client/.*/info,healthcheck" - -will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``. - -References ----------- -* `OpenTelemetry Pyramid Instrumentation `_ -* `OpenTelemetry Project `_ - diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/setup.cfg b/instrumentation/opentelemetry-instrumentation-pyramid/setup.cfg deleted file mode 100644 index 44db6be9610..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/setup.cfg +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-pyramid -description = OpenTelemetry Pyramid instrumentation -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/master/instrumentation/opentelemetry-instrumentation-pyramid -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - pyramid >= 1.7 - opentelemetry-instrumentation == 0.16.dev0 - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation-wsgi == 0.16.dev0 - wrapt >= 1.0.0, < 2.0.0 - -[options.extras_require] -test = - werkzeug == 0.16.1 - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - pyramid = opentelemetry.instrumentation.pyramid:PyramidInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/setup.py b/instrumentation/opentelemetry-instrumentation-pyramid/setup.py deleted file mode 100644 index 7141a898132..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "pyramid", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/__init__.py b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/__init__.py deleted file mode 100644 index 4125453153b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/__init__.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Pyramid instrumentation supporting `pyramid`_, it can be enabled by -using ``PyramidInstrumentor``. - -.. _pyramid: https://docs.pylonsproject.org/projects/pyramid/en/latest/ - -Usage ------ -There are two methods to instrument Pyramid: - -Method 1 (Instrument all Configurators): ----------------------------------------- - -.. code:: python - - from pyramid.config import Configurator - from opentelemetry.instrumentation.pyramid import PyramidInstrumentor - - PyramidInstrumentor().instrument() - - config = Configurator() - - # use your config as normal - config.add_route('index', '/') - -Method 2 (Instrument one Configurator): ---------------------------------------- - -.. code:: python - - from pyramid.config import Configurator - from opentelemetry.instrumentation.pyramid import PyramidInstrumentor - - config = Configurator() - PyramidInstrumentor().instrument_config(config) - - # use your config as normal - config.add_route('index', '/') - -Using ``pyramid.tweens`` setting: ---------------------------------- - -If you use Method 2 and then set tweens for your application with the ``pyramid.tweens`` setting, -you need to add ``opentelemetry.instrumentation.pyramid.trace_tween_factory`` explicity to the list, -*as well as* instrumenting the config as shown above. - -For example: - -.. code:: python - - from pyramid.config import Configurator - from opentelemetry.instrumentation.pyramid import PyramidInstrumentor - - settings = { - 'pyramid.tweens', 'opentelemetry.instrumentation.pyramid.trace_tween_factory\\nyour_tween_no_1\\nyour_tween_no_2', - } - config = Configurator(settings=settings) - PyramidInstrumentor().instrument_config(config) - - # use your config as normal. - config.add_route('index', '/') - -API ---- -""" - -import typing - -from pyramid.config import Configurator -from pyramid.path import caller_package -from pyramid.settings import aslist -from wrapt import ObjectProxy -from wrapt import wrap_function_wrapper as _wrap - -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.pyramid.callbacks import ( - SETTING_TRACE_ENABLED, - TWEEN_NAME, - trace_tween_factory, -) -from opentelemetry.instrumentation.pyramid.version import __version__ -from opentelemetry.instrumentation.utils import unwrap -from opentelemetry.trace import TracerProvider, get_tracer - - -def _traced_init(wrapped, instance, args, kwargs): - settings = kwargs.get("settings", {}) - tweens = aslist(settings.get("pyramid.tweens", [])) - - if tweens and TWEEN_NAME not in settings: - # pyramid.tweens.EXCVIEW is the name of built-in exception view provided by - # pyramid. We need our tween to be before it, otherwise unhandled - # exceptions will be caught before they reach our tween. - tweens = [TWEEN_NAME] + tweens - - settings["pyramid.tweens"] = "\n".join(tweens) - - kwargs["settings"] = settings - - # `caller_package` works by walking a fixed amount of frames up the stack - # to find the calling package. So if we let the original `__init__` - # function call it, our wrapper will mess things up. - if not kwargs.get("package", None): - # Get the package for the third frame up from this one. - # Default is `level=2` which will give us the package from `wrapt` - # instead of the desired package (the caller) - kwargs["package"] = caller_package(level=3) - - wrapped(*args, **kwargs) - instance.include("opentelemetry.instrumentation.pyramid.callbacks") - - -class PyramidInstrumentor(BaseInstrumentor): - def _instrument(self, **kwargs): - """Integrate with Pyramid Python library. - https://docs.pylonsproject.org/projects/pyramid/en/latest/ - """ - _wrap("pyramid.config", "Configurator.__init__", _traced_init) - - def _uninstrument(self, **kwargs): - """"Disable Pyramid instrumentation""" - unwrap(Configurator, "__init__") - - # pylint:disable=no-self-use - def instrument_config(self, config): - """Enable instrumentation in a Pyramid configurator. - - Args: - config: The Configurator to instrument. - """ - config.include("opentelemetry.instrumentation.pyramid.callbacks") - - def uninstrument_config(self, config): - config.add_settings({SETTING_TRACE_ENABLED: False}) diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py deleted file mode 100644 index e7110bd2b55..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py +++ /dev/null @@ -1,165 +0,0 @@ -from logging import getLogger - -from pyramid.events import BeforeTraversal -from pyramid.httpexceptions import HTTPException -from pyramid.settings import asbool -from pyramid.tweens import EXCVIEW - -import opentelemetry.instrumentation.wsgi as otel_wsgi -from opentelemetry import configuration, context, propagators, trace -from opentelemetry.instrumentation.pyramid.version import __version__ -from opentelemetry.util import ExcludeList, time_ns - -TWEEN_NAME = "opentelemetry.instrumentation.pyramid.trace_tween_factory" -SETTING_TRACE_ENABLED = "opentelemetry-pyramid.trace_enabled" - -_ENVIRON_STARTTIME_KEY = "opentelemetry-pyramid.starttime_key" -_ENVIRON_SPAN_KEY = "opentelemetry-pyramid.span_key" -_ENVIRON_ACTIVATION_KEY = "opentelemetry-pyramid.activation_key" -_ENVIRON_ENABLED_KEY = "opentelemetry-pyramid.tracing_enabled_key" -_ENVIRON_TOKEN = "opentelemetry-pyramid.token" - -_logger = getLogger(__name__) - - -def get_excluded_urls(): - urls = configuration.Configuration().PYRAMID_EXCLUDED_URLS or [] - if urls: - urls = str.split(urls, ",") - return ExcludeList(urls) - - -_excluded_urls = get_excluded_urls() - - -def includeme(config): - config.add_settings({SETTING_TRACE_ENABLED: True}) - - config.add_subscriber(_before_traversal, BeforeTraversal) - _insert_tween(config) - - -def _insert_tween(config): - settings = config.get_settings() - tweens = settings.get("pyramid.tweens") - # If the list is empty, pyramid does not consider the tweens have been - # set explicitly. And if our tween is already there, nothing to do - if not tweens or not tweens.strip(): - # Add our tween just before the default exception handler - config.add_tween(TWEEN_NAME, over=EXCVIEW) - - -def _before_traversal(event): - request = event.request - environ = request.environ - span_name = otel_wsgi.get_default_span_name(environ) - - enabled = environ.get(_ENVIRON_ENABLED_KEY) - if enabled is None: - _logger.warning( - "Opentelemetry pyramid tween 'opentelemetry.instrumentation.pyramid.trace_tween_factory'" - "was not called. Make sure that the tween is included in 'pyramid.tweens' if" - "the tween list was created manually" - ) - return - - if not enabled: - # Tracing not enabled, return - return - - start_time = environ.get(_ENVIRON_STARTTIME_KEY) - - token = context.attach( - propagators.extract(otel_wsgi.carrier_getter, environ) - ) - tracer = trace.get_tracer(__name__, __version__) - - if request.matched_route: - span_name = request.matched_route.pattern - else: - span_name = otel_wsgi.get_default_span_name(environ) - - span = tracer.start_span( - span_name, kind=trace.SpanKind.SERVER, start_time=start_time, - ) - - if span.is_recording(): - attributes = otel_wsgi.collect_request_attributes(environ) - if request.matched_route: - attributes["http.route"] = request.matched_route.pattern - for key, value in attributes.items(): - span.set_attribute(key, value) - - activation = tracer.use_span(span, end_on_exit=True) - activation.__enter__() - environ[_ENVIRON_ACTIVATION_KEY] = activation - environ[_ENVIRON_SPAN_KEY] = span - environ[_ENVIRON_TOKEN] = token - - -def trace_tween_factory(handler, registry): - settings = registry.settings - enabled = asbool(settings.get(SETTING_TRACE_ENABLED, True)) - - if not enabled: - # If disabled, make a tween that signals to the - # BeforeTraversal subscriber that tracing is disabled - def disabled_tween(request): - request.environ[_ENVIRON_ENABLED_KEY] = False - return handler(request) - - return disabled_tween - - # make a request tracing function - def trace_tween(request): - if _excluded_urls.url_disabled(request.url): - request.environ[_ENVIRON_ENABLED_KEY] = False - # short-circuit when we don't want to trace anything - return handler(request) - - request.environ[_ENVIRON_ENABLED_KEY] = True - request.environ[_ENVIRON_STARTTIME_KEY] = time_ns() - - try: - response = handler(request) - response_or_exception = response - except HTTPException as exc: - # If the exception is a pyramid HTTPException, - # that's still valuable information that isn't necessarily - # a 500. For instance, HTTPFound is a 302. - # As described in docs, Pyramid exceptions are all valid - # response types - response_or_exception = exc - raise - finally: - span = request.environ.get(_ENVIRON_SPAN_KEY) - enabled = request.environ.get(_ENVIRON_ENABLED_KEY) - if not span and enabled: - _logger.warning( - "Pyramid environ's OpenTelemetry span missing." - "If the OpenTelemetry tween was added manually, make sure" - "PyramidInstrumentor().instrument_config(config) is called" - ) - elif enabled: - otel_wsgi.add_response_attributes( - span, - response_or_exception.status, - response_or_exception.headers, - ) - - activation = request.environ.get(_ENVIRON_ACTIVATION_KEY) - - if isinstance(response_or_exception, HTTPException): - activation.__exit__( - type(response_or_exception), - response_or_exception, - getattr(response_or_exception, "__traceback__", None), - ) - else: - activation.__exit__(None, None, None) - - context.detach(request.environ.get(_ENVIRON_TOKEN)) - - return response - - return trace_tween diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/version.py b/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/pyramid_base_test.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/pyramid_base_test.py deleted file mode 100644 index 21a6a1ab95e..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/pyramid_base_test.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pyramid.httpexceptions as exc -from pyramid.response import Response -from werkzeug.test import Client -from werkzeug.wrappers import BaseResponse - -from opentelemetry.configuration import Configuration - - -class InstrumentationTest: - def setUp(self): # pylint: disable=invalid-name - super().setUp() # pylint: disable=no-member - Configuration._reset() # pylint: disable=protected-access - - @staticmethod - def _hello_endpoint(request): - helloid = int(request.matchdict["helloid"]) - if helloid == 500: - raise exc.HTTPInternalServerError() - return Response("Hello: " + str(helloid)) - - def _common_initialization(self, config): - # pylint: disable=unused-argument - def excluded_endpoint(request): - return Response("excluded") - - # pylint: disable=unused-argument - def excluded2_endpoint(request): - return Response("excluded2") - - config.add_route("hello", "/hello/{helloid}") - config.add_view(self._hello_endpoint, route_name="hello") - config.add_route("excluded_arg", "/excluded/{helloid}") - config.add_view(self._hello_endpoint, route_name="excluded_arg") - config.add_route("excluded", "/excluded_noarg") - config.add_view(excluded_endpoint, route_name="excluded") - config.add_route("excluded2", "/excluded_noarg2") - config.add_view(excluded2_endpoint, route_name="excluded2") - - # pylint: disable=attribute-defined-outside-init - self.client = Client(config.make_wsgi_app(), BaseResponse) diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py deleted file mode 100644 index b065e260648..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from pyramid.config import Configurator - -from opentelemetry.instrumentation.pyramid import PyramidInstrumentor -from opentelemetry.test.test_base import TestBase -from opentelemetry.test.wsgitestutil import WsgiTestBase - -# pylint: disable=import-error -from .pyramid_base_test import InstrumentationTest - - -class TestAutomatic(InstrumentationTest, TestBase, WsgiTestBase): - def setUp(self): - super().setUp() - - PyramidInstrumentor().instrument() - - self.config = Configurator() - - self._common_initialization(self.config) - - def tearDown(self): - super().tearDown() - with self.disable_logging(): - PyramidInstrumentor().uninstrument() - - def test_uninstrument(self): - # pylint: disable=access-member-before-definition - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - PyramidInstrumentor().uninstrument() - self.config = Configurator() - - self._common_initialization(self.config) - - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - def test_tween_list(self): - tween_list = "pyramid.tweens.excview_tween_factory" - config = Configurator(settings={"pyramid.tweens": tween_list}) - self._common_initialization(config) - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - PyramidInstrumentor().uninstrument() - - self.config = Configurator() - - self._common_initialization(self.config) - - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py deleted file mode 100644 index 77427b0db7a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py +++ /dev/null @@ -1,209 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest.mock import Mock, patch - -from pyramid.config import Configurator - -from opentelemetry import trace -from opentelemetry.instrumentation.pyramid import PyramidInstrumentor -from opentelemetry.test.test_base import TestBase -from opentelemetry.test.wsgitestutil import WsgiTestBase -from opentelemetry.util import ExcludeList - -# pylint: disable=import-error -from .pyramid_base_test import InstrumentationTest - - -def expected_attributes(override_attributes): - default_attributes = { - "component": "http", - "http.method": "GET", - "http.server_name": "localhost", - "http.scheme": "http", - "host.port": 80, - "http.host": "localhost", - "http.target": "/", - "http.flavor": "1.1", - "http.status_text": "OK", - "http.status_code": 200, - } - for key, val in override_attributes.items(): - default_attributes[key] = val - return default_attributes - - -class TestProgrammatic(InstrumentationTest, TestBase, WsgiTestBase): - def setUp(self): - super().setUp() - config = Configurator() - PyramidInstrumentor().instrument_config(config) - - self.config = config - - self._common_initialization(self.config) - - def tearDown(self): - super().tearDown() - with self.disable_logging(): - PyramidInstrumentor().uninstrument_config(self.config) - - def test_uninstrument(self): - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - PyramidInstrumentor().uninstrument_config(self.config) - # Need to remake the WSGI app export - self._common_initialization(self.config) - - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - def test_simple(self): - expected_attrs = expected_attributes( - {"http.target": "/hello/123", "http.route": "/hello/{helloid}"} - ) - self.client.get("/hello/123") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, "/hello/{helloid}") - self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) - self.assertEqual(span_list[0].attributes, expected_attrs) - - def test_not_recording(self): - mock_tracer = Mock() - mock_span = Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - with patch("opentelemetry.trace.get_tracer"): - self.client.get("/hello/123") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 0) - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_404(self): - expected_attrs = expected_attributes( - { - "http.method": "POST", - "http.target": "/bye", - "http.status_text": "Not Found", - "http.status_code": 404, - } - ) - - resp = self.client.post("/bye") - self.assertEqual(404, resp.status_code) - resp.close() - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, "HTTP POST") - self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) - self.assertEqual(span_list[0].attributes, expected_attrs) - - def test_internal_error(self): - expected_attrs = expected_attributes( - { - "http.target": "/hello/500", - "http.route": "/hello/{helloid}", - "http.status_text": "Internal Server Error", - "http.status_code": 500, - } - ) - resp = self.client.get("/hello/500") - self.assertEqual(500, resp.status_code) - resp.close() - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, "/hello/{helloid}") - self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) - self.assertEqual(span_list[0].attributes, expected_attrs) - - def test_tween_list(self): - tween_list = "opentelemetry.instrumentation.pyramid.trace_tween_factory\npyramid.tweens.excview_tween_factory" - config = Configurator(settings={"pyramid.tweens": tween_list}) - PyramidInstrumentor().instrument_config(config) - self._common_initialization(config) - - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - PyramidInstrumentor().uninstrument_config(config) - # Need to remake the WSGI app export - self._common_initialization(config) - - resp = self.client.get("/hello/123") - self.assertEqual(200, resp.status_code) - self.assertEqual([b"Hello: 123"], list(resp.response)) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - @patch("opentelemetry.instrumentation.pyramid.callbacks._logger") - def test_warnings(self, mock_logger): - tween_list = "pyramid.tweens.excview_tween_factory" - config = Configurator(settings={"pyramid.tweens": tween_list}) - PyramidInstrumentor().instrument_config(config) - self._common_initialization(config) - - self.client.get("/hello/123") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 0) - self.assertEqual(mock_logger.warning.called, True) - - mock_logger.warning.called = False - - tween_list = ( - "opentelemetry.instrumentation.pyramid.trace_tween_factory" - ) - config = Configurator(settings={"pyramid.tweens": tween_list}) - self._common_initialization(config) - - self.client.get("/hello/123") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 0) - self.assertEqual(mock_logger.warning.called, True) - - @patch( - "opentelemetry.instrumentation.pyramid.callbacks._excluded_urls", - ExcludeList(["http://localhost/excluded_arg/123", "excluded_noarg"]), - ) - def test_exclude_lists(self): - self.client.get("/excluded_arg/123") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 0) - - self.client.get("/excluded_arg/125") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - self.client.get("/excluded_noarg") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - - self.client.get("/excluded_noarg2") - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) diff --git a/instrumentation/opentelemetry-instrumentation-redis/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-redis/CHANGELOG.md deleted file mode 100644 index 8f2d5f7e84f..00000000000 --- a/instrumentation/opentelemetry-instrumentation-redis/CHANGELOG.md +++ /dev/null @@ -1,24 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Update default SpanKind to `SpanKind.CLIENT` ([#965](https://github.com/open-telemetry/opentelemetry-python/pull/965)) -- Change package name to opentelemetry-instrumentation-redis - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) - -## 0.7b1 - -Released 2020-05-12 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-redis/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-redis/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-redis/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-redis/README.rst b/instrumentation/opentelemetry-instrumentation-redis/README.rst deleted file mode 100644 index 1a071ad0fee..00000000000 --- a/instrumentation/opentelemetry-instrumentation-redis/README.rst +++ /dev/null @@ -1,23 +0,0 @@ -OpenTelemetry Redis Instrumentation -=================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-redis.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-redis/ - -This library allows tracing requests made by the Redis library. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-redis - - -References ----------- - -* `OpenTelemetry Redis Instrumentation `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-redis/setup.cfg b/instrumentation/opentelemetry-instrumentation-redis/setup.cfg deleted file mode 100644 index 186e167dea2..00000000000 --- a/instrumentation/opentelemetry-instrumentation-redis/setup.cfg +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-redis -description = OpenTelemetry Redis instrumentation -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/master/instrumentation/opentelemetry-instrumentation-redis -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - redis >= 2.6 - wrapt >= 1.12.1 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - opentelemetry-sdk == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - redis = opentelemetry.instrumentation.redis:RedisInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-redis/setup.py b/instrumentation/opentelemetry-instrumentation-redis/setup.py deleted file mode 100644 index df80a8fd1aa..00000000000 --- a/instrumentation/opentelemetry-instrumentation-redis/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "redis", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py deleted file mode 100644 index e1c5db1e940..00000000000 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -""" -Instrument `redis`_ to report Redis queries. - -There are two options for instrumenting code. The first option is to use the -``opentelemetry-instrumentation`` executable which will automatically -instrument your Redis client. The second is to programmatically enable -instrumentation via the following code: - -.. _redis: https://pypi.org/project/redis/ - -Usage ------ - -.. code:: python - - from opentelemetry import trace - from opentelemetry.instrumentation.redis import RedisInstrumentor - from opentelemetry.sdk.trace import TracerProvider - import redis - - trace.set_tracer_provider(TracerProvider()) - - # Instrument redis - RedisInstrumentor().instrument(tracer_provider=trace.get_tracer_provider()) - - # This will report a span with the default settings - client = redis.StrictRedis(host="localhost", port=6379) - client.get("my-key") - -API ---- -""" - -import redis -from wrapt import ObjectProxy, wrap_function_wrapper - -from opentelemetry import trace -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.redis.util import ( - _extract_conn_attributes, - _format_command_args, -) -from opentelemetry.instrumentation.redis.version import __version__ -from opentelemetry.instrumentation.utils import unwrap - -_DEFAULT_SERVICE = "redis" -_RAWCMD = "db.statement" -_CMD = "redis.command" - - -def _set_connection_attributes(span, conn): - if not span.is_recording(): - return - for key, value in _extract_conn_attributes( - conn.connection_pool.connection_kwargs - ).items(): - span.set_attribute(key, value) - - -def _traced_execute_command(func, instance, args, kwargs): - tracer = getattr(redis, "_opentelemetry_tracer") - query = _format_command_args(args) - with tracer.start_as_current_span( - _CMD, kind=trace.SpanKind.CLIENT - ) as span: - if span.is_recording(): - span.set_attribute("service", tracer.instrumentation_info.name) - span.set_attribute(_RAWCMD, query) - _set_connection_attributes(span, instance) - span.set_attribute("redis.args_length", len(args)) - return func(*args, **kwargs) - - -def _traced_execute_pipeline(func, instance, args, kwargs): - tracer = getattr(redis, "_opentelemetry_tracer") - - cmds = [_format_command_args(c) for c, _ in instance.command_stack] - resource = "\n".join(cmds) - - with tracer.start_as_current_span( - _CMD, kind=trace.SpanKind.CLIENT - ) as span: - if span.is_recording(): - span.set_attribute("service", tracer.instrumentation_info.name) - span.set_attribute(_RAWCMD, resource) - _set_connection_attributes(span, instance) - span.set_attribute( - "redis.pipeline_length", len(instance.command_stack) - ) - return func(*args, **kwargs) - - -class RedisInstrumentor(BaseInstrumentor): - """An instrumentor for Redis - See `BaseInstrumentor` - """ - - def _instrument(self, **kwargs): - tracer_provider = kwargs.get( - "tracer_provider", trace.get_tracer_provider() - ) - setattr( - redis, - "_opentelemetry_tracer", - tracer_provider.get_tracer(_DEFAULT_SERVICE, __version__), - ) - - if redis.VERSION < (3, 0, 0): - wrap_function_wrapper( - "redis", "StrictRedis.execute_command", _traced_execute_command - ) - wrap_function_wrapper( - "redis.client", - "BasePipeline.execute", - _traced_execute_pipeline, - ) - wrap_function_wrapper( - "redis.client", - "BasePipeline.immediate_execute_command", - _traced_execute_command, - ) - else: - wrap_function_wrapper( - "redis", "Redis.execute_command", _traced_execute_command - ) - wrap_function_wrapper( - "redis.client", "Pipeline.execute", _traced_execute_pipeline - ) - wrap_function_wrapper( - "redis.client", - "Pipeline.immediate_execute_command", - _traced_execute_command, - ) - - def _uninstrument(self, **kwargs): - if redis.VERSION < (3, 0, 0): - unwrap(redis.StrictRedis, "execute_command") - unwrap(redis.StrictRedis, "pipeline") - unwrap(redis.Redis, "pipeline") - unwrap( - redis.client.BasePipeline, # pylint:disable=no-member - "execute", - ) - unwrap( - redis.client.BasePipeline, # pylint:disable=no-member - "immediate_execute_command", - ) - else: - unwrap(redis.Redis, "execute_command") - unwrap(redis.Redis, "pipeline") - unwrap(redis.client.Pipeline, "execute") - unwrap(redis.client.Pipeline, "immediate_execute_command") diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py deleted file mode 100644 index 28951340893..00000000000 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -""" -Some utils used by the redis integration -""" - - -def _extract_conn_attributes(conn_kwargs): - """ Transform redis conn info into dict """ - attributes = { - "db.type": "redis", - "db.instance": conn_kwargs.get("db", 0), - } - try: - attributes["db.url"] = "redis://{}:{}".format( - conn_kwargs["host"], conn_kwargs["port"] - ) - except KeyError: - pass # don't include url attribute - - return attributes - - -def _format_command_args(args): - """Format command arguments and trim them as needed""" - value_max_len = 100 - value_too_long_mark = "..." - cmd_max_len = 1000 - length = 0 - out = [] - for arg in args: - cmd = str(arg) - - if len(cmd) > value_max_len: - cmd = cmd[:value_max_len] + value_too_long_mark - - if length + len(cmd) > cmd_max_len: - prefix = cmd[: cmd_max_len - length] - out.append("%s%s" % (prefix, value_too_long_mark)) - break - - out.append(cmd) - length += len(cmd) - - return " ".join(out) diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/version.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-redis/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-redis/tests/__init__.py deleted file mode 100644 index b0a6f428417..00000000000 --- a/instrumentation/opentelemetry-instrumentation-redis/tests/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py b/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py deleted file mode 100644 index 3e07ac725e2..00000000000 --- a/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from unittest import mock - -import redis - -from opentelemetry.instrumentation.redis import RedisInstrumentor -from opentelemetry.test.test_base import TestBase -from opentelemetry.trace import SpanKind - - -class TestRedis(TestBase): - def test_span_properties(self): - redis_client = redis.Redis() - RedisInstrumentor().instrument(tracer_provider=self.tracer_provider) - - with mock.patch.object(redis_client, "connection"): - redis_client.get("key") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertEqual(span.name, "redis.command") - self.assertEqual(span.kind, SpanKind.CLIENT) - - def test_not_recording(self): - redis_client = redis.Redis() - RedisInstrumentor().instrument(tracer_provider=self.tracer_provider) - - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - with mock.patch("opentelemetry.trace.get_tracer") as tracer: - with mock.patch.object(redis_client, "connection"): - tracer.return_value = mock_tracer - redis_client.get("key") - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_instrument_uninstrument(self): - redis_client = redis.Redis() - RedisInstrumentor().instrument(tracer_provider=self.tracer_provider) - - with mock.patch.object(redis_client, "connection"): - redis_client.get("key") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - self.memory_exporter.clear() - - # Test uninstrument - RedisInstrumentor().uninstrument() - - with mock.patch.object(redis_client, "connection"): - redis_client.get("key") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 0) - self.memory_exporter.clear() - - # Test instrument again - RedisInstrumentor().instrument() - - with mock.patch.object(redis_client, "connection"): - redis_client.get("key") - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) diff --git a/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md deleted file mode 100644 index 00d730f4f4d..00000000000 --- a/instrumentation/opentelemetry-instrumentation-requests/CHANGELOG.md +++ /dev/null @@ -1,59 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.15b0 - -Released 2020-11-02 - -- Add support for tracking http metrics - ([#1230](https://github.com/open-telemetry/opentelemetry-python/pull/1230)) - -## Version 0.13b0 - -Released 2020-09-17 - -- Add support for instrumenting prepared requests - ([#1040](https://github.com/open-telemetry/opentelemetry-python/pull/1040)) -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) -- Add support for http metrics - ([#1116](https://github.com/open-telemetry/opentelemetry-python/pull/1116)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-requests - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) -- Span name reported updated to follow semantic conventions to reduce - cardinality ([#972](https://github.com/open-telemetry/opentelemetry-python/pull/972)) - -## 0.7b1 - -Released 2020-05-12 - -- Rename package to opentelemetry-ext-requests - ([#619](https://github.com/open-telemetry/opentelemetry-python/pull/619)) -- Implement instrumentor interface, enabling auto-instrumentation - ([#597](https://github.com/open-telemetry/opentelemetry-python/pull/597)) -- Adding disable_session for more granular instrumentation control - ([#573](https://github.com/open-telemetry/opentelemetry-python/pull/573)) -- Add a callback for custom attributes - ([#656](https://github.com/open-telemetry/opentelemetry-python/pull/656)) - -## 0.3a0 - -Released 2019-10-29 - -## 0.2a0 - -Released 2019-10-29 - -- Updates for core library changes - -## 0.1a0 - -Released 2019-09-30 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-requests/LICENSE b/instrumentation/opentelemetry-instrumentation-requests/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-requests/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-requests/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-requests/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-requests/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-requests/README.rst b/instrumentation/opentelemetry-instrumentation-requests/README.rst deleted file mode 100644 index d4944d35268..00000000000 --- a/instrumentation/opentelemetry-instrumentation-requests/README.rst +++ /dev/null @@ -1,23 +0,0 @@ -OpenTelemetry Requests Instrumentation -====================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-requests.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-requests/ - -This library allows tracing HTTP requests made by the -`requests `_ library. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-requests - -References ----------- - -* `OpenTelemetry requests Instrumentation `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-requests/setup.cfg b/instrumentation/opentelemetry-instrumentation-requests/setup.cfg deleted file mode 100644 index 8aaec6e84ae..00000000000 --- a/instrumentation/opentelemetry-instrumentation-requests/setup.cfg +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-requests -description = OpenTelemetry requests instrumentation -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/master/instrumentation/opentelemetry-instrumentation-requests -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - requests ~= 2.0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - httpretty ~= 1.0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - requests = opentelemetry.instrumentation.requests:RequestsInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-requests/setup.py b/instrumentation/opentelemetry-instrumentation-requests/setup.py deleted file mode 100644 index 237fef583b9..00000000000 --- a/instrumentation/opentelemetry-instrumentation-requests/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "requests", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py deleted file mode 100644 index b4738647dba..00000000000 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ /dev/null @@ -1,251 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -This library allows tracing HTTP requests made by the -`requests `_ library. - -Usage ------ - -.. code-block:: python - - import requests - import opentelemetry.instrumentation.requests - - # You can optionally pass a custom TracerProvider to - # RequestInstrumentor.instrument() - opentelemetry.instrumentation.requests.RequestsInstrumentor().instrument() - response = requests.get(url="https://www.example.org/") - -API ---- -""" - -import functools -import types - -from requests import Timeout, URLRequired -from requests.exceptions import InvalidSchema, InvalidURL, MissingSchema -from requests.sessions import Session -from requests.structures import CaseInsensitiveDict - -from opentelemetry import context, propagators -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.metric import ( - HTTPMetricRecorder, - HTTPMetricType, - MetricMixin, -) -from opentelemetry.instrumentation.requests.version import __version__ -from opentelemetry.instrumentation.utils import http_status_to_status_code -from opentelemetry.trace import SpanKind, get_tracer -from opentelemetry.trace.status import ( - EXCEPTION_STATUS_FIELD, - Status, - StatusCode, -) - -# A key to a context variable to avoid creating duplicate spans when instrumenting -# both, Session.request and Session.send, since Session.request calls into Session.send -_SUPPRESS_REQUESTS_INSTRUMENTATION_KEY = "suppress_requests_instrumentation" - - -# pylint: disable=unused-argument -# pylint: disable=R0915 -def _instrument(tracer_provider=None, span_callback=None): - """Enables tracing of all requests calls that go through - :code:`requests.session.Session.request` (this includes - :code:`requests.get`, etc.).""" - - # Since - # https://github.com/psf/requests/commit/d72d1162142d1bf8b1b5711c664fbbd674f349d1 - # (v0.7.0, Oct 23, 2011), get, post, etc are implemented via request which - # again, is implemented via Session.request (`Session` was named `session` - # before v1.0.0, Dec 17, 2012, see - # https://github.com/psf/requests/commit/4e5c4a6ab7bb0195dececdd19bb8505b872fe120) - - wrapped_request = Session.request - wrapped_send = Session.send - - @functools.wraps(wrapped_request) - def instrumented_request(self, method, url, *args, **kwargs): - def get_or_create_headers(): - headers = kwargs.get("headers") - if headers is None: - headers = {} - kwargs["headers"] = headers - - return headers - - def call_wrapped(): - return wrapped_request(self, method, url, *args, **kwargs) - - return _instrumented_requests_call( - method, url, call_wrapped, get_or_create_headers - ) - - @functools.wraps(wrapped_send) - def instrumented_send(self, request, **kwargs): - def get_or_create_headers(): - request.headers = ( - request.headers - if request.headers is not None - else CaseInsensitiveDict() - ) - return request.headers - - def call_wrapped(): - return wrapped_send(self, request, **kwargs) - - return _instrumented_requests_call( - request.method, request.url, call_wrapped, get_or_create_headers - ) - - def _instrumented_requests_call( - method: str, url: str, call_wrapped, get_or_create_headers - ): - if context.get_value("suppress_instrumentation") or context.get_value( - _SUPPRESS_REQUESTS_INSTRUMENTATION_KEY - ): - return call_wrapped() - - # See - # https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md#http-client - method = method.upper() - span_name = "HTTP {}".format(method) - - recorder = RequestsInstrumentor().metric_recorder - - labels = {} - labels["http.method"] = method - labels["http.url"] = url - - with get_tracer( - __name__, __version__, tracer_provider - ).start_as_current_span(span_name, kind=SpanKind.CLIENT) as span: - exception = None - with recorder.record_client_duration(labels): - if span.is_recording(): - span.set_attribute("component", "http") - span.set_attribute("http.method", method) - span.set_attribute("http.url", url) - - headers = get_or_create_headers() - propagators.inject(type(headers).__setitem__, headers) - - token = context.attach( - context.set_value( - _SUPPRESS_REQUESTS_INSTRUMENTATION_KEY, True - ) - ) - try: - result = call_wrapped() # *** PROCEED - except Exception as exc: # pylint: disable=W0703 - exception = exc - setattr( - exception, EXCEPTION_STATUS_FIELD, StatusCode.ERROR, - ) - result = getattr(exc, "response", None) - finally: - context.detach(token) - - if result is not None: - if span.is_recording(): - span.set_attribute( - "http.status_code", result.status_code - ) - span.set_attribute("http.status_text", result.reason) - span.set_status( - Status( - http_status_to_status_code(result.status_code) - ) - ) - labels["http.status_code"] = str(result.status_code) - if result.raw and result.raw.version: - labels["http.flavor"] = ( - str(result.raw.version)[:1] - + "." - + str(result.raw.version)[:-1] - ) - if span_callback is not None: - span_callback(span, result) - - if exception is not None: - raise exception.with_traceback(exception.__traceback__) - - return result - - instrumented_request.opentelemetry_instrumentation_requests_applied = True - Session.request = instrumented_request - - instrumented_send.opentelemetry_instrumentation_requests_applied = True - Session.send = instrumented_send - - -def _uninstrument(): - """Disables instrumentation of :code:`requests` through this module. - - Note that this only works if no other module also patches requests.""" - _uninstrument_from(Session) - - -def _uninstrument_from(instr_root, restore_as_bound_func=False): - for instr_func_name in ("request", "send"): - instr_func = getattr(instr_root, instr_func_name) - if not getattr( - instr_func, - "opentelemetry_instrumentation_requests_applied", - False, - ): - continue - - original = instr_func.__wrapped__ # pylint:disable=no-member - if restore_as_bound_func: - original = types.MethodType(original, instr_root) - setattr(instr_root, instr_func_name, original) - - -class RequestsInstrumentor(BaseInstrumentor, MetricMixin): - """An instrumentor for requests - See `BaseInstrumentor` - """ - - def _instrument(self, **kwargs): - """Instruments requests module - - Args: - **kwargs: Optional arguments - ``tracer_provider``: a TracerProvider, defaults to global - ``span_callback``: An optional callback invoked before returning the http response. Invoked with Span and requests.Response - """ - _instrument( - tracer_provider=kwargs.get("tracer_provider"), - span_callback=kwargs.get("span_callback"), - ) - self.init_metrics( - __name__, __version__, - ) - # pylint: disable=W0201 - self.metric_recorder = HTTPMetricRecorder( - self.meter, HTTPMetricType.CLIENT - ) - - def _uninstrument(self, **kwargs): - _uninstrument() - - @staticmethod - def uninstrument_session(session): - """Disables instrumentation on the session object.""" - _uninstrument_from(session, restore_as_bound_func=True) diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/version.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py deleted file mode 100644 index f5209108e33..00000000000 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ /dev/null @@ -1,380 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import abc -from unittest import mock - -import httpretty -import requests - -import opentelemetry.instrumentation.requests -from opentelemetry import context, propagators, trace -from opentelemetry.instrumentation.requests import RequestsInstrumentor -from opentelemetry.sdk import resources -from opentelemetry.sdk.util import get_dict_as_key -from opentelemetry.test.mock_textmap import MockTextMapPropagator -from opentelemetry.test.test_base import TestBase -from opentelemetry.trace.status import StatusCode - - -class RequestsIntegrationTestBase(abc.ABC): - # pylint: disable=no-member - - URL = "http://httpbin.org/status/200" - - # pylint: disable=invalid-name - def setUp(self): - super().setUp() - RequestsInstrumentor().instrument() - httpretty.enable() - httpretty.register_uri(httpretty.GET, self.URL, body="Hello!") - - # pylint: disable=invalid-name - def tearDown(self): - super().tearDown() - RequestsInstrumentor().uninstrument() - httpretty.disable() - - def assert_span(self, exporter=None, num_spans=1): - if exporter is None: - exporter = self.memory_exporter - span_list = exporter.get_finished_spans() - self.assertEqual(num_spans, len(span_list)) - if num_spans == 0: - return None - if num_spans == 1: - return span_list[0] - return span_list - - @staticmethod - @abc.abstractmethod - def perform_request(url: str, session: requests.Session = None): - pass - - def test_basic(self): - result = self.perform_request(self.URL) - self.assertEqual(result.text, "Hello!") - span = self.assert_span() - - self.assertIs(span.kind, trace.SpanKind.CLIENT) - self.assertEqual(span.name, "HTTP GET") - - self.assertEqual( - span.attributes, - { - "component": "http", - "http.method": "GET", - "http.url": self.URL, - "http.status_code": 200, - "http.status_text": "OK", - }, - ) - - self.assertIs(span.status.status_code, trace.status.StatusCode.UNSET) - - self.check_span_instrumentation_info( - span, opentelemetry.instrumentation.requests - ) - - self.assertIsNotNone(RequestsInstrumentor().meter) - self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) - recorder = RequestsInstrumentor().meter.metrics.pop() - match_key = get_dict_as_key( - { - "http.flavor": "1.1", - "http.method": "GET", - "http.status_code": "200", - "http.url": "http://httpbin.org/status/200", - } - ) - for key in recorder.bound_instruments.keys(): - self.assertEqual(key, match_key) - # pylint: disable=protected-access - bound = recorder.bound_instruments.get(key) - for view_data in bound.view_datas: - self.assertEqual(view_data.labels, key) - self.assertEqual(view_data.aggregator.current.count, 1) - self.assertGreaterEqual(view_data.aggregator.current.sum, 0) - - def test_not_foundbasic(self): - url_404 = "http://httpbin.org/status/404" - httpretty.register_uri( - httpretty.GET, url_404, status=404, - ) - result = self.perform_request(url_404) - self.assertEqual(result.status_code, 404) - - span = self.assert_span() - - self.assertEqual(span.attributes.get("http.status_code"), 404) - self.assertEqual(span.attributes.get("http.status_text"), "Not Found") - - self.assertIs( - span.status.status_code, trace.status.StatusCode.ERROR, - ) - - def test_uninstrument(self): - RequestsInstrumentor().uninstrument() - result = self.perform_request(self.URL) - self.assertEqual(result.text, "Hello!") - self.assert_span(num_spans=0) - # instrument again to avoid annoying warning message - RequestsInstrumentor().instrument() - - def test_uninstrument_session(self): - session1 = requests.Session() - RequestsInstrumentor().uninstrument_session(session1) - - result = self.perform_request(self.URL, session1) - self.assertEqual(result.text, "Hello!") - self.assert_span(num_spans=0) - - # Test that other sessions as well as global requests is still - # instrumented - session2 = requests.Session() - result = self.perform_request(self.URL, session2) - self.assertEqual(result.text, "Hello!") - self.assert_span() - - self.memory_exporter.clear() - - result = self.perform_request(self.URL) - self.assertEqual(result.text, "Hello!") - self.assert_span() - - def test_suppress_instrumentation(self): - token = context.attach( - context.set_value("suppress_instrumentation", True) - ) - try: - result = self.perform_request(self.URL) - self.assertEqual(result.text, "Hello!") - finally: - context.detach(token) - - self.assert_span(num_spans=0) - - def test_not_recording(self): - with mock.patch("opentelemetry.trace.INVALID_SPAN") as mock_span: - RequestsInstrumentor().uninstrument() - # original_tracer_provider returns a default tracer provider, which - # in turn will return an INVALID_SPAN, which is always not recording - RequestsInstrumentor().instrument( - tracer_provider=self.original_tracer_provider - ) - mock_span.is_recording.return_value = False - result = self.perform_request(self.URL) - self.assertEqual(result.text, "Hello!") - self.assert_span(None, 0) - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_distributed_context(self): - previous_propagator = propagators.get_global_textmap() - try: - propagators.set_global_textmap(MockTextMapPropagator()) - result = self.perform_request(self.URL) - self.assertEqual(result.text, "Hello!") - - span = self.assert_span() - - headers = dict(httpretty.last_request().headers) - self.assertIn(MockTextMapPropagator.TRACE_ID_KEY, headers) - self.assertEqual( - str(span.get_span_context().trace_id), - headers[MockTextMapPropagator.TRACE_ID_KEY], - ) - self.assertIn(MockTextMapPropagator.SPAN_ID_KEY, headers) - self.assertEqual( - str(span.get_span_context().span_id), - headers[MockTextMapPropagator.SPAN_ID_KEY], - ) - - finally: - propagators.set_global_textmap(previous_propagator) - - def test_span_callback(self): - RequestsInstrumentor().uninstrument() - - def span_callback(span, result: requests.Response): - span.set_attribute( - "http.response.body", result.content.decode("utf-8") - ) - - RequestsInstrumentor().instrument( - tracer_provider=self.tracer_provider, span_callback=span_callback, - ) - - result = self.perform_request(self.URL) - self.assertEqual(result.text, "Hello!") - - span = self.assert_span() - self.assertEqual( - span.attributes, - { - "component": "http", - "http.method": "GET", - "http.url": self.URL, - "http.status_code": 200, - "http.status_text": "OK", - "http.response.body": "Hello!", - }, - ) - - def test_custom_tracer_provider(self): - resource = resources.Resource.create({}) - result = self.create_tracer_provider(resource=resource) - tracer_provider, exporter = result - RequestsInstrumentor().uninstrument() - RequestsInstrumentor().instrument(tracer_provider=tracer_provider) - - result = self.perform_request(self.URL) - self.assertEqual(result.text, "Hello!") - - span = self.assert_span(exporter=exporter) - self.assertIs(span.resource, resource) - - @mock.patch( - "requests.adapters.HTTPAdapter.send", - side_effect=requests.RequestException, - ) - def test_requests_exception_without_response(self, *_, **__): - with self.assertRaises(requests.RequestException): - self.perform_request(self.URL) - - span = self.assert_span() - self.assertEqual( - span.attributes, - {"component": "http", "http.method": "GET", "http.url": self.URL}, - ) - self.assertEqual(span.status.status_code, StatusCode.ERROR) - - self.assertIsNotNone(RequestsInstrumentor().meter) - self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) - recorder = RequestsInstrumentor().meter.metrics.pop() - match_key = get_dict_as_key( - { - "http.method": "GET", - "http.url": "http://httpbin.org/status/200", - } - ) - for key in recorder.bound_instruments.keys(): - self.assertEqual(key, match_key) - # pylint: disable=protected-access - bound = recorder.bound_instruments.get(key) - for view_data in bound.view_datas: - self.assertEqual(view_data.labels, key) - self.assertEqual(view_data.aggregator.current.count, 1) - - mocked_response = requests.Response() - mocked_response.status_code = 500 - mocked_response.reason = "Internal Server Error" - - @mock.patch( - "requests.adapters.HTTPAdapter.send", - side_effect=requests.RequestException(response=mocked_response), - ) - def test_requests_exception_with_response(self, *_, **__): - with self.assertRaises(requests.RequestException): - self.perform_request(self.URL) - - span = self.assert_span() - self.assertEqual( - span.attributes, - { - "component": "http", - "http.method": "GET", - "http.url": self.URL, - "http.status_code": 500, - "http.status_text": "Internal Server Error", - }, - ) - self.assertEqual(span.status.status_code, StatusCode.ERROR) - self.assertIsNotNone(RequestsInstrumentor().meter) - self.assertEqual(len(RequestsInstrumentor().meter.metrics), 1) - recorder = RequestsInstrumentor().meter.metrics.pop() - match_key = get_dict_as_key( - { - "http.method": "GET", - "http.status_code": "500", - "http.url": "http://httpbin.org/status/200", - } - ) - for key in recorder.bound_instruments.keys(): - self.assertEqual(key, match_key) - # pylint: disable=protected-access - bound = recorder.bound_instruments.get(key) - for view_data in bound.view_datas: - self.assertEqual(view_data.labels, key) - self.assertEqual(view_data.aggregator.current.count, 1) - - @mock.patch("requests.adapters.HTTPAdapter.send", side_effect=Exception) - def test_requests_basic_exception(self, *_, **__): - with self.assertRaises(Exception): - self.perform_request(self.URL) - - span = self.assert_span() - self.assertEqual(span.status.status_code, StatusCode.ERROR) - - @mock.patch( - "requests.adapters.HTTPAdapter.send", side_effect=requests.Timeout - ) - def test_requests_timeout_exception(self, *_, **__): - with self.assertRaises(Exception): - self.perform_request(self.URL) - - span = self.assert_span() - self.assertEqual(span.status.status_code, StatusCode.ERROR) - - -class TestRequestsIntegration(RequestsIntegrationTestBase, TestBase): - @staticmethod - def perform_request(url: str, session: requests.Session = None): - if session is None: - return requests.get(url) - return session.get(url) - - def test_invalid_url(self): - url = "http://[::1/nope" - - with self.assertRaises(ValueError): - requests.post(url) - - span = self.assert_span() - - self.assertEqual(span.name, "HTTP POST") - self.assertEqual( - span.attributes, - {"component": "http", "http.method": "POST", "http.url": url}, - ) - self.assertEqual(span.status.status_code, StatusCode.ERROR) - - def test_if_headers_equals_none(self): - result = requests.get(self.URL, headers=None) - self.assertEqual(result.text, "Hello!") - self.assert_span() - - -class TestRequestsIntegrationPreparedRequest( - RequestsIntegrationTestBase, TestBase -): - @staticmethod - def perform_request(url: str, session: requests.Session = None): - if session is None: - session = requests.Session() - request = requests.Request("GET", url) - prepared_request = session.prepare_request(request) - return session.send(prepared_request) diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-sqlalchemy/CHANGELOG.md deleted file mode 100644 index 73fa087a119..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-sqlalchemy - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) - -## 0.7b1 - -Released 2020-05-12 - -- Initial release \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/LICENSE b/instrumentation/opentelemetry-instrumentation-sqlalchemy/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-sqlalchemy/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-sqlalchemy/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-sqlalchemy/README.rst b/instrumentation/opentelemetry-instrumentation-sqlalchemy/README.rst deleted file mode 100644 index f29cbe9ffff..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/README.rst +++ /dev/null @@ -1,24 +0,0 @@ -OpenTelemetry SQLAlchemy Instrumentation -======================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-sqlalchemy.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-sqlalchemy/ - -This library allows tracing requests made by the SQLAlchemy library. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-sqlalchemy - - -References ----------- - -* `SQLAlchemy Project `_ -* `OpenTelemetry SQLAlchemy Tracing `_ -* `OpenTelemetry Project `_ \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/setup.cfg b/instrumentation/opentelemetry-instrumentation-sqlalchemy/setup.cfg deleted file mode 100644 index c6b44013cea..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/setup.cfg +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-sqlalchemy -description = OpenTelemetry SQLAlchemy instrumentation -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/master/instrumentation/opentelemetry-instrumentation-sqlalchemy -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - wrapt >= 1.11.2 - sqlalchemy - -[options.extras_require] -test = - opentelemetry-sdk == 0.16.dev0 - pytest - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - sqlalchemy = opentelemetry.instrumentation.sqlalchemy:SQLAlchemyInstrumentor diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/setup.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/setup.py deleted file mode 100644 index 26d3ef4fc92..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "sqlalchemy", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/__init__.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/__init__.py deleted file mode 100644 index aad6dbfc07c..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/__init__.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Instrument `sqlalchemy`_ to report SQL queries. - -There are two options for instrumenting code. The first option is to use -the ``opentelemetry-instrument`` executable which will automatically -instrument your SQLAlchemy engine. The second is to programmatically enable -instrumentation via the following code: - -.. _sqlalchemy: https://pypi.org/project/sqlalchemy/ - -Usage ------ -.. code:: python - - from sqlalchemy import create_engine - - from opentelemetry import trace - from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor - from opentelemetry.sdk.trace import TracerProvider - import sqlalchemy - - trace.set_tracer_provider(TracerProvider()) - engine = create_engine("sqlite:///:memory:") - SQLAlchemyInstrumentor().instrument( - engine=engine, - service="service-A", - ) - -API ---- -""" -import sqlalchemy -import wrapt -from wrapt import wrap_function_wrapper as _w - -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.sqlalchemy.engine import ( - EngineTracer, - _get_tracer, - _wrap_create_engine, -) -from opentelemetry.instrumentation.utils import unwrap - - -class SQLAlchemyInstrumentor(BaseInstrumentor): - """An instrumentor for SQLAlchemy - See `BaseInstrumentor` - """ - - def _instrument(self, **kwargs): - """Instruments SQLAlchemy engine creation methods and the engine - if passed as an argument. - - Args: - **kwargs: Optional arguments - ``engine``: a SQLAlchemy engine instance - ``tracer_provider``: a TracerProvider, defaults to global - ``service``: the name of the service to trace. - - Returns: - An instrumented engine if passed in as an argument, None otherwise. - """ - _w("sqlalchemy", "create_engine", _wrap_create_engine) - _w("sqlalchemy.engine", "create_engine", _wrap_create_engine) - if kwargs.get("engine") is not None: - return EngineTracer( - _get_tracer( - kwargs.get("engine"), kwargs.get("tracer_provider") - ), - kwargs.get("service"), - kwargs.get("engine"), - ) - return None - - def _uninstrument(self, **kwargs): - unwrap(sqlalchemy, "create_engine") - unwrap(sqlalchemy.engine, "create_engine") diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py deleted file mode 100644 index 7c97c685da5..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/engine.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from sqlalchemy.event import listen - -from opentelemetry import trace -from opentelemetry.instrumentation.sqlalchemy.version import __version__ -from opentelemetry.trace.status import Status, StatusCode - -# Network attribute semantic convention here: -# https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/span-general.md#general-network-connection-attributes -_HOST = "net.peer.name" -_PORT = "net.peer.port" -# Database semantic conventions here: -# https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md -_ROWS = "sql.rows" # number of rows returned by a query -_STMT = "db.statement" -_DB = "db.type" -_URL = "db.url" - - -def _normalize_vendor(vendor): - """Return a canonical name for a type of database.""" - if not vendor: - return "db" # should this ever happen? - - if "sqlite" in vendor: - return "sqlite" - - if "postgres" in vendor or vendor == "psycopg2": - return "postgres" - - return vendor - - -def _get_tracer(engine, tracer_provider=None): - if tracer_provider is None: - tracer_provider = trace.get_tracer_provider() - return tracer_provider.get_tracer( - _normalize_vendor(engine.name), __version__ - ) - - -# pylint: disable=unused-argument -def _wrap_create_engine(func, module, args, kwargs): - """Trace the SQLAlchemy engine, creating an `EngineTracer` - object that will listen to SQLAlchemy events. - """ - engine = func(*args, **kwargs) - EngineTracer(_get_tracer(engine), None, engine) - return engine - - -class EngineTracer: - def __init__(self, tracer, service, engine): - self.tracer = tracer - self.engine = engine - self.vendor = _normalize_vendor(engine.name) - self.service = service or self.vendor - self.name = "%s.query" % self.vendor - self.current_span = None - - listen(engine, "before_cursor_execute", self._before_cur_exec) - listen(engine, "after_cursor_execute", self._after_cur_exec) - listen(engine, "handle_error", self._handle_error) - - # pylint: disable=unused-argument - def _before_cur_exec(self, conn, cursor, statement, *args): - self.current_span = self.tracer.start_span(self.name) - with self.tracer.use_span(self.current_span, end_on_exit=False): - if self.current_span.is_recording(): - self.current_span.set_attribute("service", self.vendor) - self.current_span.set_attribute(_STMT, statement) - - if not _set_attributes_from_url( - self.current_span, conn.engine.url - ): - _set_attributes_from_cursor( - self.current_span, self.vendor, cursor - ) - - # pylint: disable=unused-argument - def _after_cur_exec(self, conn, cursor, statement, *args): - if self.current_span is None: - return - - try: - if ( - cursor - and cursor.rowcount >= 0 - and self.current_span.is_recording() - ): - self.current_span.set_attribute(_ROWS, cursor.rowcount) - finally: - self.current_span.end() - - def _handle_error(self, context): - if self.current_span is None: - return - - try: - if self.current_span.is_recording(): - self.current_span.set_status( - Status(StatusCode.ERROR, str(context.original_exception),) - ) - finally: - self.current_span.end() - - -def _set_attributes_from_url(span: trace.Span, url): - """Set connection tags from the url. return true if successful.""" - if span.is_recording(): - if url.host: - span.set_attribute(_HOST, url.host) - if url.port: - span.set_attribute(_PORT, url.port) - if url.database: - span.set_attribute(_DB, url.database) - - return bool(url.host) - - -def _set_attributes_from_cursor(span: trace.Span, vendor, cursor): - """Attempt to set db connection attributes by introspecting the cursor.""" - if not span.is_recording(): - return - if vendor == "postgres": - # pylint: disable=import-outside-toplevel - from psycopg2.extensions import parse_dsn - - if hasattr(cursor, "connection") and hasattr(cursor.connection, "dsn"): - dsn = getattr(cursor.connection, "dsn", None) - if dsn: - data = parse_dsn(dsn) - span.set_attribute(_DB, data.get("dbname")) - span.set_attribute(_HOST, data.get("host")) - span.set_attribute(_PORT, int(data.get("port"))) diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/version.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/src/opentelemetry/instrumentation/sqlalchemy/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/__init__.py deleted file mode 100644 index b0a6f428417..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py deleted file mode 100644 index 3b2e3548a5c..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from unittest import mock - -from sqlalchemy import create_engine - -from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor -from opentelemetry.test.test_base import TestBase - - -class TestSqlalchemyInstrumentation(TestBase): - def tearDown(self): - super().tearDown() - SQLAlchemyInstrumentor().uninstrument() - - def test_trace_integration(self): - engine = create_engine("sqlite:///:memory:") - SQLAlchemyInstrumentor().instrument( - engine=engine, - tracer_provider=self.tracer_provider, - service="my-database", - ) - cnx = engine.connect() - cnx.execute("SELECT 1 + 1;").fetchall() - spans = self.memory_exporter.get_finished_spans() - - self.assertEqual(len(spans), 1) - self.assertEqual(spans[0].name, "sqlite.query") - - def test_not_recording(self): - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - with mock.patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - engine = create_engine("sqlite:///:memory:") - SQLAlchemyInstrumentor().instrument( - engine=engine, - tracer_provider=self.tracer_provider, - service="my-database", - ) - cnx = engine.connect() - cnx.execute("SELECT 1 + 1;").fetchall() - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_create_engine_wrapper(self): - SQLAlchemyInstrumentor().instrument() - from sqlalchemy import create_engine # pylint: disable-all - - engine = create_engine("sqlite:///:memory:") - cnx = engine.connect() - cnx.execute("SELECT 1 + 1;").fetchall() - spans = self.memory_exporter.get_finished_spans() - - self.assertEqual(len(spans), 1) - self.assertEqual(spans[0].name, "sqlite.query") diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-sqlite3/CHANGELOG.md deleted file mode 100644 index 39545604081..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-sqlite3 - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) - -## 0.8b0 - -Released 2020-05-27 - -- Initial release \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/LICENSE b/instrumentation/opentelemetry-instrumentation-sqlite3/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-sqlite3/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-sqlite3/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-sqlite3/README.rst b/instrumentation/opentelemetry-instrumentation-sqlite3/README.rst deleted file mode 100644 index 0d2aa2dd992..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/README.rst +++ /dev/null @@ -1,21 +0,0 @@ -OpenTelemetry SQLite3 Instrumentation -===================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-sqlite3.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-sqlite3/ - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-sqlite3 - - -References ----------- -* `OpenTelemetry SQLite3 Instrumentation `_ -* `OpenTelemetry Project `_ - diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/setup.cfg b/instrumentation/opentelemetry-instrumentation-sqlite3/setup.cfg deleted file mode 100644 index d8145be3911..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/setup.cfg +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-sqlite3 -description = OpenTelemetry SQLite3 instrumentation -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/master/instrumentation/opentelemetry-instrumentation-sqlite3 -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation-dbapi == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - wrapt >= 1.0.0, < 2.0.0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src - -[options.entry_points] -opentelemetry_instrumentor = - sqlite3 = opentelemetry.instrumentation.sqlite3:SQLite3Instrumentor diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/setup.py b/instrumentation/opentelemetry-instrumentation-sqlite3/setup.py deleted file mode 100644 index 67ce350fab5..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "sqlite3", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/__init__.py b/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/__init__.py deleted file mode 100644 index 568e83f0d6a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/__init__.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -SQLite instrumentation supporting `sqlite3`_, it can be enabled by -using ``SQLite3Instrumentor``. - -.. _sqlite3: https://docs.python.org/3/library/sqlite3.html - -Usage ------ - -.. code:: python - - import sqlite3 - from opentelemetry import trace - from opentelemetry.trace import TracerProvider - from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor - - trace.set_tracer_provider(TracerProvider()) - - SQLite3Instrumentor().instrument() - - cnx = sqlite3.connect('example.db') - cursor = cnx.cursor() - cursor.execute("INSERT INTO test (testField) VALUES (123)") - cursor.close() - cnx.close() - -API ---- -""" - -import sqlite3 - -from opentelemetry.instrumentation import dbapi -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.sqlite3.version import __version__ -from opentelemetry.trace import get_tracer - - -class SQLite3Instrumentor(BaseInstrumentor): - # No useful attributes of sqlite3 connection object - _CONNECTION_ATTRIBUTES = {} - - _DATABASE_COMPONENT = "sqlite3" - _DATABASE_TYPE = "sql" - - def _instrument(self, **kwargs): - """Integrate with SQLite3 Python library. - https://docs.python.org/3/library/sqlite3.html - """ - tracer_provider = kwargs.get("tracer_provider") - - dbapi.wrap_connect( - __name__, - sqlite3, - "connect", - self._DATABASE_COMPONENT, - self._DATABASE_TYPE, - self._CONNECTION_ATTRIBUTES, - version=__version__, - tracer_provider=tracer_provider, - ) - - def _uninstrument(self, **kwargs): - """"Disable SQLite3 instrumentation""" - dbapi.unwrap_connect(sqlite3, "connect") - - # pylint:disable=no-self-use - def instrument_connection(self, connection): - """Enable instrumentation in a SQLite connection. - - Args: - connection: The connection to instrument. - - Returns: - An instrumented connection. - """ - tracer = get_tracer(__name__, __version__) - - return dbapi.instrument_connection( - tracer, - connection, - self._DATABASE_COMPONENT, - self._DATABASE_TYPE, - self._CONNECTION_ATTRIBUTES, - ) - - def uninstrument_connection(self, connection): - """Disable instrumentation in a SQLite connection. - - Args: - connection: The connection to uninstrument. - - Returns: - An uninstrumented connection. - """ - return dbapi.uninstrument_connection(connection) diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/version.py b/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/src/opentelemetry/instrumentation/sqlite3/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-sqlite3/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/tests/test_sqlite3.py b/instrumentation/opentelemetry-instrumentation-sqlite3/tests/test_sqlite3.py deleted file mode 100644 index 0e385cf3e76..00000000000 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/tests/test_sqlite3.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright 2020, 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 sqlite3 - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor -from opentelemetry.test.test_base import TestBase - - -class TestSQLite3(TestBase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls._connection = None - cls._cursor = None - cls._tracer = cls.tracer_provider.get_tracer(__name__) - SQLite3Instrumentor().instrument(tracer_provider=cls.tracer_provider) - cls._connection = sqlite3.connect(":memory:") - cls._cursor = cls._connection.cursor() - - @classmethod - def tearDownClass(cls): - if cls._cursor: - cls._cursor.close() - if cls._connection: - cls._connection.close() - - def validate_spans(self): - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 2) - for span in spans: - if span.name == "rootSpan": - root_span = span - else: - child_span = span - self.assertIsInstance(span.start_time, int) - self.assertIsInstance(span.end_time, int) - self.assertIsNotNone(root_span) - self.assertIsNotNone(child_span) - self.assertEqual(root_span.name, "rootSpan") - self.assertEqual(child_span.name, "sqlite3") - self.assertIsNotNone(child_span.parent) - self.assertIs(child_span.parent, root_span.get_span_context()) - self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT) - - def test_execute(self): - """Should create a child span for execute method - """ - with self._tracer.start_as_current_span("rootSpan"): - self._cursor.execute( - "CREATE TABLE IF NOT EXISTS test (id integer)" - ) - self.validate_spans() - - def test_executemany(self): - """Should create a child span for executemany - """ - with self._tracer.start_as_current_span("rootSpan"): - data = [("1",), ("2",), ("3",)] - stmt = "INSERT INTO test (id) VALUES (?)" - self._cursor.executemany(stmt, data) - self.validate_spans() - - def test_callproc(self): - """Should create a child span for callproc - """ - with self._tracer.start_as_current_span("rootSpan"), self.assertRaises( - Exception - ): - self._cursor.callproc("test", ()) - self.validate_spans() diff --git a/instrumentation/opentelemetry-instrumentation-starlette/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-starlette/CHANGELOG.md deleted file mode 100644 index 1991025f6cb..00000000000 --- a/instrumentation/opentelemetry-instrumentation-starlette/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.10b0 - -Released 2020-06-23 - -- Initial release ([#777](https://github.com/open-telemetry/opentelemetry-python/pull/777)) \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-starlette/README.rst b/instrumentation/opentelemetry-instrumentation-starlette/README.rst deleted file mode 100644 index 1d05c0b717f..00000000000 --- a/instrumentation/opentelemetry-instrumentation-starlette/README.rst +++ /dev/null @@ -1,45 +0,0 @@ -OpenTelemetry Starlette Instrumentation -======================================= - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-starlette.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-starlette/ - - -This library provides automatic and manual instrumentation of Starlette web frameworks, -instrumenting http requests served by applications utilizing the framework. - -auto-instrumentation using the opentelemetry-instrumentation package is also supported. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-starlette - - -Usage ------ - -.. code-block:: python - - from opentelemetry.instrumentation.starlette import StarletteInstrumentor - from starlette import applications - from starlette.responses import PlainTextResponse - from starlette.routing import Route - - def home(request): - return PlainTextResponse("hi") - - app = applications.Starlette( - routes=[Route("/foobar", home)] - ) - StarletteInstrumentor.instrument_app(app) - - -References ----------- - -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-starlette/setup.cfg b/instrumentation/opentelemetry-instrumentation-starlette/setup.cfg deleted file mode 100644 index 04b1f3cbec7..00000000000 --- a/instrumentation/opentelemetry-instrumentation-starlette/setup.cfg +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-starlette -description = OpenTelemetry Starlette Instrumentation -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/master/instrumentation/opentelemetry-instrumentation-starlette -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.6 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation-asgi == 0.16.dev0 - -[options.entry_points] -opentelemetry_instrumentor = - starlette = opentelemetry.instrumentation.starlette:StarletteInstrumentor - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - starlette ~= 0.13.0 - requests ~= 2.23.0 # needed for testclient - -[options.packages.find] -where = src diff --git a/instrumentation/opentelemetry-instrumentation-starlette/setup.py b/instrumentation/opentelemetry-instrumentation-starlette/setup.py deleted file mode 100644 index 0232a6f448c..00000000000 --- a/instrumentation/opentelemetry-instrumentation-starlette/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "starlette", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/__init__.py b/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/__init__.py deleted file mode 100644 index f469447f3ac..00000000000 --- a/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from typing import Optional - -from starlette import applications -from starlette.routing import Match - -from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.starlette.version import __version__ # noqa - - -class StarletteInstrumentor(BaseInstrumentor): - """An instrumentor for starlette - - See `BaseInstrumentor` - """ - - _original_starlette = None - - @staticmethod - def instrument_app(app: applications.Starlette): - """Instrument an uninstrumented Starlette application. - """ - if not getattr(app, "is_instrumented_by_opentelemetry", False): - app.add_middleware( - OpenTelemetryMiddleware, - span_details_callback=_get_route_details, - ) - app.is_instrumented_by_opentelemetry = True - - def _instrument(self, **kwargs): - self._original_starlette = applications.Starlette - applications.Starlette = _InstrumentedStarlette - - def _uninstrument(self, **kwargs): - applications.Starlette = self._original_starlette - - -class _InstrumentedStarlette(applications.Starlette): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.add_middleware( - OpenTelemetryMiddleware, span_details_callback=_get_route_details - ) - - -def _get_route_details(scope): - """Callback to retrieve the starlette route being served. - - TODO: there is currently no way to retrieve http.route from - a starlette application from scope. - - See: https://github.com/encode/starlette/pull/804 - """ - app = scope["app"] - route = None - for starlette_route in app.routes: - match, _ = starlette_route.matches(scope) - if match == Match.FULL: - route = starlette_route.path - break - if match == Match.PARTIAL: - route = starlette_route.path - # method only exists for http, if websocket - # leave it blank. - span_name = route or scope.get("method", "") - attributes = {} - if route: - attributes["http.route"] = route - return span_name, attributes diff --git a/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/version.py b/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-starlette/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-starlette/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py b/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py deleted file mode 100644 index a49db07c9a7..00000000000 --- a/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest - -from starlette import applications -from starlette.responses import PlainTextResponse -from starlette.routing import Route -from starlette.testclient import TestClient - -import opentelemetry.instrumentation.starlette as otel_starlette -from opentelemetry.test.test_base import TestBase - - -class TestStarletteManualInstrumentation(TestBase): - def _create_app(self): - app = self._create_starlette_app() - self._instrumentor.instrument_app(app) - return app - - def setUp(self): - super().setUp() - self._instrumentor = otel_starlette.StarletteInstrumentor() - self._app = self._create_app() - self._client = TestClient(self._app) - - def test_basic_starlette_call(self): - self._client.get("/foobar") - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 3) - for span in spans: - self.assertIn("/foobar", span.name) - - def test_starlette_route_attribute_added(self): - """Ensure that starlette routes are used as the span name.""" - self._client.get("/user/123") - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 3) - for span in spans: - self.assertIn("/user/{username}", span.name) - self.assertEqual( - spans[-1].attributes["http.route"], "/user/{username}" - ) - # ensure that at least one attribute that is populated by - # the asgi instrumentation is successfully feeding though. - self.assertEqual(spans[-1].attributes["http.flavor"], "1.1") - - @staticmethod - def _create_starlette_app(): - def home(_): - return PlainTextResponse("hi") - - app = applications.Starlette( - routes=[Route("/foobar", home), Route("/user/{username}", home)] - ) - return app - - -class TestAutoInstrumentation(TestStarletteManualInstrumentation): - """Test the auto-instrumented variant - - Extending the manual instrumentation as most test cases apply - to both. - """ - - def _create_app(self): - # instrumentation is handled by the instrument call - self._instrumentor.instrument() - return self._create_starlette_app() - - def tearDown(self): - self._instrumentor.uninstrument() - super().tearDown() - - -class TestAutoInstrumentationLogic(unittest.TestCase): - def test_instrumentation(self): - """Verify that instrumentation methods are instrumenting and - removing as expected. - """ - instrumentor = otel_starlette.StarletteInstrumentor() - original = applications.Starlette - instrumentor.instrument() - try: - instrumented = applications.Starlette - self.assertIsNot(original, instrumented) - finally: - instrumentor.uninstrument() - - should_be_original = applications.Starlette - self.assertIs(original, should_be_original) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md deleted file mode 100644 index 5f6ff0530c7..00000000000 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/CHANGELOG.md +++ /dev/null @@ -1,30 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.14b0 - -Released 2020-10-13 - -- Fix issue when specific metrics are not available in certain OS - ([#1207](https://github.com/open-telemetry/opentelemetry-python/pull/1207)) - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-system-metrics - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) - -## 0.9b0 - -Released 2020-06-10 - -- Initial release (https://github.com/open-telemetry/opentelemetry-python/pull/652) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/LICENSE b/instrumentation/opentelemetry-instrumentation-system-metrics/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-system-metrics/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-system-metrics/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-system-metrics/README.rst b/instrumentation/opentelemetry-instrumentation-system-metrics/README.rst deleted file mode 100644 index fc984256bf6..00000000000 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/README.rst +++ /dev/null @@ -1,24 +0,0 @@ -OpenTelemetry System Metrics Instrumentation -============================================ - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-system-metrics.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-system-metrics/ - -Instrumentation to collect system performance metrics. - - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-system-metrics - - -References ----------- -* `OpenTelemetry System Metrics Instrumentation `_ -* `OpenTelemetry Project `_ - diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg deleted file mode 100644 index f8c27e54d0a..00000000000 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.cfg +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-system-metrics -description = OpenTelemetry System Metrics Instrumentation -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/master/instrumentation/opentelemetry-instrumentation-system-metrics -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-sdk == 0.16.dev0 - psutil ~= 5.7.0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.py b/instrumentation/opentelemetry-instrumentation-system-metrics/setup.py deleted file mode 100644 index f0bbf9eff07..00000000000 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "system_metrics", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py deleted file mode 100644 index 2b453dbd7d0..00000000000 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py +++ /dev/null @@ -1,683 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Instrument to report system (CPU, memory, network) and -process (CPU, memory, garbage collection) metrics. By default, the -following metrics are configured: - -.. code:: python - - { - "system.cpu.time": ["idle", "user", "system", "irq"], - "system.cpu.utilization": ["idle", "user", "system", "irq"], - "system.memory.usage": ["used", "free", "cached"], - "system.memory.utilization": ["used", "free", "cached"], - "system.swap.usage": ["used", "free"], - "system.swap.utilization": ["used", "free"], - "system.disk.io": ["read", "write"], - "system.disk.operations": ["read", "write"], - "system.disk.time": ["read", "write"], - "system.disk.merged": ["read", "write"], - "system.network.dropped.packets": ["transmit", "receive"], - "system.network.packets": ["transmit", "receive"], - "system.network.errors": ["transmit", "receive"], - "system.network.io": ["trasmit", "receive"], - "system.network.connections": ["family", "type"], - "runtime.memory": ["rss", "vms"], - "runtime.cpu.time": ["user", "system"], - } - -Usage ------ - -.. code:: python - - from opentelemetry import metrics - from opentelemetry.instrumentation.system_metrics import SystemMetrics - from opentelemetry.sdk.metrics import MeterProvider, - from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter - - metrics.set_meter_provider(MeterProvider()) - exporter = ConsoleMetricsExporter() - SystemMetrics(exporter) - - # metrics are collected asynchronously - input("...") - - # to configure custom metrics - configuration = { - "system.memory.usage": ["used", "free", "cached"], - "system.cpu.time": ["idle", "user", "system", "irq"], - "system.network.io": ["trasmit", "receive"], - "runtime.memory": ["rss", "vms"], - "runtime.cpu.time": ["user", "system"], - } - SystemMetrics(exporter, config=configuration) - -API ---- -""" - -import gc -import os -import typing -from platform import python_implementation - -import psutil - -from opentelemetry import metrics -from opentelemetry.sdk.metrics.export import MetricsExporter -from opentelemetry.sdk.metrics.export.controller import PushController -from opentelemetry.sdk.util import get_dict_as_key - - -class SystemMetrics: - # pylint: disable=too-many-statements - def __init__( - self, - exporter: MetricsExporter, - interval: int = 30, - labels: typing.Optional[typing.Dict[str, str]] = None, - config: typing.Optional[typing.Dict[str, typing.List[str]]] = None, - ): - self._labels = {} if labels is None else labels - self.meter = metrics.get_meter(__name__) - self.controller = PushController( - meter=self.meter, exporter=exporter, interval=interval - ) - self._python_implementation = python_implementation().lower() - if config is None: - self._config = { - "system.cpu.time": ["idle", "user", "system", "irq"], - "system.cpu.utilization": ["idle", "user", "system", "irq"], - "system.memory.usage": ["used", "free", "cached"], - "system.memory.utilization": ["used", "free", "cached"], - "system.swap.usage": ["used", "free"], - "system.swap.utilization": ["used", "free"], - # system.swap.page.faults: [], - # system.swap.page.operations: [], - "system.disk.io": ["read", "write"], - "system.disk.operations": ["read", "write"], - "system.disk.time": ["read", "write"], - "system.disk.merged": ["read", "write"], - # "system.filesystem.usage": [], - # "system.filesystem.utilization": [], - "system.network.dropped.packets": ["transmit", "receive"], - "system.network.packets": ["transmit", "receive"], - "system.network.errors": ["transmit", "receive"], - "system.network.io": ["trasmit", "receive"], - "system.network.connections": ["family", "type"], - "runtime.memory": ["rss", "vms"], - "runtime.cpu.time": ["user", "system"], - } - else: - self._config = config - - self._proc = psutil.Process(os.getpid()) - - self._system_cpu_time_labels = self._labels.copy() - self._system_cpu_utilization_labels = self._labels.copy() - - self._system_memory_usage_labels = self._labels.copy() - self._system_memory_utilization_labels = self._labels.copy() - - self._system_swap_usage_labels = self._labels.copy() - self._system_swap_utilization_labels = self._labels.copy() - # self._system_swap_page_faults = self._labels.copy() - # self._system_swap_page_operations = self._labels.copy() - - self._system_disk_io_labels = self._labels.copy() - self._system_disk_operations_labels = self._labels.copy() - self._system_disk_time_labels = self._labels.copy() - self._system_disk_merged_labels = self._labels.copy() - - # self._system_filesystem_usage_labels = self._labels.copy() - # self._system_filesystem_utilization_labels = self._labels.copy() - - self._system_network_dropped_packets_labels = self._labels.copy() - self._system_network_packets_labels = self._labels.copy() - self._system_network_errors_labels = self._labels.copy() - self._system_network_io_labels = self._labels.copy() - self._system_network_connections_labels = self._labels.copy() - - self._runtime_memory_labels = self._labels.copy() - self._runtime_cpu_time_labels = self._labels.copy() - self._runtime_gc_count_labels = self._labels.copy() - - self.meter.register_sumobserver( - callback=self._get_system_cpu_time, - name="system.cpu.time", - description="System CPU time", - unit="seconds", - value_type=float, - ) - - self.meter.register_valueobserver( - callback=self._get_system_cpu_utilization, - name="system.cpu.utilization", - description="System CPU utilization", - unit="1", - value_type=float, - ) - - self.meter.register_valueobserver( - callback=self._get_system_memory_usage, - name="system.memory.usage", - description="System memory usage", - unit="bytes", - value_type=int, - ) - - self.meter.register_valueobserver( - callback=self._get_system_memory_utilization, - name="system.memory.utilization", - description="System memory utilization", - unit="1", - value_type=float, - ) - - self.meter.register_valueobserver( - callback=self._get_system_swap_usage, - name="system.swap.usage", - description="System swap usage", - unit="pages", - value_type=int, - ) - - self.meter.register_valueobserver( - callback=self._get_system_swap_utilization, - name="system.swap.utilization", - description="System swap utilization", - unit="1", - value_type=float, - ) - - # self.meter.register_sumobserver( - # callback=self._get_system_swap_page_faults, - # name="system.swap.page_faults", - # description="System swap page faults", - # unit="faults", - # value_type=int, - # ) - - # self.meter.register_sumobserver( - # callback=self._get_system_swap_page_operations, - # name="system.swap.page_operations", - # description="System swap page operations", - # unit="operations", - # value_type=int, - # ) - - self.meter.register_sumobserver( - callback=self._get_system_disk_io, - name="system.disk.io", - description="System disk IO", - unit="bytes", - value_type=int, - ) - - self.meter.register_sumobserver( - callback=self._get_system_disk_operations, - name="system.disk.operations", - description="System disk operations", - unit="operations", - value_type=int, - ) - - self.meter.register_sumobserver( - callback=self._get_system_disk_time, - name="system.disk.time", - description="System disk time", - unit="seconds", - value_type=float, - ) - - self.meter.register_sumobserver( - callback=self._get_system_disk_merged, - name="system.disk.merged", - description="System disk merged", - unit="1", - value_type=int, - ) - - # self.meter.register_valueobserver( - # callback=self._get_system_filesystem_usage, - # name="system.filesystem.usage", - # description="System filesystem usage", - # unit="bytes", - # value_type=int, - # ) - - # self.meter.register_valueobserver( - # callback=self._get_system_filesystem_utilization, - # name="system.filesystem.utilization", - # description="System filesystem utilization", - # unit="1", - # value_type=float, - # ) - - self.meter.register_sumobserver( - callback=self._get_system_network_dropped_packets, - name="system.network.dropped_packets", - description="System network dropped_packets", - unit="packets", - value_type=int, - ) - - self.meter.register_sumobserver( - callback=self._get_system_network_packets, - name="system.network.packets", - description="System network packets", - unit="packets", - value_type=int, - ) - - self.meter.register_sumobserver( - callback=self._get_system_network_errors, - name="system.network.errors", - description="System network errors", - unit="errors", - value_type=int, - ) - - self.meter.register_sumobserver( - callback=self._get_system_network_io, - name="system.network.io", - description="System network io", - unit="bytes", - value_type=int, - ) - - self.meter.register_updownsumobserver( - callback=self._get_system_network_connections, - name="system.network.connections", - description="System network connections", - unit="connections", - value_type=int, - ) - - self.meter.register_sumobserver( - callback=self._get_runtime_memory, - name="runtime.{}.memory".format(self._python_implementation), - description="Runtime {} memory".format( - self._python_implementation - ), - unit="bytes", - value_type=int, - ) - - self.meter.register_sumobserver( - callback=self._get_runtime_cpu_time, - name="runtime.{}.cpu_time".format(self._python_implementation), - description="Runtime {} CPU time".format( - self._python_implementation - ), - unit="seconds", - value_type=float, - ) - - self.meter.register_sumobserver( - callback=self._get_runtime_gc_count, - name="runtime.{}.gc_count".format(self._python_implementation), - description="Runtime {} GC count".format( - self._python_implementation - ), - unit="bytes", - value_type=int, - ) - - def _get_system_cpu_time(self, observer: metrics.ValueObserver) -> None: - """Observer callback for system CPU time - - Args: - observer: the observer to update - """ - for cpu, times in enumerate(psutil.cpu_times(percpu=True)): - for metric in self._config["system.cpu.time"]: - if hasattr(times, metric): - self._system_cpu_time_labels["state"] = metric - self._system_cpu_time_labels["cpu"] = cpu + 1 - observer.observe( - getattr(times, metric), self._system_cpu_time_labels - ) - - def _get_system_cpu_utilization( - self, observer: metrics.ValueObserver - ) -> None: - """Observer callback for system CPU utilization - - Args: - observer: the observer to update - """ - - for cpu, times_percent in enumerate( - psutil.cpu_times_percent(percpu=True) - ): - for metric in self._config["system.cpu.utilization"]: - if hasattr(times_percent, metric): - self._system_cpu_utilization_labels["state"] = metric - self._system_cpu_utilization_labels["cpu"] = cpu + 1 - observer.observe( - getattr(times_percent, metric) / 100, - self._system_cpu_utilization_labels, - ) - - def _get_system_memory_usage( - self, observer: metrics.ValueObserver - ) -> None: - """Observer callback for memory usage - - Args: - observer: the observer to update - """ - virtual_memory = psutil.virtual_memory() - for metric in self._config["system.memory.usage"]: - self._system_memory_usage_labels["state"] = metric - if hasattr(virtual_memory, metric): - observer.observe( - getattr(virtual_memory, metric), - self._system_memory_usage_labels, - ) - - def _get_system_memory_utilization( - self, observer: metrics.ValueObserver - ) -> None: - """Observer callback for memory utilization - - Args: - observer: the observer to update - """ - system_memory = psutil.virtual_memory() - - for metric in self._config["system.memory.utilization"]: - self._system_memory_utilization_labels["state"] = metric - if hasattr(system_memory, metric): - observer.observe( - getattr(system_memory, metric) / system_memory.total, - self._system_memory_utilization_labels, - ) - - def _get_system_swap_usage(self, observer: metrics.ValueObserver) -> None: - """Observer callback for swap usage - - Args: - observer: the observer to update - """ - system_swap = psutil.swap_memory() - - for metric in self._config["system.swap.usage"]: - self._system_swap_usage_labels["state"] = metric - if hasattr(system_swap, metric): - observer.observe( - getattr(system_swap, metric), - self._system_swap_usage_labels, - ) - - def _get_system_swap_utilization( - self, observer: metrics.ValueObserver - ) -> None: - """Observer callback for swap utilization - - Args: - observer: the observer to update - """ - system_swap = psutil.swap_memory() - - for metric in self._config["system.swap.utilization"]: - if hasattr(system_swap, metric): - self._system_swap_utilization_labels["state"] = metric - observer.observe( - getattr(system_swap, metric) / system_swap.total, - self._system_swap_utilization_labels, - ) - - # TODO Add _get_system_swap_page_faults - # TODO Add _get_system_swap_page_operations - - def _get_system_disk_io(self, observer: metrics.SumObserver) -> None: - """Observer callback for disk IO - - Args: - observer: the observer to update - """ - for device, counters in psutil.disk_io_counters(perdisk=True).items(): - for metric in self._config["system.disk.io"]: - if hasattr(counters, "{}_bytes".format(metric)): - self._system_disk_io_labels["device"] = device - self._system_disk_io_labels["direction"] = metric - observer.observe( - getattr(counters, "{}_bytes".format(metric)), - self._system_disk_io_labels, - ) - - def _get_system_disk_operations( - self, observer: metrics.SumObserver - ) -> None: - """Observer callback for disk operations - - Args: - observer: the observer to update - """ - for device, counters in psutil.disk_io_counters(perdisk=True).items(): - for metric in self._config["system.disk.operations"]: - if hasattr(counters, "{}_count".format(metric)): - self._system_disk_operations_labels["device"] = device - self._system_disk_operations_labels["direction"] = metric - observer.observe( - getattr(counters, "{}_count".format(metric)), - self._system_disk_operations_labels, - ) - - def _get_system_disk_time(self, observer: metrics.SumObserver) -> None: - """Observer callback for disk time - - Args: - observer: the observer to update - """ - for device, counters in psutil.disk_io_counters(perdisk=True).items(): - for metric in self._config["system.disk.time"]: - if hasattr(counters, "{}_time".format(metric)): - self._system_disk_time_labels["device"] = device - self._system_disk_time_labels["direction"] = metric - observer.observe( - getattr(counters, "{}_time".format(metric)) / 1000, - self._system_disk_time_labels, - ) - - def _get_system_disk_merged(self, observer: metrics.SumObserver) -> None: - """Observer callback for disk merged operations - - Args: - observer: the observer to update - """ - - # FIXME The units in the spec is 1, it seems like it should be - # operations or the value type should be Double - - for device, counters in psutil.disk_io_counters(perdisk=True).items(): - for metric in self._config["system.disk.time"]: - if hasattr(counters, "{}_merged_count".format(metric)): - self._system_disk_merged_labels["device"] = device - self._system_disk_merged_labels["direction"] = metric - observer.observe( - getattr(counters, "{}_merged_count".format(metric)), - self._system_disk_merged_labels, - ) - - # TODO Add _get_system_filesystem_usage - # TODO Add _get_system_filesystem_utilization - # TODO Filesystem information can be obtained with os.statvfs in Unix-like - # OSs, how to do the same in Windows? - - def _get_system_network_dropped_packets( - self, observer: metrics.SumObserver - ) -> None: - """Observer callback for network dropped packets - - Args: - observer: the observer to update - """ - - for device, counters in psutil.net_io_counters(pernic=True).items(): - for metric in self._config["system.network.dropped.packets"]: - in_out = {"receive": "in", "transmit": "out"}[metric] - if hasattr(counters, "drop{}".format(in_out)): - self._system_network_dropped_packets_labels[ - "device" - ] = device - self._system_network_dropped_packets_labels[ - "direction" - ] = metric - observer.observe( - getattr(counters, "drop{}".format(in_out)), - self._system_network_dropped_packets_labels, - ) - - def _get_system_network_packets( - self, observer: metrics.SumObserver - ) -> None: - """Observer callback for network packets - - Args: - observer: the observer to update - """ - - for device, counters in psutil.net_io_counters(pernic=True).items(): - for metric in self._config["system.network.dropped.packets"]: - recv_sent = {"receive": "recv", "transmit": "sent"}[metric] - if hasattr(counters, "packets_{}".format(recv_sent)): - self._system_network_packets_labels["device"] = device - self._system_network_packets_labels["direction"] = metric - observer.observe( - getattr(counters, "packets_{}".format(recv_sent)), - self._system_network_packets_labels, - ) - - def _get_system_network_errors( - self, observer: metrics.SumObserver - ) -> None: - """Observer callback for network errors - - Args: - observer: the observer to update - """ - for device, counters in psutil.net_io_counters(pernic=True).items(): - for metric in self._config["system.network.errors"]: - in_out = {"receive": "in", "transmit": "out"}[metric] - if hasattr(counters, "err{}".format(in_out)): - self._system_network_errors_labels["device"] = device - self._system_network_errors_labels["direction"] = metric - observer.observe( - getattr(counters, "err{}".format(in_out)), - self._system_network_errors_labels, - ) - - def _get_system_network_io(self, observer: metrics.SumObserver) -> None: - """Observer callback for network IO - - Args: - observer: the observer to update - """ - - for device, counters in psutil.net_io_counters(pernic=True).items(): - for metric in self._config["system.network.dropped.packets"]: - recv_sent = {"receive": "recv", "transmit": "sent"}[metric] - if hasattr(counters, "bytes_{}".format(recv_sent)): - self._system_network_io_labels["device"] = device - self._system_network_io_labels["direction"] = metric - observer.observe( - getattr(counters, "bytes_{}".format(recv_sent)), - self._system_network_io_labels, - ) - - def _get_system_network_connections( - self, observer: metrics.UpDownSumObserver - ) -> None: - """Observer callback for network connections - - Args: - observer: the observer to update - """ - # TODO How to find the device identifier for a particular - # connection? - - connection_counters = {} - - for net_connection in psutil.net_connections(): - for metric in self._config["system.network.connections"]: - self._system_network_connections_labels["protocol"] = { - 1: "tcp", - 2: "udp", - }[net_connection.type.value] - self._system_network_connections_labels[ - "state" - ] = net_connection.status - self._system_network_connections_labels[metric] = getattr( - net_connection, metric - ) - - connection_counters_key = get_dict_as_key( - self._system_network_connections_labels - ) - - if connection_counters_key in connection_counters.keys(): - connection_counters[connection_counters_key]["counter"] += 1 - else: - connection_counters[connection_counters_key] = { - "counter": 1, - "labels": self._system_network_connections_labels.copy(), - } - - for connection_counter in connection_counters.values(): - observer.observe( - connection_counter["counter"], connection_counter["labels"], - ) - - def _get_runtime_memory(self, observer: metrics.SumObserver) -> None: - """Observer callback for runtime memory - - Args: - observer: the observer to update - """ - proc_memory = self._proc.memory_info() - for metric in self._config["runtime.memory"]: - if hasattr(proc_memory, metric): - self._runtime_memory_labels["type"] = metric - observer.observe( - getattr(proc_memory, metric), self._runtime_memory_labels, - ) - - def _get_runtime_cpu_time(self, observer: metrics.SumObserver) -> None: - """Observer callback for runtime CPU time - - Args: - observer: the observer to update - """ - proc_cpu = self._proc.cpu_times() - for metric in self._config["runtime.cpu.time"]: - if hasattr(proc_cpu, metric): - self._runtime_cpu_time_labels["type"] = metric - observer.observe( - getattr(proc_cpu, metric), self._runtime_cpu_time_labels, - ) - - def _get_runtime_gc_count(self, observer: metrics.SumObserver) -> None: - """Observer callback for garbage collection - - Args: - observer: the observer to update - """ - for index, count in enumerate(gc.get_count()): - self._runtime_gc_count_labels["count"] = str(index) - observer.observe(count, self._runtime_gc_count_labels) diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py b/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-system-metrics/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py b/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py deleted file mode 100644 index 2f155383f46..00000000000 --- a/instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py +++ /dev/null @@ -1,703 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pylint: disable=protected-access - -from collections import namedtuple -from platform import python_implementation -from unittest import mock - -from opentelemetry import metrics -from opentelemetry.instrumentation.system_metrics import SystemMetrics -from opentelemetry.sdk.metrics.export.aggregate import ValueObserverAggregator -from opentelemetry.test.test_base import TestBase - - -class TestSystemMetrics(TestBase): - def setUp(self): - super().setUp() - self.memory_metrics_exporter.clear() - self.implementation = python_implementation().lower() - - def test_system_metrics_constructor(self): - # ensure the observers have been registered - meter = metrics.get_meter(__name__) - with mock.patch("opentelemetry.metrics.get_meter") as mock_get_meter: - mock_get_meter.return_value = meter - SystemMetrics(self.memory_metrics_exporter) - - self.assertEqual(len(meter.observers), 18) - - observer_names = [ - "system.cpu.time", - "system.cpu.utilization", - "system.memory.usage", - "system.memory.utilization", - "system.swap.usage", - "system.swap.utilization", - "system.disk.io", - "system.disk.operations", - "system.disk.time", - "system.disk.merged", - "system.network.dropped_packets", - "system.network.packets", - "system.network.errors", - "system.network.io", - "system.network.connections", - "runtime.{}.memory".format(self.implementation), - "runtime.{}.cpu_time".format(self.implementation), - "runtime.{}.gc_count".format(self.implementation), - ] - - for observer in meter.observers: - self.assertIn(observer.name, observer_names) - observer_names.remove(observer.name) - - def _assert_metrics(self, observer_name, system_metrics, expected): - system_metrics.controller.tick() - assertions = 0 - for ( - metric - ) in ( - self.memory_metrics_exporter._exported_metrics # pylint: disable=protected-access - ): - if ( - metric.labels in expected - and metric.instrument.name == observer_name - ): - self.assertEqual( - metric.aggregator.checkpoint, expected[metric.labels], - ) - assertions += 1 - self.assertEqual(len(expected), assertions) - - def _test_metrics(self, observer_name, expected): - meter = self.meter_provider.get_meter(__name__) - - with mock.patch("opentelemetry.metrics.get_meter") as mock_get_meter: - mock_get_meter.return_value = meter - system_metrics = SystemMetrics(self.memory_metrics_exporter) - self._assert_metrics(observer_name, system_metrics, expected) - - # When this test case is executed, _get_system_cpu_utilization gets run - # too because of the controller thread which runs all observers. This patch - # is added here to stop a warning that would otherwise be raised. - # pylint: disable=unused-argument - @mock.patch("psutil.cpu_times_percent") - @mock.patch("psutil.cpu_times") - def test_system_cpu_time(self, mock_cpu_times, mock_cpu_times_percent): - CPUTimes = namedtuple("CPUTimes", ["idle", "user", "system", "irq"]) - mock_cpu_times.return_value = [ - CPUTimes(idle=1.2, user=3.4, system=5.6, irq=7.8), - CPUTimes(idle=1.2, user=3.4, system=5.6, irq=7.8), - ] - - expected = { - (("cpu", 1), ("state", "idle"),): 1.2, - (("cpu", 1), ("state", "user"),): 3.4, - (("cpu", 1), ("state", "system"),): 5.6, - (("cpu", 1), ("state", "irq"),): 7.8, - (("cpu", 2), ("state", "idle"),): 1.2, - (("cpu", 2), ("state", "user"),): 3.4, - (("cpu", 2), ("state", "system"),): 5.6, - (("cpu", 2), ("state", "irq"),): 7.8, - } - self._test_metrics("system.cpu.time", expected) - - @mock.patch("psutil.cpu_times_percent") - def test_system_cpu_utilization(self, mock_cpu_times_percent): - CPUTimesPercent = namedtuple( - "CPUTimesPercent", ["idle", "user", "system", "irq"] - ) - mock_cpu_times_percent.return_value = [ - CPUTimesPercent(idle=1.2, user=3.4, system=5.6, irq=7.8), - CPUTimesPercent(idle=1.2, user=3.4, system=5.6, irq=7.8), - ] - - expected = { - (("cpu", 1), ("state", "idle"),): ValueObserverAggregator._TYPE( - min=1.2 / 100, - max=1.2 / 100, - sum=1.2 / 100, - count=1, - last=1.2 / 100, - ), - (("cpu", 1), ("state", "user"),): ValueObserverAggregator._TYPE( - min=3.4 / 100, - max=3.4 / 100, - sum=3.4 / 100, - count=1, - last=3.4 / 100, - ), - (("cpu", 1), ("state", "system"),): ValueObserverAggregator._TYPE( - min=5.6 / 100, - max=5.6 / 100, - sum=5.6 / 100, - count=1, - last=5.6 / 100, - ), - (("cpu", 1), ("state", "irq"),): ValueObserverAggregator._TYPE( - min=7.8 / 100, - max=7.8 / 100, - sum=7.8 / 100, - count=1, - last=7.8 / 100, - ), - (("cpu", 2), ("state", "idle"),): ValueObserverAggregator._TYPE( - min=1.2 / 100, - max=1.2 / 100, - sum=1.2 / 100, - count=1, - last=1.2 / 100, - ), - (("cpu", 2), ("state", "user"),): ValueObserverAggregator._TYPE( - min=3.4 / 100, - max=3.4 / 100, - sum=3.4 / 100, - count=1, - last=3.4 / 100, - ), - (("cpu", 2), ("state", "system"),): ValueObserverAggregator._TYPE( - min=5.6 / 100, - max=5.6 / 100, - sum=5.6 / 100, - count=1, - last=5.6 / 100, - ), - (("cpu", 2), ("state", "irq"),): ValueObserverAggregator._TYPE( - min=7.8 / 100, - max=7.8 / 100, - sum=7.8 / 100, - count=1, - last=7.8 / 100, - ), - } - self._test_metrics("system.cpu.utilization", expected) - - @mock.patch("psutil.virtual_memory") - def test_system_memory_usage(self, mock_virtual_memory): - VirtualMemory = namedtuple( - "VirtualMemory", ["used", "free", "cached", "total"] - ) - mock_virtual_memory.return_value = VirtualMemory( - used=1, free=2, cached=3, total=4 - ) - - expected = { - (("state", "used"),): ValueObserverAggregator._TYPE( - min=1, max=1, sum=1, count=1, last=1 - ), - (("state", "free"),): ValueObserverAggregator._TYPE( - min=2, max=2, sum=2, count=1, last=2 - ), - (("state", "cached"),): ValueObserverAggregator._TYPE( - min=3, max=3, sum=3, count=1, last=3 - ), - } - self._test_metrics("system.memory.usage", expected) - - @mock.patch("psutil.virtual_memory") - def test_system_memory_utilization(self, mock_virtual_memory): - VirtualMemory = namedtuple( - "VirtualMemory", ["used", "free", "cached", "total"] - ) - mock_virtual_memory.return_value = VirtualMemory( - used=1, free=2, cached=3, total=4 - ) - - expected = { - (("state", "used"),): ValueObserverAggregator._TYPE( - min=1 / 4, max=1 / 4, sum=1 / 4, count=1, last=1 / 4 - ), - (("state", "free"),): ValueObserverAggregator._TYPE( - min=2 / 4, max=2 / 4, sum=2 / 4, count=1, last=2 / 4 - ), - (("state", "cached"),): ValueObserverAggregator._TYPE( - min=3 / 4, max=3 / 4, sum=3 / 4, count=1, last=3 / 4 - ), - } - self._test_metrics("system.memory.utilization", expected) - - @mock.patch("psutil.swap_memory") - def test_system_swap_usage(self, mock_swap_memory): - SwapMemory = namedtuple("SwapMemory", ["used", "free", "total"]) - mock_swap_memory.return_value = SwapMemory(used=1, free=2, total=3) - - expected = { - (("state", "used"),): ValueObserverAggregator._TYPE( - min=1, max=1, sum=1, count=1, last=1 - ), - (("state", "free"),): ValueObserverAggregator._TYPE( - min=2, max=2, sum=2, count=1, last=2 - ), - } - self._test_metrics("system.swap.usage", expected) - - @mock.patch("psutil.swap_memory") - def test_system_swap_utilization(self, mock_swap_memory): - SwapMemory = namedtuple("SwapMemory", ["used", "free", "total"]) - mock_swap_memory.return_value = SwapMemory(used=1, free=2, total=3) - - expected = { - (("state", "used"),): ValueObserverAggregator._TYPE( - min=1 / 3, max=1 / 3, sum=1 / 3, count=1, last=1 / 3 - ), - (("state", "free"),): ValueObserverAggregator._TYPE( - min=2 / 3, max=2 / 3, sum=2 / 3, count=1, last=2 / 3 - ), - } - self._test_metrics("system.swap.utilization", expected) - - @mock.patch("psutil.disk_io_counters") - def test_system_disk_io(self, mock_disk_io_counters): - DiskIO = namedtuple( - "DiskIO", - [ - "read_count", - "write_count", - "read_bytes", - "write_bytes", - "read_time", - "write_time", - "read_merged_count", - "write_merged_count", - ], - ) - mock_disk_io_counters.return_value = { - "sda": DiskIO( - read_count=1, - write_count=2, - read_bytes=3, - write_bytes=4, - read_time=5, - write_time=6, - read_merged_count=7, - write_merged_count=8, - ), - "sdb": DiskIO( - read_count=9, - write_count=10, - read_bytes=11, - write_bytes=12, - read_time=13, - write_time=14, - read_merged_count=15, - write_merged_count=16, - ), - } - - expected = { - (("device", "sda"), ("direction", "read"),): 3, - (("device", "sda"), ("direction", "write"),): 4, - (("device", "sdb"), ("direction", "read"),): 11, - (("device", "sdb"), ("direction", "write"),): 12, - } - self._test_metrics("system.disk.io", expected) - - @mock.patch("psutil.disk_io_counters") - def test_system_disk_operations(self, mock_disk_io_counters): - DiskIO = namedtuple( - "DiskIO", - [ - "read_count", - "write_count", - "read_bytes", - "write_bytes", - "read_time", - "write_time", - "read_merged_count", - "write_merged_count", - ], - ) - mock_disk_io_counters.return_value = { - "sda": DiskIO( - read_count=1, - write_count=2, - read_bytes=3, - write_bytes=4, - read_time=5, - write_time=6, - read_merged_count=7, - write_merged_count=8, - ), - "sdb": DiskIO( - read_count=9, - write_count=10, - read_bytes=11, - write_bytes=12, - read_time=13, - write_time=14, - read_merged_count=15, - write_merged_count=16, - ), - } - - expected = { - (("device", "sda"), ("direction", "read"),): 1, - (("device", "sda"), ("direction", "write"),): 2, - (("device", "sdb"), ("direction", "read"),): 9, - (("device", "sdb"), ("direction", "write"),): 10, - } - self._test_metrics("system.disk.operations", expected) - - @mock.patch("psutil.disk_io_counters") - def test_system_disk_time(self, mock_disk_io_counters): - DiskIO = namedtuple( - "DiskIO", - [ - "read_count", - "write_count", - "read_bytes", - "write_bytes", - "read_time", - "write_time", - "read_merged_count", - "write_merged_count", - ], - ) - mock_disk_io_counters.return_value = { - "sda": DiskIO( - read_count=1, - write_count=2, - read_bytes=3, - write_bytes=4, - read_time=5, - write_time=6, - read_merged_count=7, - write_merged_count=8, - ), - "sdb": DiskIO( - read_count=9, - write_count=10, - read_bytes=11, - write_bytes=12, - read_time=13, - write_time=14, - read_merged_count=15, - write_merged_count=16, - ), - } - - expected = { - (("device", "sda"), ("direction", "read"),): 5 / 1000, - (("device", "sda"), ("direction", "write"),): 6 / 1000, - (("device", "sdb"), ("direction", "read"),): 13 / 1000, - (("device", "sdb"), ("direction", "write"),): 14 / 1000, - } - self._test_metrics("system.disk.time", expected) - - @mock.patch("psutil.disk_io_counters") - def test_system_disk_merged(self, mock_disk_io_counters): - DiskIO = namedtuple( - "DiskIO", - [ - "read_count", - "write_count", - "read_bytes", - "write_bytes", - "read_time", - "write_time", - "read_merged_count", - "write_merged_count", - ], - ) - mock_disk_io_counters.return_value = { - "sda": DiskIO( - read_count=1, - write_count=2, - read_bytes=3, - write_bytes=4, - read_time=5, - write_time=6, - read_merged_count=7, - write_merged_count=8, - ), - "sdb": DiskIO( - read_count=9, - write_count=10, - read_bytes=11, - write_bytes=12, - read_time=13, - write_time=14, - read_merged_count=15, - write_merged_count=16, - ), - } - - expected = { - (("device", "sda"), ("direction", "read"),): 7, - (("device", "sda"), ("direction", "write"),): 8, - (("device", "sdb"), ("direction", "read"),): 15, - (("device", "sdb"), ("direction", "write"),): 16, - } - self._test_metrics("system.disk.merged", expected) - - @mock.patch("psutil.net_io_counters") - def test_system_network_dropped_packets(self, mock_net_io_counters): - NetIO = namedtuple( - "NetIO", - [ - "dropin", - "dropout", - "packets_sent", - "packets_recv", - "errin", - "errout", - "bytes_sent", - "bytes_recv", - ], - ) - mock_net_io_counters.return_value = { - "eth0": NetIO( - dropin=1, - dropout=2, - packets_sent=3, - packets_recv=4, - errin=5, - errout=6, - bytes_sent=7, - bytes_recv=8, - ), - "eth1": NetIO( - dropin=9, - dropout=10, - packets_sent=11, - packets_recv=12, - errin=13, - errout=14, - bytes_sent=15, - bytes_recv=16, - ), - } - - expected = { - (("device", "eth0"), ("direction", "receive"),): 1, - (("device", "eth0"), ("direction", "transmit"),): 2, - (("device", "eth1"), ("direction", "receive"),): 9, - (("device", "eth1"), ("direction", "transmit"),): 10, - } - self._test_metrics("system.network.dropped_packets", expected) - - @mock.patch("psutil.net_io_counters") - def test_system_network_packets(self, mock_net_io_counters): - NetIO = namedtuple( - "NetIO", - [ - "dropin", - "dropout", - "packets_sent", - "packets_recv", - "errin", - "errout", - "bytes_sent", - "bytes_recv", - ], - ) - mock_net_io_counters.return_value = { - "eth0": NetIO( - dropin=1, - dropout=2, - packets_sent=3, - packets_recv=4, - errin=5, - errout=6, - bytes_sent=7, - bytes_recv=8, - ), - "eth1": NetIO( - dropin=9, - dropout=10, - packets_sent=11, - packets_recv=12, - errin=13, - errout=14, - bytes_sent=15, - bytes_recv=16, - ), - } - - expected = { - (("device", "eth0"), ("direction", "receive"),): 4, - (("device", "eth0"), ("direction", "transmit"),): 3, - (("device", "eth1"), ("direction", "receive"),): 12, - (("device", "eth1"), ("direction", "transmit"),): 11, - } - self._test_metrics("system.network.packets", expected) - - @mock.patch("psutil.net_io_counters") - def test_system_network_errors(self, mock_net_io_counters): - NetIO = namedtuple( - "NetIO", - [ - "dropin", - "dropout", - "packets_sent", - "packets_recv", - "errin", - "errout", - "bytes_sent", - "bytes_recv", - ], - ) - mock_net_io_counters.return_value = { - "eth0": NetIO( - dropin=1, - dropout=2, - packets_sent=3, - packets_recv=4, - errin=5, - errout=6, - bytes_sent=7, - bytes_recv=8, - ), - "eth1": NetIO( - dropin=9, - dropout=10, - packets_sent=11, - packets_recv=12, - errin=13, - errout=14, - bytes_sent=15, - bytes_recv=16, - ), - } - - expected = { - (("device", "eth0"), ("direction", "receive"),): 5, - (("device", "eth0"), ("direction", "transmit"),): 6, - (("device", "eth1"), ("direction", "receive"),): 13, - (("device", "eth1"), ("direction", "transmit"),): 14, - } - self._test_metrics("system.network.errors", expected) - - @mock.patch("psutil.net_io_counters") - def test_system_network_io(self, mock_net_io_counters): - NetIO = namedtuple( - "NetIO", - [ - "dropin", - "dropout", - "packets_sent", - "packets_recv", - "errin", - "errout", - "bytes_sent", - "bytes_recv", - ], - ) - mock_net_io_counters.return_value = { - "eth0": NetIO( - dropin=1, - dropout=2, - packets_sent=3, - packets_recv=4, - errin=5, - errout=6, - bytes_sent=7, - bytes_recv=8, - ), - "eth1": NetIO( - dropin=9, - dropout=10, - packets_sent=11, - packets_recv=12, - errin=13, - errout=14, - bytes_sent=15, - bytes_recv=16, - ), - } - - expected = { - (("device", "eth0"), ("direction", "receive"),): 8, - (("device", "eth0"), ("direction", "transmit"),): 7, - (("device", "eth1"), ("direction", "receive"),): 16, - (("device", "eth1"), ("direction", "transmit"),): 15, - } - self._test_metrics("system.network.io", expected) - - @mock.patch("psutil.net_connections") - def test_system_network_connections(self, mock_net_connections): - NetConnection = namedtuple( - "NetworkConnection", ["family", "type", "status"] - ) - Type = namedtuple("Type", ["value"]) - mock_net_connections.return_value = [ - NetConnection(family=1, status="ESTABLISHED", type=Type(value=2),), - NetConnection(family=1, status="ESTABLISHED", type=Type(value=1),), - ] - - expected = { - ( - ("family", 1), - ("protocol", "udp"), - ("state", "ESTABLISHED"), - ("type", Type(value=2)), - ): 1, - ( - ("family", 1), - ("protocol", "tcp"), - ("state", "ESTABLISHED"), - ("type", Type(value=1)), - ): 1, - } - self._test_metrics("system.network.connections", expected) - - @mock.patch("psutil.Process.memory_info") - def test_runtime_memory(self, mock_process_memory_info): - - PMem = namedtuple("PMem", ["rss", "vms"]) - - mock_process_memory_info.configure_mock( - **{"return_value": PMem(rss=1, vms=2)} - ) - - expected = { - (("type", "rss"),): 1, - (("type", "vms"),): 2, - } - self._test_metrics( - "runtime.{}.memory".format(self.implementation), expected - ) - - @mock.patch("psutil.Process.cpu_times") - def test_runtime_cpu_time(self, mock_process_cpu_times): - - PCPUTimes = namedtuple("PCPUTimes", ["user", "system"]) - - mock_process_cpu_times.configure_mock( - **{"return_value": PCPUTimes(user=1.1, system=2.2)} - ) - - expected = { - (("type", "user"),): 1.1, - (("type", "system"),): 2.2, - } - self._test_metrics( - "runtime.{}.cpu_time".format(self.implementation), expected - ) - - @mock.patch("gc.get_count") - def test_runtime_get_count(self, mock_gc_get_count): - - mock_gc_get_count.configure_mock(**{"return_value": (1, 2, 3)}) - - expected = { - (("count", "0"),): 1, - (("count", "1"),): 2, - (("count", "2"),): 3, - } - self._test_metrics( - "runtime.{}.gc_count".format(self.implementation), expected - ) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-tornado/CHANGELOG.md deleted file mode 100644 index e341a1f5a39..00000000000 --- a/instrumentation/opentelemetry-instrumentation-tornado/CHANGELOG.md +++ /dev/null @@ -1,15 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.14b0 - -Released 2020-10-13 - -- Added support for `OTEL_PYTHON_TORNADO_TRACED_REQUEST_ATTRS` ([#1178](https://github.com/open-telemetry/opentelemetry-python/pull/1178)) - -## Version 0.13b0 - -Released 2020-09-17 - -- Initial release. Supports Tornado 6.x on Python 3.5 and newer. diff --git a/instrumentation/opentelemetry-instrumentation-tornado/LICENSE b/instrumentation/opentelemetry-instrumentation-tornado/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-tornado/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-tornado/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-tornado/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-tornado/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-tornado/README.rst b/instrumentation/opentelemetry-instrumentation-tornado/README.rst deleted file mode 100644 index 088c7f0e85d..00000000000 --- a/instrumentation/opentelemetry-instrumentation-tornado/README.rst +++ /dev/null @@ -1,51 +0,0 @@ -OpenTelemetry Tornado Instrumentation -====================================== - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-tornado.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-tornado/ - -This library builds on the OpenTelemetry WSGI middleware to track web requests -in Tornado applications. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-tornado - -Configuration -------------- - -The following environment variables are supported as configuration options: - -- OTEL_PYTHON_TORNADO_EXCLUDED_URLS - -A comma separated list of paths that should not be automatically traced. For example, if this is set to - -:: - - export OTEL_PYTHON_TORNADO_EXLUDED_URLS='/healthz,/ping' - -Then any requests made to ``/healthz`` and ``/ping`` will not be automatically traced. - -Request attributes -******************** -To extract certain attributes from Tornado's request object and use them as span attributes, set the environment variable ``OTEL_PYTHON_TORNADO_TRACED_REQUEST_ATTRS`` to a comma -delimited list of request attribute names. - -For example, - -:: - - export OTEL_PYTHON_TORNADO_TRACED_REQUEST_ATTRS='uri,query' - -will extract path_info and content_type attributes from every traced request and add them as span attributes. - -References ----------- - -* `OpenTelemetry Tornado Instrumentation `_ -* `OpenTelemetry Project `_ diff --git a/instrumentation/opentelemetry-instrumentation-tornado/setup.cfg b/instrumentation/opentelemetry-instrumentation-tornado/setup.cfg deleted file mode 100644 index 889eb0e97c3..00000000000 --- a/instrumentation/opentelemetry-instrumentation-tornado/setup.cfg +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-tornado -description = Tornado instrumentation for OpenTelemetry -long_description = file: README.rst -long_description_content_type = text/x-rst -author = OpenTelemetry Authors -author_email = cncf-opentelemetry-contributors@lists.cncf.io -url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-tornado -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - tornado >= 6.0 - opentelemetry-instrumentation == 0.16.dev0 - opentelemetry-api == 0.16.dev0 - -[options.extras_require] -test = - tornado >= 6.0 - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src diff --git a/instrumentation/opentelemetry-instrumentation-tornado/setup.py b/instrumentation/opentelemetry-instrumentation-tornado/setup.py deleted file mode 100644 index bd6814b50ee..00000000000 --- a/instrumentation/opentelemetry-instrumentation-tornado/setup.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, - "src", - "opentelemetry", - "instrumentation", - "tornado", - "version.py", -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup( - version=PACKAGE_INFO["__version__"], - entry_points={ - "opentelemetry_instrumentor": [ - "tornado = opentelemetry.instrumentation.tornado:TornadoInstrumentor" - ] - }, -) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py deleted file mode 100644 index 6bb956ecb58..00000000000 --- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -This library uses OpenTelemetry to track web requests in Tornado applications. - -Usage ------ - -.. code-block:: python - - import tornado.web - from opentelemetry.instrumentation.tornado import TornadoInstrumentor - - # apply tornado instrumentation - TornadoInstrumentor().instrument() - - class Handler(tornado.web.RequestHandler): - def get(self): - self.set_status(200) - - app = tornado.web.Application([(r"/", Handler)]) - app.listen(8080) - tornado.ioloop.IOLoop.current().start() -""" - -import inspect -import typing -from collections import namedtuple -from functools import partial, wraps -from logging import getLogger - -import tornado.web -import wrapt -from tornado.routing import Rule -from wrapt import wrap_function_wrapper - -from opentelemetry import configuration, context, propagators, trace -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentation.tornado.version import __version__ -from opentelemetry.instrumentation.utils import ( - extract_attributes_from_object, - http_status_to_status_code, - unwrap, -) -from opentelemetry.trace.propagation.textmap import DictGetter -from opentelemetry.trace.status import Status -from opentelemetry.util import ExcludeList, time_ns - -from .client import fetch_async # pylint: disable=E0401 - -_logger = getLogger(__name__) -_TraceContext = namedtuple("TraceContext", ["activation", "span", "token"]) -_HANDLER_CONTEXT_KEY = "_otel_trace_context_key" -_OTEL_PATCHED_KEY = "_otel_patched_key" - - -def get_excluded_urls(): - urls = configuration.Configuration().TORNADO_EXCLUDED_URLS or "" - if urls: - urls = str.split(urls, ",") - return ExcludeList(urls) - - -def get_traced_request_attrs(): - attrs = configuration.Configuration().TORNADO_TRACED_REQUEST_ATTRS or "" - if attrs: - attrs = [attr.strip() for attr in attrs.split(",")] - else: - attrs = [] - return attrs - - -_excluded_urls = get_excluded_urls() -_traced_attrs = get_traced_request_attrs() - -carrier_getter = DictGetter() - - -class TornadoInstrumentor(BaseInstrumentor): - patched_handlers = [] - original_handler_new = None - - def _instrument(self, **kwargs): - """ - _instrument patches tornado.web.RequestHandler and tornado.httpclient.AsyncHTTPClient classes - to automatically instrument requests both received and sent by Tornado. - - We don't patch RequestHandler._execute as it causes some issues with contextvars based context. - Mainly the context fails to detach from within RequestHandler.on_finish() if it is attached inside - RequestHandler._execute. Same issue plagues RequestHandler.initialize. RequestHandler.prepare works - perfectly on the other hand as it executes in the same context as on_finish and log_exection which - are patched to finish a span after a request is served. - - However, we cannot just patch RequestHandler's prepare method because it is supposed to be overwridden - by sub-classes and since the parent prepare method does not do anything special, sub-classes don't - have to call super() when overriding the method. - - In order to work around this, we patch the __init__ method of RequestHandler and then dynamically patch - the prepare, on_finish and log_exception methods of the derived classes _only_ the first time we see them. - Note that the patch does not apply on every single __init__ call, only the first one for the enture - process lifetime. - """ - tracer_provider = kwargs.get("tracer_provider") - tracer = trace.get_tracer(__name__, __version__, tracer_provider) - - def handler_init(init, handler, args, kwargs): - cls = handler.__class__ - if patch_handler_class(tracer, cls): - self.patched_handlers.append(cls) - return init(*args, **kwargs) - - wrap_function_wrapper( - "tornado.web", "RequestHandler.__init__", handler_init - ) - wrap_function_wrapper( - "tornado.httpclient", - "AsyncHTTPClient.fetch", - partial(fetch_async, tracer), - ) - - def _uninstrument(self, **kwargs): - unwrap(tornado.web.RequestHandler, "__init__") - unwrap(tornado.httpclient.AsyncHTTPClient, "fetch") - for handler in self.patched_handlers: - unpatch_handler_class(handler) - self.patched_handlers = [] - - -def patch_handler_class(tracer, cls): - if getattr(cls, _OTEL_PATCHED_KEY, False): - return False - - setattr(cls, _OTEL_PATCHED_KEY, True) - _wrap(cls, "prepare", partial(_prepare, tracer)) - _wrap(cls, "on_finish", partial(_on_finish, tracer)) - _wrap(cls, "log_exception", partial(_log_exception, tracer)) - return True - - -def unpatch_handler_class(cls): - if not getattr(cls, _OTEL_PATCHED_KEY, False): - return - - unwrap(cls, "prepare") - unwrap(cls, "on_finish") - unwrap(cls, "log_exception") - delattr(cls, _OTEL_PATCHED_KEY) - - -def _wrap(cls, method_name, wrapper): - original = getattr(cls, method_name) - wrapper = wrapt.FunctionWrapper(original, wrapper) - wrapt.apply_patch(cls, method_name, wrapper) - - -def _prepare(tracer, func, handler, args, kwargs): - start_time = time_ns() - request = handler.request - if _excluded_urls.url_disabled(request.uri): - return func(*args, **kwargs) - _start_span(tracer, handler, start_time) - return func(*args, **kwargs) - - -def _on_finish(tracer, func, handler, args, kwargs): - _finish_span(tracer, handler) - return func(*args, **kwargs) - - -def _log_exception(tracer, func, handler, args, kwargs): - error = None - if len(args) == 3: - error = args[1] - - _finish_span(tracer, handler, error) - return func(*args, **kwargs) - - -def _get_attributes_from_request(request): - attrs = { - "component": "tornado", - "http.method": request.method, - "http.scheme": request.protocol, - "http.host": request.host, - "http.target": request.path, - } - - if request.host: - attrs["http.host"] = request.host - - if request.remote_ip: - attrs["net.peer.ip"] = request.remote_ip - - return extract_attributes_from_object(request, _traced_attrs, attrs) - - -def _get_operation_name(handler, request): - full_class_name = type(handler).__name__ - class_name = full_class_name.rsplit(".")[-1] - return "{0}.{1}".format(class_name, request.method.lower()) - - -def _start_span(tracer, handler, start_time) -> _TraceContext: - token = context.attach( - propagators.extract(carrier_getter, handler.request.headers,) - ) - - span = tracer.start_span( - _get_operation_name(handler, handler.request), - kind=trace.SpanKind.SERVER, - start_time=start_time, - ) - if span.is_recording(): - attributes = _get_attributes_from_request(handler.request) - for key, value in attributes.items(): - span.set_attribute(key, value) - - activation = tracer.use_span(span, end_on_exit=True) - activation.__enter__() - ctx = _TraceContext(activation, span, token) - setattr(handler, _HANDLER_CONTEXT_KEY, ctx) - return ctx - - -def _finish_span(tracer, handler, error=None): - status_code = handler.get_status() - reason = getattr(handler, "_reason") - finish_args = (None, None, None) - ctx = getattr(handler, _HANDLER_CONTEXT_KEY, None) - - if error: - if isinstance(error, tornado.web.HTTPError): - status_code = error.status_code - if not ctx and status_code == 404: - ctx = _start_span(tracer, handler, time_ns()) - if status_code != 404: - finish_args = ( - type(error), - error, - getattr(error, "__traceback__", None), - ) - status_code = 500 - reason = None - - if not ctx: - return - - if ctx.span.is_recording(): - if reason: - ctx.span.set_attribute("http.status_text", reason) - ctx.span.set_attribute("http.status_code", status_code) - ctx.span.set_status( - Status( - status_code=http_status_to_status_code(status_code), - description=reason, - ) - ) - - ctx.activation.__exit__(*finish_args) - context.detach(ctx.token) - delattr(handler, _HANDLER_CONTEXT_KEY) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/client.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/client.py deleted file mode 100644 index 5ec001bdab4..00000000000 --- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/client.py +++ /dev/null @@ -1,81 +0,0 @@ -import functools - -from tornado.httpclient import HTTPError, HTTPRequest - -from opentelemetry import propagators, trace -from opentelemetry.instrumentation.utils import http_status_to_status_code -from opentelemetry.trace.status import Status -from opentelemetry.util import time_ns - - -def _normalize_request(args, kwargs): - req = args[0] - if not isinstance(req, str): - return (args, kwargs) - - new_kwargs = {} - for param in ("callback", "raise_error"): - if param in kwargs: - new_kwargs[param] = kwargs.pop(param) - - req = HTTPRequest(req, **kwargs) - new_args = [req] - new_args.extend(args[1:]) - return (new_args, new_kwargs) - - -def fetch_async(tracer, func, _, args, kwargs): - start_time = time_ns() - - # Return immediately if no args were provided (error) - # or original_request is set (meaning we are in a redirect step). - if len(args) == 0 or hasattr(args[0], "original_request"): - return func(*args, **kwargs) - - # Force the creation of a HTTPRequest object if needed, - # so we can inject the context into the headers. - args, kwargs = _normalize_request(args, kwargs) - request = args[0] - - span = tracer.start_span( - request.method, kind=trace.SpanKind.CLIENT, start_time=start_time, - ) - - if span.is_recording(): - attributes = { - "component": "tornado", - "http.url": request.url, - "http.method": request.method, - } - for key, value in attributes.items(): - span.set_attribute(key, value) - - with tracer.use_span(span): - propagators.inject(type(request.headers).__setitem__, request.headers) - future = func(*args, **kwargs) - future.add_done_callback( - functools.partial(_finish_tracing_callback, span=span) - ) - return future - - -def _finish_tracing_callback(future, span): - status_code = None - description = None - exc = future.exception() - if span.is_recording() and exc: - if isinstance(exc, HTTPError): - status_code = exc.code - description = "{}: {}".format(type(exc).__name__, exc) - else: - status_code = future.result().code - - if status_code is not None: - span.set_attribute("http.status_code", status_code) - span.set_status( - Status( - status_code=http_status_to_status_code(status_code), - description=description, - ) - ) - span.end() diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/version.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-tornado/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-tornado/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py deleted file mode 100644 index 5b429766ec3..00000000000 --- a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py +++ /dev/null @@ -1,392 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from unittest.mock import Mock, patch - -from tornado.testing import AsyncHTTPTestCase - -from opentelemetry import trace -from opentelemetry.instrumentation.tornado import ( - TornadoInstrumentor, - patch_handler_class, - unpatch_handler_class, -) -from opentelemetry.test.test_base import TestBase -from opentelemetry.trace import SpanKind -from opentelemetry.util import ExcludeList - -from .tornado_test_app import ( - AsyncHandler, - DynamicHandler, - MainHandler, - make_app, -) - - -class TornadoTest(AsyncHTTPTestCase, TestBase): - def get_app(self): - tracer = trace.get_tracer(__name__) - app = make_app(tracer) - return app - - def setUp(self): - TornadoInstrumentor().instrument() - super().setUp() - - def tearDown(self): - TornadoInstrumentor().uninstrument() - super().tearDown() - - -class TestTornadoInstrumentor(TornadoTest): - def test_patch_references(self): - self.assertEqual(len(TornadoInstrumentor().patched_handlers), 0) - - self.fetch("/") - self.fetch("/async") - self.assertEqual( - TornadoInstrumentor().patched_handlers, [MainHandler, AsyncHandler] - ) - - self.fetch("/async") - self.fetch("/") - self.assertEqual( - TornadoInstrumentor().patched_handlers, [MainHandler, AsyncHandler] - ) - - TornadoInstrumentor().uninstrument() - self.assertEqual(TornadoInstrumentor().patched_handlers, []) - - def test_patch_applied_only_once(self): - tracer = trace.get_tracer(__name__) - self.assertTrue(patch_handler_class(tracer, AsyncHandler)) - self.assertFalse(patch_handler_class(tracer, AsyncHandler)) - self.assertFalse(patch_handler_class(tracer, AsyncHandler)) - unpatch_handler_class(AsyncHandler) - - -class TestTornadoInstrumentation(TornadoTest): - def test_http_calls(self): - methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"] - for method in methods: - self._test_http_method_call(method) - - def _test_http_method_call(self, method): - body = "" if method in ["POST", "PUT", "PATCH"] else None - response = self.fetch("/", method=method, body=body) - self.assertEqual(response.code, 201) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 3) - - manual, server, client = self.sorted_spans(spans) - - self.assertEqual(manual.name, "manual") - self.assertEqual(manual.parent, server.context) - self.assertEqual(manual.context.trace_id, client.context.trace_id) - - self.assertEqual(server.name, "MainHandler." + method.lower()) - self.assertTrue(server.parent.is_remote) - self.assertNotEqual(server.parent, client.context) - self.assertEqual(server.parent.span_id, client.context.span_id) - self.assertEqual(server.context.trace_id, client.context.trace_id) - self.assertEqual(server.kind, SpanKind.SERVER) - self.assert_span_has_attributes( - server, - { - "component": "tornado", - "http.method": method, - "http.scheme": "http", - "http.host": "127.0.0.1:" + str(self.get_http_port()), - "http.target": "/", - "net.peer.ip": "127.0.0.1", - "http.status_text": "Created", - "http.status_code": 201, - }, - ) - - self.assertEqual(client.name, method) - self.assertFalse(client.context.is_remote) - self.assertIsNone(client.parent) - self.assertEqual(client.kind, SpanKind.CLIENT) - self.assert_span_has_attributes( - client, - { - "component": "tornado", - "http.url": self.get_url("/"), - "http.method": method, - "http.status_code": 201, - }, - ) - - self.memory_exporter.clear() - - def test_not_recording(self): - mock_tracer = Mock() - mock_span = Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = True - with patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - self.fetch("/") - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_async_handler(self): - self._test_async_handler("/async", "AsyncHandler") - - def test_coroutine_handler(self): - self._test_async_handler("/cor", "CoroutineHandler") - - def _test_async_handler(self, url, handler_name): - response = self.fetch(url) - self.assertEqual(response.code, 201) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 5) - - sub2, sub1, sub_wrapper, server, client = self.sorted_spans(spans) - - self.assertEqual(sub2.name, "sub-task-2") - self.assertEqual(sub2.parent, sub_wrapper.context) - self.assertEqual(sub2.context.trace_id, client.context.trace_id) - - self.assertEqual(sub1.name, "sub-task-1") - self.assertEqual(sub1.parent, sub_wrapper.context) - self.assertEqual(sub1.context.trace_id, client.context.trace_id) - - self.assertEqual(sub_wrapper.name, "sub-task-wrapper") - self.assertEqual(sub_wrapper.parent, server.context) - self.assertEqual(sub_wrapper.context.trace_id, client.context.trace_id) - - self.assertEqual(server.name, handler_name + ".get") - self.assertTrue(server.parent.is_remote) - self.assertNotEqual(server.parent, client.context) - self.assertEqual(server.parent.span_id, client.context.span_id) - self.assertEqual(server.context.trace_id, client.context.trace_id) - self.assertEqual(server.kind, SpanKind.SERVER) - self.assert_span_has_attributes( - server, - { - "component": "tornado", - "http.method": "GET", - "http.scheme": "http", - "http.host": "127.0.0.1:" + str(self.get_http_port()), - "http.target": url, - "net.peer.ip": "127.0.0.1", - "http.status_text": "Created", - "http.status_code": 201, - }, - ) - - self.assertEqual(client.name, "GET") - self.assertFalse(client.context.is_remote) - self.assertIsNone(client.parent) - self.assertEqual(client.kind, SpanKind.CLIENT) - self.assert_span_has_attributes( - client, - { - "component": "tornado", - "http.url": self.get_url(url), - "http.method": "GET", - "http.status_code": 201, - }, - ) - - def test_500(self): - response = self.fetch("/error") - self.assertEqual(response.code, 500) - - spans = self.sorted_spans(self.memory_exporter.get_finished_spans()) - self.assertEqual(len(spans), 2) - server, client = spans - - self.assertEqual(server.name, "BadHandler.get") - self.assertEqual(server.kind, SpanKind.SERVER) - self.assert_span_has_attributes( - server, - { - "component": "tornado", - "http.method": "GET", - "http.scheme": "http", - "http.host": "127.0.0.1:" + str(self.get_http_port()), - "http.target": "/error", - "net.peer.ip": "127.0.0.1", - "http.status_code": 500, - }, - ) - - self.assertEqual(client.name, "GET") - self.assertEqual(client.kind, SpanKind.CLIENT) - self.assert_span_has_attributes( - client, - { - "component": "tornado", - "http.url": self.get_url("/error"), - "http.method": "GET", - "http.status_code": 500, - }, - ) - - def test_404(self): - response = self.fetch("/missing-url") - self.assertEqual(response.code, 404) - - spans = self.sorted_spans(self.memory_exporter.get_finished_spans()) - self.assertEqual(len(spans), 2) - server, client = spans - - self.assertEqual(server.name, "ErrorHandler.get") - self.assertEqual(server.kind, SpanKind.SERVER) - self.assert_span_has_attributes( - server, - { - "component": "tornado", - "http.method": "GET", - "http.scheme": "http", - "http.host": "127.0.0.1:" + str(self.get_http_port()), - "http.target": "/missing-url", - "net.peer.ip": "127.0.0.1", - "http.status_text": "Not Found", - "http.status_code": 404, - }, - ) - - self.assertEqual(client.name, "GET") - self.assertEqual(client.kind, SpanKind.CLIENT) - self.assert_span_has_attributes( - client, - { - "component": "tornado", - "http.url": self.get_url("/missing-url"), - "http.method": "GET", - "http.status_code": 404, - }, - ) - - def test_dynamic_handler(self): - response = self.fetch("/dyna") - self.assertEqual(response.code, 404) - self.memory_exporter.clear() - - self._app.add_handlers(r".+", [(r"/dyna", DynamicHandler)]) - - response = self.fetch("/dyna") - self.assertEqual(response.code, 202) - - spans = self.sorted_spans(self.memory_exporter.get_finished_spans()) - self.assertEqual(len(spans), 2) - server, client = spans - - self.assertEqual(server.name, "DynamicHandler.get") - self.assertTrue(server.parent.is_remote) - self.assertNotEqual(server.parent, client.context) - self.assertEqual(server.parent.span_id, client.context.span_id) - self.assertEqual(server.context.trace_id, client.context.trace_id) - self.assertEqual(server.kind, SpanKind.SERVER) - self.assert_span_has_attributes( - server, - { - "component": "tornado", - "http.method": "GET", - "http.scheme": "http", - "http.host": "127.0.0.1:" + str(self.get_http_port()), - "http.target": "/dyna", - "net.peer.ip": "127.0.0.1", - "http.status_text": "Accepted", - "http.status_code": 202, - }, - ) - - self.assertEqual(client.name, "GET") - self.assertFalse(client.context.is_remote) - self.assertIsNone(client.parent) - self.assertEqual(client.kind, SpanKind.CLIENT) - self.assert_span_has_attributes( - client, - { - "component": "tornado", - "http.url": self.get_url("/dyna"), - "http.method": "GET", - "http.status_code": 202, - }, - ) - - @patch( - "opentelemetry.instrumentation.tornado._excluded_urls", - ExcludeList(["healthz", "ping"]), - ) - def test_exclude_lists(self): - def test_excluded(path): - self.fetch(path) - spans = self.sorted_spans( - self.memory_exporter.get_finished_spans() - ) - self.assertEqual(len(spans), 1) - client = spans[0] - self.assertEqual(client.name, "GET") - self.assertEqual(client.kind, SpanKind.CLIENT) - self.assert_span_has_attributes( - client, - { - "component": "tornado", - "http.url": self.get_url(path), - "http.method": "GET", - "http.status_code": 200, - }, - ) - self.memory_exporter.clear() - - test_excluded("/healthz") - test_excluded("/ping") - - @patch( - "opentelemetry.instrumentation.tornado._traced_attrs", - ["uri", "full_url", "query"], - ) - def test_traced_attrs(self): - self.fetch("/ping?q=abc&b=123") - spans = self.sorted_spans(self.memory_exporter.get_finished_spans()) - self.assertEqual(len(spans), 2) - server_span = spans[0] - self.assertEqual(server_span.kind, SpanKind.SERVER) - self.assert_span_has_attributes( - server_span, {"uri": "/ping?q=abc&b=123", "query": "q=abc&b=123"} - ) - self.memory_exporter.clear() - - -class TestTornadoUninstrument(TornadoTest): - def test_uninstrument(self): - response = self.fetch("/") - self.assertEqual(response.code, 201) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 3) - manual, server, client = self.sorted_spans(spans) - self.assertEqual(manual.name, "manual") - self.assertEqual(server.name, "MainHandler.get") - self.assertEqual(client.name, "GET") - self.memory_exporter.clear() - - TornadoInstrumentor().uninstrument() - - response = self.fetch("/") - self.assertEqual(response.code, 201) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - manual = spans[0] - self.assertEqual(manual.name, "manual") diff --git a/instrumentation/opentelemetry-instrumentation-tornado/tests/tornado_test_app.py b/instrumentation/opentelemetry-instrumentation-tornado/tests/tornado_test_app.py deleted file mode 100644 index 307dc60b76d..00000000000 --- a/instrumentation/opentelemetry-instrumentation-tornado/tests/tornado_test_app.py +++ /dev/null @@ -1,99 +0,0 @@ -# pylint: disable=W0223,R0201 - -import tornado.web -from tornado import gen - - -class AsyncHandler(tornado.web.RequestHandler): - async def get(self): - with self.application.tracer.start_as_current_span("sub-task-wrapper"): - await self.do_something1() - await self.do_something2() - self.set_status(201) - self.write("{}") - - async def do_something1(self): - with self.application.tracer.start_as_current_span("sub-task-1"): - tornado.gen.sleep(0.1) - - async def do_something2(self): - with self.application.tracer.start_as_current_span("sub-task-2"): - tornado.gen.sleep(0.1) - - -class CoroutineHandler(tornado.web.RequestHandler): - @gen.coroutine - def get(self): - with self.application.tracer.start_as_current_span("sub-task-wrapper"): - yield self.do_something1() - yield self.do_something2() - self.set_status(201) - self.write("{}") - - @gen.coroutine - def do_something1(self): - with self.application.tracer.start_as_current_span("sub-task-1"): - tornado.gen.sleep(0.1) - - @gen.coroutine - def do_something2(self): - with self.application.tracer.start_as_current_span("sub-task-2"): - tornado.gen.sleep(0.1) - - -class MainHandler(tornado.web.RequestHandler): - def _handler(self): - with self.application.tracer.start_as_current_span("manual"): - self.write("Hello, world") - self.set_status(201) - - def get(self): - return self._handler() - - def post(self): - return self._handler() - - def patch(self): - return self._handler() - - def delete(self): - return self._handler() - - def put(self): - return self._handler() - - def head(self): - return self._handler() - - def options(self): - return self._handler() - - -class BadHandler(tornado.web.RequestHandler): - def get(self): - raise NameError("some random name error") - - -class DynamicHandler(tornado.web.RequestHandler): - def get(self): - self.set_status(202) - - -class HealthCheckHandler(tornado.web.RequestHandler): - def get(self): - self.set_status(200) - - -def make_app(tracer): - app = tornado.web.Application( - [ - (r"/", MainHandler), - (r"/error", BadHandler), - (r"/cor", CoroutineHandler), - (r"/async", AsyncHandler), - (r"/healthz", HealthCheckHandler), - (r"/ping", HealthCheckHandler), - ] - ) - app.tracer = tracer - return app diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-wsgi/CHANGELOG.md deleted file mode 100644 index 68bd25237c8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-wsgi/CHANGELOG.md +++ /dev/null @@ -1,51 +0,0 @@ -# Changelog - -## Unreleased - -## Version 0.13b0 - -Released 2020-09-17 - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 - -Released 2020-08-14 - -- Change package name to opentelemetry-instrumentation-wsgi - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) - -## Version 0.11b0 - -Released 2020-07-28 - -- Set span status on wsgi errors - ([#864](https://github.com/open-telemetry/opentelemetry-python/pull/864)) - -## 0.4a0 - -Released 2020-02-21 - -- Updating network connection attribute names - ([#350](https://github.com/open-telemetry/opentelemetry-python/pull/350)) - -## 0.3a0 - -Released 2019-12-11 - -- Support new semantic conventions - ([#299](https://github.com/open-telemetry/opentelemetry-python/pull/299)) -- Updates for core library changes - -## 0.2a0 - -Released 2019-10-29 - -- Updates for core library changes - -## 0.1a0 - -Released 2019-09-30 - -- Initial release diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/LICENSE b/instrumentation/opentelemetry-instrumentation-wsgi/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/instrumentation/opentelemetry-instrumentation-wsgi/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/instrumentation/opentelemetry-instrumentation-wsgi/MANIFEST.in b/instrumentation/opentelemetry-instrumentation-wsgi/MANIFEST.in deleted file mode 100644 index aed3e33273b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-wsgi/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -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/instrumentation/opentelemetry-instrumentation-wsgi/README.rst b/instrumentation/opentelemetry-instrumentation-wsgi/README.rst deleted file mode 100644 index ac39dac0c7c..00000000000 --- a/instrumentation/opentelemetry-instrumentation-wsgi/README.rst +++ /dev/null @@ -1,26 +0,0 @@ -OpenTelemetry WSGI Middleware -============================= - -|pypi| - -.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-wsgi.svg - :target: https://pypi.org/project/opentelemetry-instrumentation-wsgi/ - - -This library provides a WSGI middleware that can be used on any WSGI framework -(such as Django / Flask) to track requests timing through OpenTelemetry. - -Installation ------------- - -:: - - pip install opentelemetry-instrumentation-wsgi - - -References ----------- - -* `OpenTelemetry WSGI Middleware `_ -* `OpenTelemetry Project `_ -* `WSGI `_ diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/setup.cfg b/instrumentation/opentelemetry-instrumentation-wsgi/setup.cfg deleted file mode 100644 index 585a513412b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-wsgi/setup.cfg +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -[metadata] -name = opentelemetry-instrumentation-wsgi -description = WSGI Middleware for OpenTelemetry -long_description = file: README.rst -long_description_content_type = text/x-rst -author = OpenTelemetry Authors -author_email = cncf-opentelemetry-contributors@lists.cncf.io -url = https://github.com/open-telemetry/opentelemetry-python/tree/master/instrumentation/opentelemetry-instrumentation-wsgi -platforms = any -license = Apache-2.0 -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[options] -python_requires = >=3.5 -package_dir= - =src -packages=find_namespace: -install_requires = - opentelemetry-api == 0.16.dev0 - opentelemetry-instrumentation == 0.16.dev0 - -[options.extras_require] -test = - opentelemetry-test == 0.16.dev0 - -[options.packages.find] -where = src diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/setup.py b/instrumentation/opentelemetry-instrumentation-wsgi/setup.py deleted file mode 100644 index fb4ffa7437b..00000000000 --- a/instrumentation/opentelemetry-instrumentation-wsgi/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os - -import setuptools - -BASE_DIR = os.path.dirname(__file__) -VERSION_FILENAME = os.path.join( - BASE_DIR, "src", "opentelemetry", "instrumentation", "wsgi", "version.py" -) -PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) - -setuptools.setup(version=PACKAGE_INFO["__version__"]) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py deleted file mode 100644 index e1ef92c6baf..00000000000 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ /dev/null @@ -1,248 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -This library provides a WSGI middleware that can be used on any WSGI framework -(such as Django / Flask) to track requests timing through OpenTelemetry. - -Usage (Flask) -------------- - -.. code-block:: python - - from flask import Flask - from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware - - app = Flask(__name__) - app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app) - - @app.route("/") - def hello(): - return "Hello!" - - if __name__ == "__main__": - app.run(debug=True) - - -Usage (Django) --------------- - -Modify the application's ``wsgi.py`` file as shown below. - -.. code-block:: python - - import os - from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware - from django.core.wsgi import get_wsgi_application - - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings') - - application = get_wsgi_application() - application = OpenTelemetryMiddleware(application) - -API ---- -""" - -import functools -import typing -import wsgiref.util as wsgiref_util - -from opentelemetry import context, propagators, trace -from opentelemetry.instrumentation.utils import http_status_to_status_code -from opentelemetry.instrumentation.wsgi.version import __version__ -from opentelemetry.trace.propagation.textmap import DictGetter -from opentelemetry.trace.status import Status, StatusCode - -_HTTP_VERSION_PREFIX = "HTTP/" - - -class CarrierGetter(DictGetter): - def get(self, carrier: dict, key: str) -> typing.List[str]: - """Getter implementation to retrieve a HTTP header value from the - PEP3333-conforming WSGI environ - - Args: - carrier: WSGI environ object - key: header name in environ object - Returns: - A list with a single string with the header value if it exists, - else an empty list. - """ - environ_key = "HTTP_" + key.upper().replace("-", "_") - value = carrier.get(environ_key) - if value is not None: - return [value] - return [] - - def keys(self, carrier): - return [] - - -carrier_getter = CarrierGetter() - - -def setifnotnone(dic, key, value): - if value is not None: - dic[key] = value - - -def collect_request_attributes(environ): - """Collects HTTP request attributes from the PEP3333-conforming - WSGI environ and returns a dictionary to be used as span creation attributes.""" - - result = { - "component": "http", - "http.method": environ.get("REQUEST_METHOD"), - "http.server_name": environ.get("SERVER_NAME"), - "http.scheme": environ.get("wsgi.url_scheme"), - } - - host_port = environ.get("SERVER_PORT") - if host_port is not None: - result.update({"host.port": int(host_port)}) - - setifnotnone(result, "http.host", environ.get("HTTP_HOST")) - target = environ.get("RAW_URI") - if target is None: # Note: `"" or None is None` - target = environ.get("REQUEST_URI") - if target is not None: - result["http.target"] = target - else: - result["http.url"] = wsgiref_util.request_uri(environ) - - remote_addr = environ.get("REMOTE_ADDR") - if remote_addr: - result["net.peer.ip"] = remote_addr - remote_host = environ.get("REMOTE_HOST") - if remote_host and remote_host != remote_addr: - result["net.peer.name"] = remote_host - - user_agent = environ.get("HTTP_USER_AGENT") - if user_agent is not None and len(user_agent) > 0: - result["http.user_agent"] = user_agent - - setifnotnone(result, "net.peer.port", environ.get("REMOTE_PORT")) - flavor = environ.get("SERVER_PROTOCOL", "") - if flavor.upper().startswith(_HTTP_VERSION_PREFIX): - flavor = flavor[len(_HTTP_VERSION_PREFIX) :] - if flavor: - result["http.flavor"] = flavor - - return result - - -def add_response_attributes( - span, start_response_status, response_headers -): # pylint: disable=unused-argument - """Adds HTTP response attributes to span using the arguments - passed to a PEP3333-conforming start_response callable.""" - if not span.is_recording(): - return - status_code, status_text = start_response_status.split(" ", 1) - span.set_attribute("http.status_text", status_text) - - try: - status_code = int(status_code) - except ValueError: - span.set_status( - Status( - StatusCode.ERROR, - "Non-integer HTTP status: " + repr(status_code), - ) - ) - else: - span.set_attribute("http.status_code", status_code) - span.set_status(Status(http_status_to_status_code(status_code))) - - -def get_default_span_name(environ): - """Default implementation for name_callback, returns HTTP {METHOD_NAME}.""" - return "HTTP {}".format(environ.get("REQUEST_METHOD", "")).strip() - - -class OpenTelemetryMiddleware: - """The WSGI application middleware. - - This class is a PEP 3333 conforming WSGI middleware that starts and - annotates spans for any requests it is invoked with. - - Args: - wsgi: The WSGI application callable to forward requests to. - name_callback: Callback which calculates a generic span name for an - incoming HTTP request based on the PEP3333 WSGI environ. - Optional: Defaults to get_default_span_name. - """ - - def __init__(self, wsgi, name_callback=get_default_span_name): - self.wsgi = wsgi - self.tracer = trace.get_tracer(__name__, __version__) - self.name_callback = name_callback - - @staticmethod - def _create_start_response(span, start_response): - @functools.wraps(start_response) - def _start_response(status, response_headers, *args, **kwargs): - add_response_attributes(span, status, response_headers) - return start_response(status, response_headers, *args, **kwargs) - - return _start_response - - def __call__(self, environ, start_response): - """The WSGI application - - Args: - environ: A WSGI environment. - start_response: The WSGI start_response callable. - """ - - token = context.attach(propagators.extract(carrier_getter, environ)) - span_name = self.name_callback(environ) - - span = self.tracer.start_span( - span_name, - kind=trace.SpanKind.SERVER, - attributes=collect_request_attributes(environ), - ) - - try: - with self.tracer.use_span(span): - start_response = self._create_start_response( - span, start_response - ) - iterable = self.wsgi(environ, start_response) - return _end_span_after_iterating( - iterable, span, self.tracer, token - ) - except Exception as ex: - if span.is_recording(): - span.set_status(Status(StatusCode.ERROR, str(ex))) - span.end() - context.detach(token) - raise - - -# Put this in a subfunction to not delay the call to the wrapped -# WSGI application (instrumentation should change the application -# behavior as little as possible). -def _end_span_after_iterating(iterable, span, tracer, token): - try: - with tracer.use_span(span): - for yielded in iterable: - yield yielded - finally: - close = getattr(iterable, "close", None) - if close: - close() - span.end() - context.detach(token) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/version.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/version.py deleted file mode 100644 index 1f98d44fa88..00000000000 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__version__ = "0.16.dev0" diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py deleted file mode 100644 index 144b4cf0692..00000000000 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ /dev/null @@ -1,359 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys -import unittest -import unittest.mock as mock -import wsgiref.util as wsgiref_util -from urllib.parse import urlsplit - -import opentelemetry.instrumentation.wsgi as otel_wsgi -from opentelemetry import trace as trace_api -from opentelemetry.test.wsgitestutil import WsgiTestBase -from opentelemetry.trace.status import StatusCode - - -class Response: - def __init__(self): - self.iter = iter([b"*"]) - self.close_calls = 0 - - def __iter__(self): - return self - - def __next__(self): - return next(self.iter) - - def close(self): - self.close_calls += 1 - - -def simple_wsgi(environ, start_response): - assert isinstance(environ, dict) - start_response("200 OK", [("Content-Type", "text/plain")]) - return [b"*"] - - -def create_iter_wsgi(response): - def iter_wsgi(environ, start_response): - assert isinstance(environ, dict) - start_response("200 OK", [("Content-Type", "text/plain")]) - return response - - return iter_wsgi - - -def create_gen_wsgi(response): - def gen_wsgi(environ, start_response): - result = create_iter_wsgi(response)(environ, start_response) - yield from result - getattr(result, "close", lambda: None)() - - return gen_wsgi - - -def error_wsgi(environ, start_response): - assert isinstance(environ, dict) - try: - raise ValueError - except ValueError: - exc_info = sys.exc_info() - start_response("200 OK", [("Content-Type", "text/plain")], exc_info) - exc_info = None - return [b"*"] - - -def error_wsgi_unhandled(environ, start_response): - assert isinstance(environ, dict) - raise ValueError - - -class TestWsgiApplication(WsgiTestBase): - def validate_response( - self, response, error=None, span_name="HTTP GET", http_method="GET" - ): - while True: - try: - value = next(response) - self.assertEqual(value, b"*") - except StopIteration: - break - - self.assertEqual(self.status, "200 OK") - self.assertEqual( - self.response_headers, [("Content-Type", "text/plain")] - ) - if error: - self.assertIs(self.exc_info[0], error) - self.assertIsInstance(self.exc_info[1], error) - self.assertIsNotNone(self.exc_info[2]) - else: - self.assertIsNone(self.exc_info) - - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, span_name) - self.assertEqual(span_list[0].kind, trace_api.SpanKind.SERVER) - expected_attributes = { - "component": "http", - "http.server_name": "127.0.0.1", - "http.scheme": "http", - "host.port": 80, - "http.host": "127.0.0.1", - "http.flavor": "1.0", - "http.url": "http://127.0.0.1/", - "http.status_text": "OK", - "http.status_code": 200, - } - if http_method is not None: - expected_attributes["http.method"] = http_method - self.assertEqual(span_list[0].attributes, expected_attributes) - - def test_basic_wsgi_call(self): - app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) - response = app(self.environ, self.start_response) - self.validate_response(response) - - def test_wsgi_not_recording(self): - mock_tracer = mock.Mock() - mock_span = mock.Mock() - mock_span.is_recording.return_value = False - mock_tracer.start_span.return_value = mock_span - mock_tracer.use_span.return_value.__enter__ = mock_span - mock_tracer.use_span.return_value.__exit__ = mock_span - with mock.patch("opentelemetry.trace.get_tracer") as tracer: - tracer.return_value = mock_tracer - app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) - # pylint: disable=W0612 - response = app(self.environ, self.start_response) # noqa: F841 - self.assertFalse(mock_span.is_recording()) - self.assertTrue(mock_span.is_recording.called) - self.assertFalse(mock_span.set_attribute.called) - self.assertFalse(mock_span.set_status.called) - - def test_wsgi_iterable(self): - original_response = Response() - iter_wsgi = create_iter_wsgi(original_response) - app = otel_wsgi.OpenTelemetryMiddleware(iter_wsgi) - response = app(self.environ, self.start_response) - # Verify that start_response has been called - self.assertTrue(self.status) - self.validate_response(response) - - # Verify that close has been called exactly once - self.assertEqual(1, original_response.close_calls) - - def test_wsgi_generator(self): - original_response = Response() - gen_wsgi = create_gen_wsgi(original_response) - app = otel_wsgi.OpenTelemetryMiddleware(gen_wsgi) - response = app(self.environ, self.start_response) - # Verify that start_response has not been called - self.assertIsNone(self.status) - self.validate_response(response) - - # Verify that close has been called exactly once - self.assertEqual(original_response.close_calls, 1) - - def test_wsgi_exc_info(self): - app = otel_wsgi.OpenTelemetryMiddleware(error_wsgi) - response = app(self.environ, self.start_response) - self.validate_response(response, error=ValueError) - - def test_wsgi_internal_error(self): - app = otel_wsgi.OpenTelemetryMiddleware(error_wsgi_unhandled) - self.assertRaises(ValueError, app, self.environ, self.start_response) - span_list = self.memory_exporter.get_finished_spans() - self.assertEqual(len(span_list), 1) - self.assertEqual( - span_list[0].status.status_code, StatusCode.ERROR, - ) - - def test_override_span_name(self): - """Test that span_names can be overwritten by our callback function.""" - span_name = "Dymaxion" - - def get_predefined_span_name(scope): - # pylint: disable=unused-argument - return span_name - - app = otel_wsgi.OpenTelemetryMiddleware( - simple_wsgi, name_callback=get_predefined_span_name - ) - response = app(self.environ, self.start_response) - self.validate_response(response, span_name=span_name) - - def test_default_span_name_missing_request_method(self): - """Test that default span_names with missing request method.""" - self.environ.pop("REQUEST_METHOD") - app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) - response = app(self.environ, self.start_response) - self.validate_response(response, span_name="HTTP", http_method=None) - - -class TestWsgiAttributes(unittest.TestCase): - def setUp(self): - self.environ = {} - wsgiref_util.setup_testing_defaults(self.environ) - self.span = mock.create_autospec(trace_api.Span, spec_set=True) - - def test_request_attributes(self): - self.environ["QUERY_STRING"] = "foo=bar" - - attrs = otel_wsgi.collect_request_attributes(self.environ) - self.assertDictEqual( - attrs, - { - "component": "http", - "http.method": "GET", - "http.host": "127.0.0.1", - "http.url": "http://127.0.0.1/?foo=bar", - "host.port": 80, - "http.scheme": "http", - "http.server_name": "127.0.0.1", - "http.flavor": "1.0", - }, - ) - - def validate_url(self, expected_url, raw=False, has_host=True): - parts = urlsplit(expected_url) - expected = { - "http.scheme": parts.scheme, - "host.port": parts.port or (80 if parts.scheme == "http" else 443), - "http.server_name": parts.hostname, # Not true in the general case, but for all tests. - } - if raw: - expected["http.target"] = expected_url.split(parts.netloc, 1)[1] - else: - expected["http.url"] = expected_url - if has_host: - expected["http.host"] = parts.hostname - - attrs = otel_wsgi.collect_request_attributes(self.environ) - self.assertGreaterEqual( - attrs.items(), expected.items(), expected_url + " expected." - ) - - def test_request_attributes_with_partial_raw_uri(self): - self.environ["RAW_URI"] = "/#top" - self.validate_url("http://127.0.0.1/#top", raw=True) - - def test_request_attributes_with_partial_raw_uri_and_nonstandard_port( - self, - ): - self.environ["RAW_URI"] = "/?" - del self.environ["HTTP_HOST"] - self.environ["SERVER_PORT"] = "8080" - self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False) - - def test_https_uri_port(self): - del self.environ["HTTP_HOST"] - self.environ["SERVER_PORT"] = "443" - self.environ["wsgi.url_scheme"] = "https" - self.validate_url("https://127.0.0.1/", has_host=False) - - self.environ["SERVER_PORT"] = "8080" - self.validate_url("https://127.0.0.1:8080/", has_host=False) - - self.environ["SERVER_PORT"] = "80" - self.validate_url("https://127.0.0.1:80/", has_host=False) - - def test_http_uri_port(self): - del self.environ["HTTP_HOST"] - self.environ["SERVER_PORT"] = "80" - self.environ["wsgi.url_scheme"] = "http" - self.validate_url("http://127.0.0.1/", has_host=False) - - self.environ["SERVER_PORT"] = "8080" - self.validate_url("http://127.0.0.1:8080/", has_host=False) - - self.environ["SERVER_PORT"] = "443" - self.validate_url("http://127.0.0.1:443/", has_host=False) - - def test_request_attributes_with_nonstandard_port_and_no_host(self): - del self.environ["HTTP_HOST"] - self.environ["SERVER_PORT"] = "8080" - self.validate_url("http://127.0.0.1:8080/", has_host=False) - - self.environ["SERVER_PORT"] = "443" - self.validate_url("http://127.0.0.1:443/", has_host=False) - - def test_request_attributes_with_conflicting_nonstandard_port(self): - self.environ[ - "HTTP_HOST" - ] += ":8080" # Note that we do not correct SERVER_PORT - expected = { - "http.host": "127.0.0.1:8080", - "http.url": "http://127.0.0.1:8080/", - "host.port": 80, - } - self.assertGreaterEqual( - otel_wsgi.collect_request_attributes(self.environ).items(), - expected.items(), - ) - - def test_request_attributes_with_faux_scheme_relative_raw_uri(self): - self.environ["RAW_URI"] = "//127.0.0.1/?" - self.validate_url("http://127.0.0.1//127.0.0.1/?", raw=True) - - def test_request_attributes_pathless(self): - self.environ["RAW_URI"] = "" - expected = {"http.target": ""} - self.assertGreaterEqual( - otel_wsgi.collect_request_attributes(self.environ).items(), - expected.items(), - ) - - def test_request_attributes_with_full_request_uri(self): - self.environ["HTTP_HOST"] = "127.0.0.1:8080" - self.environ["REQUEST_METHOD"] = "CONNECT" - self.environ[ - "REQUEST_URI" - ] = "127.0.0.1:8080" # Might happen in a CONNECT request - expected = { - "http.host": "127.0.0.1:8080", - "http.target": "127.0.0.1:8080", - } - self.assertGreaterEqual( - otel_wsgi.collect_request_attributes(self.environ).items(), - expected.items(), - ) - - def test_http_user_agent_attribute(self): - self.environ["HTTP_USER_AGENT"] = "test-useragent" - expected = {"http.user_agent": "test-useragent"} - self.assertGreaterEqual( - otel_wsgi.collect_request_attributes(self.environ).items(), - expected.items(), - ) - - def test_response_attributes(self): - otel_wsgi.add_response_attributes(self.span, "404 Not Found", {}) - expected = ( - mock.call("http.status_code", 404), - mock.call("http.status_text", "Not Found"), - ) - self.assertEqual(self.span.set_attribute.call_count, len(expected)) - self.span.set_attribute.assert_has_calls(expected, any_order=True) - - def test_response_attributes_invalid_status_code(self): - otel_wsgi.add_response_attributes(self.span, "Invalid Status Code", {}) - self.assertEqual(self.span.set_attribute.call_count, 1) - self.span.set_attribute.assert_called_with( - "http.status_text", "Status Code" - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/opentelemetry-docker-tests/tests/asyncpg/test_asyncpg_functional.py b/tests/opentelemetry-docker-tests/tests/asyncpg/test_asyncpg_functional.py deleted file mode 100644 index 2e37efe2246..00000000000 --- a/tests/opentelemetry-docker-tests/tests/asyncpg/test_asyncpg_functional.py +++ /dev/null @@ -1,260 +0,0 @@ -import asyncio -import os - -import asyncpg - -from opentelemetry.instrumentation.asyncpg import AsyncPGInstrumentor -from opentelemetry.test.test_base import TestBase -from opentelemetry.trace.status import StatusCode - -POSTGRES_HOST = os.getenv("POSTGRESQL_HOST ", "localhost") -POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT ", "5432")) -POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME ", "opentelemetry-tests") -POSTGRES_PASSWORD = os.getenv("POSTGRESQL_HOST ", "testpassword") -POSTGRES_USER = os.getenv("POSTGRESQL_HOST ", "testuser") - - -def async_call(coro): - loop = asyncio.get_event_loop() - return loop.run_until_complete(coro) - - -class TestFunctionalAsyncPG(TestBase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls._connection = None - cls._cursor = None - cls._tracer = cls.tracer_provider.get_tracer(__name__) - AsyncPGInstrumentor().instrument(tracer_provider=cls.tracer_provider) - cls._connection = async_call( - asyncpg.connect( - database=POSTGRES_DB_NAME, - user=POSTGRES_USER, - password=POSTGRES_PASSWORD, - host=POSTGRES_HOST, - port=POSTGRES_PORT, - ) - ) - - @classmethod - def tearDownClass(cls): - AsyncPGInstrumentor().uninstrument() - - def test_instrumented_execute_method_without_arguments(self, *_, **__): - async_call(self._connection.execute("SELECT 42;")) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - self.assertIs(StatusCode.UNSET, spans[0].status.status_code) - self.assertEqual( - spans[0].attributes, - { - "db.type": "sql", - "db.user": POSTGRES_USER, - "db.instance": POSTGRES_DB_NAME, - "db.statement": "SELECT 42;", - }, - ) - - def test_instrumented_fetch_method_without_arguments(self, *_, **__): - async_call(self._connection.fetch("SELECT 42;")) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - self.assertEqual( - spans[0].attributes, - { - "db.type": "sql", - "db.user": POSTGRES_USER, - "db.instance": POSTGRES_DB_NAME, - "db.statement": "SELECT 42;", - }, - ) - - def test_instrumented_transaction_method(self, *_, **__): - async def _transaction_execute(): - async with self._connection.transaction(): - await self._connection.execute("SELECT 42;") - - async_call(_transaction_execute()) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(3, len(spans)) - self.assertEqual( - { - "db.instance": POSTGRES_DB_NAME, - "db.user": POSTGRES_USER, - "db.type": "sql", - "db.statement": "BEGIN;", - }, - spans[0].attributes, - ) - self.assertIs(StatusCode.UNSET, spans[0].status.status_code) - self.assertEqual( - { - "db.instance": POSTGRES_DB_NAME, - "db.user": POSTGRES_USER, - "db.type": "sql", - "db.statement": "SELECT 42;", - }, - spans[1].attributes, - ) - self.assertIs(StatusCode.UNSET, spans[1].status.status_code) - self.assertEqual( - { - "db.instance": POSTGRES_DB_NAME, - "db.user": POSTGRES_USER, - "db.type": "sql", - "db.statement": "COMMIT;", - }, - spans[2].attributes, - ) - self.assertIs(StatusCode.UNSET, spans[2].status.status_code) - - def test_instrumented_failed_transaction_method(self, *_, **__): - async def _transaction_execute(): - async with self._connection.transaction(): - await self._connection.execute("SELECT 42::uuid;") - - with self.assertRaises(asyncpg.CannotCoerceError): - async_call(_transaction_execute()) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(3, len(spans)) - self.assertEqual( - { - "db.instance": POSTGRES_DB_NAME, - "db.user": POSTGRES_USER, - "db.type": "sql", - "db.statement": "BEGIN;", - }, - spans[0].attributes, - ) - self.assertIs(StatusCode.UNSET, spans[0].status.status_code) - self.assertEqual( - { - "db.instance": POSTGRES_DB_NAME, - "db.user": POSTGRES_USER, - "db.type": "sql", - "db.statement": "SELECT 42::uuid;", - }, - spans[1].attributes, - ) - self.assertEqual( - StatusCode.ERROR, spans[1].status.status_code, - ) - self.assertEqual( - { - "db.instance": POSTGRES_DB_NAME, - "db.user": POSTGRES_USER, - "db.type": "sql", - "db.statement": "ROLLBACK;", - }, - spans[2].attributes, - ) - self.assertIs(StatusCode.UNSET, spans[2].status.status_code) - - def test_instrumented_method_doesnt_capture_parameters(self, *_, **__): - async_call(self._connection.execute("SELECT $1;", "1")) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - self.assertIs(StatusCode.UNSET, spans[0].status.status_code) - self.assertEqual( - spans[0].attributes, - { - "db.type": "sql", - "db.user": POSTGRES_USER, - # This shouldn't be set because we don't capture parameters by - # default - # - # "db.statement.parameters": "('1',)", - "db.instance": POSTGRES_DB_NAME, - "db.statement": "SELECT $1;", - }, - ) - - -class TestFunctionalAsyncPG_CaptureParameters(TestBase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls._connection = None - cls._cursor = None - cls._tracer = cls.tracer_provider.get_tracer(__name__) - AsyncPGInstrumentor(capture_parameters=True).instrument( - tracer_provider=cls.tracer_provider - ) - cls._connection = async_call( - asyncpg.connect( - database=POSTGRES_DB_NAME, - user=POSTGRES_USER, - password=POSTGRES_PASSWORD, - host=POSTGRES_HOST, - port=POSTGRES_PORT, - ) - ) - - @classmethod - def tearDownClass(cls): - AsyncPGInstrumentor().uninstrument() - - def test_instrumented_execute_method_with_arguments(self, *_, **__): - async_call(self._connection.execute("SELECT $1;", "1")) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - self.assertIs(StatusCode.UNSET, spans[0].status.status_code) - self.assertEqual( - spans[0].attributes, - { - "db.type": "sql", - "db.user": POSTGRES_USER, - "db.statement.parameters": "('1',)", - "db.instance": POSTGRES_DB_NAME, - "db.statement": "SELECT $1;", - }, - ) - - def test_instrumented_fetch_method_with_arguments(self, *_, **__): - async_call(self._connection.fetch("SELECT $1;", "1")) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - self.assertEqual( - spans[0].attributes, - { - "db.type": "sql", - "db.user": POSTGRES_USER, - "db.statement.parameters": "('1',)", - "db.instance": POSTGRES_DB_NAME, - "db.statement": "SELECT $1;", - }, - ) - - def test_instrumented_executemany_method_with_arguments(self, *_, **__): - async_call(self._connection.executemany("SELECT $1;", [["1"], ["2"]])) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - self.assertEqual( - { - "db.type": "sql", - "db.statement": "SELECT $1;", - "db.statement.parameters": "([['1'], ['2']],)", - "db.user": POSTGRES_USER, - "db.instance": POSTGRES_DB_NAME, - }, - spans[0].attributes, - ) - - def test_instrumented_execute_interface_error_method(self, *_, **__): - with self.assertRaises(asyncpg.InterfaceError): - async_call(self._connection.execute("SELECT 42;", 1, 2, 3)) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - self.assertEqual( - spans[0].attributes, - { - "db.type": "sql", - "db.instance": POSTGRES_DB_NAME, - "db.user": POSTGRES_USER, - "db.statement.parameters": "(1, 2, 3)", - "db.statement": "SELECT 42;", - }, - ) diff --git a/tests/opentelemetry-docker-tests/tests/celery/conftest.py b/tests/opentelemetry-docker-tests/tests/celery/conftest.py deleted file mode 100644 index 085fe3bab1e..00000000000 --- a/tests/opentelemetry-docker-tests/tests/celery/conftest.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -from functools import wraps - -import pytest - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.celery import CeleryInstrumentor -from opentelemetry.sdk.trace import TracerProvider, export -from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( - InMemorySpanExporter, -) - -REDIS_HOST = os.getenv("REDIS_HOST", "localhost") -REDIS_PORT = int(os.getenv("REDIS_PORT ", "6379")) -REDIS_URL = "redis://{host}:{port}".format(host=REDIS_HOST, port=REDIS_PORT) -BROKER_URL = "{redis}/{db}".format(redis=REDIS_URL, db=0) -BACKEND_URL = "{redis}/{db}".format(redis=REDIS_URL, db=1) - - -@pytest.fixture(scope="session") -def celery_config(): - return {"broker_url": BROKER_URL, "result_backend": BACKEND_URL} - - -@pytest.fixture -def celery_worker_parameters(): - return { - # See https://github.com/celery/celery/issues/3642#issuecomment-457773294 - "perform_ping_check": False, - } - - -@pytest.fixture(autouse=True) -def patch_celery_app(celery_app, celery_worker): - """Patch task decorator on app fixture to reload worker""" - # See https://github.com/celery/celery/issues/3642 - def wrap_task(fn): - @wraps(fn) - def wrapper(*args, **kwargs): - result = fn(*args, **kwargs) - celery_worker.reload() - return result - - return wrapper - - celery_app.task = wrap_task(celery_app.task) - - -@pytest.fixture(autouse=True) -def instrument(tracer_provider, memory_exporter): - CeleryInstrumentor().instrument(tracer_provider=tracer_provider) - memory_exporter.clear() - - yield - - CeleryInstrumentor().uninstrument() - - -@pytest.fixture(scope="session") -def tracer_provider(memory_exporter): - original_tracer_provider = trace_api.get_tracer_provider() - - tracer_provider = TracerProvider() - - span_processor = export.SimpleExportSpanProcessor(memory_exporter) - tracer_provider.add_span_processor(span_processor) - - trace_api.set_tracer_provider(tracer_provider) - - yield tracer_provider - - trace_api.set_tracer_provider(original_tracer_provider) - - -@pytest.fixture(scope="session") -def memory_exporter(): - memory_exporter = InMemorySpanExporter() - return memory_exporter diff --git a/tests/opentelemetry-docker-tests/tests/celery/test_celery_functional.py b/tests/opentelemetry-docker-tests/tests/celery/test_celery_functional.py deleted file mode 100644 index 7786890a5fe..00000000000 --- a/tests/opentelemetry-docker-tests/tests/celery/test_celery_functional.py +++ /dev/null @@ -1,537 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import celery -import pytest -from celery.exceptions import Retry - -import opentelemetry.instrumentation.celery -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.celery import CeleryInstrumentor -from opentelemetry.sdk import resources -from opentelemetry.sdk.trace import TracerProvider, export -from opentelemetry.trace.status import StatusCode - -# set a high timeout for async executions due to issues in CI -ASYNC_GET_TIMEOUT = 120 - - -class MyException(Exception): - pass - - -@pytest.mark.skip(reason="inconsistent test results") -def test_instrumentation_info(celery_app, memory_exporter): - @celery_app.task - def fn_task(): - return 42 - - result = fn_task.apply_async() - assert result.get(timeout=ASYNC_GET_TIMEOUT) == 42 - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 2 - - async_span, run_span = spans - - assert run_span.parent == async_span.context - assert run_span.parent.span_id == async_span.context.span_id - assert run_span.context.trace_id == async_span.context.trace_id - - assert async_span.instrumentation_info.name == "apply_async/{0}".format( - opentelemetry.instrumentation.celery.__name__ - ) - assert async_span.instrumentation_info.version == "apply_async/{0}".format( - opentelemetry.instrumentation.celery.__version__ - ) - assert run_span.instrumentation_info.name == "run/{0}".format( - opentelemetry.instrumentation.celery.__name__ - ) - assert run_span.instrumentation_info.version == "run/{0}".format( - opentelemetry.instrumentation.celery.__version__ - ) - - -def test_fn_task_run(celery_app, memory_exporter): - @celery_app.task - def fn_task(): - return 42 - - t = fn_task.run() - assert t == 42 - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 0 - - -def test_fn_task_call(celery_app, memory_exporter): - @celery_app.task - def fn_task(): - return 42 - - t = fn_task() - assert t == 42 - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 0 - - -def test_fn_task_apply(celery_app, memory_exporter): - @celery_app.task - def fn_task(): - return 42 - - t = fn_task.apply() - assert t.successful() is True - assert t.result == 42 - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 1 - - span = spans[0] - - assert span.status.is_ok is True - assert span.name == "run/test_celery_functional.fn_task" - assert span.attributes.get("messaging.message_id") == t.task_id - assert ( - span.attributes.get("celery.task_name") - == "test_celery_functional.fn_task" - ) - assert span.attributes.get("celery.action") == "run" - assert span.attributes.get("celery.state") == "SUCCESS" - - -def test_fn_task_apply_bind(celery_app, memory_exporter): - @celery_app.task(bind=True) - def fn_task(self): - return self - - t = fn_task.apply() - assert t.successful() is True - assert "fn_task" in t.result.name - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 1 - - span = spans[0] - - assert span.status.is_ok is True - assert span.name == "run/test_celery_functional.fn_task" - assert span.attributes.get("messaging.message_id") == t.task_id - assert ( - span.attributes.get("celery.task_name") - == "test_celery_functional.fn_task" - ) - assert span.attributes.get("celery.action") == "run" - assert span.attributes.get("celery.state") == "SUCCESS" - - -@pytest.mark.skip(reason="inconsistent test results") -def test_fn_task_apply_async(celery_app, memory_exporter): - @celery_app.task - def fn_task_parameters(user, force_logout=False): - return (user, force_logout) - - result = fn_task_parameters.apply_async( - args=["user"], kwargs={"force_logout": True} - ) - assert result.get(timeout=ASYNC_GET_TIMEOUT) == ["user", True] - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 2 - - async_span, run_span = spans - - assert run_span.context.trace_id != async_span.context.trace_id - - assert async_span.status.is_ok is True - assert ( - async_span.name - == "apply_async/test_celery_functional.fn_task_parameters" - ) - assert async_span.attributes.get("celery.action") == "apply_async" - assert async_span.attributes.get("messaging.message_id") == result.task_id - assert ( - async_span.attributes.get("celery.task_name") - == "test_celery_functional.fn_task_parameters" - ) - - assert run_span.status.is_ok is True - assert run_span.name == "test_celery_functional.fn_task_parameters" - assert run_span.attributes.get("celery.action") == "run" - assert run_span.attributes.get("celery.state") == "SUCCESS" - assert run_span.attributes.get("messaging.message_id") == result.task_id - assert ( - run_span.attributes.get("celery.task_name") - == "test_celery_functional.fn_task_parameters" - ) - - -@pytest.mark.skip(reason="inconsistent test results") -def test_concurrent_delays(celery_app, memory_exporter): - @celery_app.task - def fn_task(): - return 42 - - results = [fn_task.delay() for _ in range(100)] - - for result in results: - assert result.get(timeout=ASYNC_GET_TIMEOUT) == 42 - - spans = memory_exporter.get_finished_spans() - - assert len(spans) == 200 - - -@pytest.mark.skip(reason="inconsistent test results") -def test_fn_task_delay(celery_app, memory_exporter): - @celery_app.task - def fn_task_parameters(user, force_logout=False): - return (user, force_logout) - - result = fn_task_parameters.delay("user", force_logout=True) - assert result.get(timeout=ASYNC_GET_TIMEOUT) == ["user", True] - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 2 - - async_span, run_span = spans - - assert run_span.context.trace_id != async_span.context.trace_id - - assert async_span.status.is_ok is True - assert ( - async_span.name - == "apply_async/test_celery_functional.fn_task_parameters" - ) - assert async_span.attributes.get("celery.action") == "apply_async" - assert async_span.attributes.get("messaging.message_id") == result.task_id - assert ( - async_span.attributes.get("celery.task_name") - == "test_celery_functional.fn_task_parameters" - ) - - assert run_span.status.is_ok is True - assert run_span.name == "run/test_celery_functional.fn_task_parameters" - assert run_span.attributes.get("celery.action") == "run" - assert run_span.attributes.get("celery.state") == "SUCCESS" - assert run_span.attributes.get("messaging.message_id") == result.task_id - assert ( - run_span.attributes.get("celery.task_name") - == "test_celery_functional.fn_task_parameters" - ) - - -def test_fn_exception(celery_app, memory_exporter): - @celery_app.task - def fn_exception(): - raise Exception("Task class is failing") - - result = fn_exception.apply() - - assert result.failed() is True - assert "Task class is failing" in result.traceback - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 1 - - span = spans[0] - - assert span.status.is_ok is False - assert span.name == "run/test_celery_functional.fn_exception" - assert span.attributes.get("celery.action") == "run" - assert span.attributes.get("celery.state") == "FAILURE" - assert ( - span.attributes.get("celery.task_name") - == "test_celery_functional.fn_exception" - ) - assert span.status.status_code == StatusCode.ERROR - assert span.attributes.get("messaging.message_id") == result.task_id - assert "Task class is failing" in span.status.description - - -def test_fn_exception_expected(celery_app, memory_exporter): - @celery_app.task(throws=(MyException,)) - def fn_exception(): - raise MyException("Task class is failing") - - result = fn_exception.apply() - - assert result.failed() is True - assert "Task class is failing" in result.traceback - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 1 - - span = spans[0] - - assert span.status.is_ok is True - assert span.status.status_code == StatusCode.UNSET - assert span.name == "run/test_celery_functional.fn_exception" - assert span.attributes.get("celery.action") == "run" - assert span.attributes.get("celery.state") == "FAILURE" - assert ( - span.attributes.get("celery.task_name") - == "test_celery_functional.fn_exception" - ) - assert span.attributes.get("messaging.message_id") == result.task_id - - -def test_fn_retry_exception(celery_app, memory_exporter): - @celery_app.task - def fn_exception(): - raise Retry("Task class is being retried") - - result = fn_exception.apply() - - assert result.failed() is False - assert "Task class is being retried" in result.traceback - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 1 - - span = spans[0] - - assert span.status.is_ok is True - assert span.status.status_code == StatusCode.UNSET - assert span.name == "run/test_celery_functional.fn_exception" - assert span.attributes.get("celery.action") == "run" - assert span.attributes.get("celery.state") == "RETRY" - assert ( - span.attributes.get("celery.task_name") - == "test_celery_functional.fn_exception" - ) - assert span.attributes.get("messaging.message_id") == result.task_id - - -def test_class_task(celery_app, memory_exporter): - class BaseTask(celery_app.Task): - def run(self): - return 42 - - task = BaseTask() - # register the Task class if it's available (required in Celery 4.0+) - register_task = getattr(celery_app, "register_task", None) - if register_task is not None: - register_task(task) - - result = task.apply() - - assert result.successful() is True - assert result.result == 42 - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 1 - - span = spans[0] - - assert span.status.is_ok is True - assert span.name == "run/test_celery_functional.BaseTask" - assert ( - span.attributes.get("celery.task_name") - == "test_celery_functional.BaseTask" - ) - assert span.attributes.get("celery.action") == "run" - assert span.attributes.get("celery.state") == "SUCCESS" - assert span.attributes.get("messaging.message_id") == result.task_id - - -def test_class_task_exception(celery_app, memory_exporter): - class BaseTask(celery_app.Task): - def run(self): - raise Exception("Task class is failing") - - task = BaseTask() - # register the Task class if it's available (required in Celery 4.0+) - register_task = getattr(celery_app, "register_task", None) - if register_task is not None: - register_task(task) - - result = task.apply() - - assert result.failed() is True - assert "Task class is failing" in result.traceback - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 1 - - span = spans[0] - - assert span.status.is_ok is False - assert span.name == "run/test_celery_functional.BaseTask" - assert ( - span.attributes.get("celery.task_name") - == "test_celery_functional.BaseTask" - ) - assert span.attributes.get("celery.action") == "run" - assert span.attributes.get("celery.state") == "FAILURE" - assert span.status.status_code == StatusCode.ERROR - assert span.attributes.get("messaging.message_id") == result.task_id - assert "Task class is failing" in span.status.description - - -def test_class_task_exception_excepted(celery_app, memory_exporter): - class BaseTask(celery_app.Task): - throws = (MyException,) - - def run(self): - raise MyException("Task class is failing") - - task = BaseTask() - # register the Task class if it's available (required in Celery 4.0+) - register_task = getattr(celery_app, "register_task", None) - if register_task is not None: - register_task(task) - - result = task.apply() - - assert result.failed() is True - assert "Task class is failing" in result.traceback - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 1 - - span = spans[0] - - assert span.status.is_ok is True - assert span.status.status_code == StatusCode.UNSET - assert span.name == "run/test_celery_functional.BaseTask" - assert span.attributes.get("celery.action") == "run" - assert span.attributes.get("celery.state") == "FAILURE" - assert span.attributes.get("messaging.message_id") == result.task_id - - -def test_shared_task(celery_app, memory_exporter): - """Ensure Django Shared Task are supported""" - - @celery.shared_task - def add(x, y): - return x + y - - result = add.apply([2, 2]) - assert result.result == 4 - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 1 - - span = spans[0] - - assert span.status.is_ok is True - assert span.name == "run/test_celery_functional.add" - assert ( - span.attributes.get("celery.task_name") == "test_celery_functional.add" - ) - assert span.attributes.get("celery.action") == "run" - assert span.attributes.get("celery.state") == "SUCCESS" - assert span.attributes.get("messaging.message_id") == result.task_id - - -@pytest.mark.skip(reason="inconsistent test results") -def test_apply_async_previous_style_tasks( - celery_app, celery_worker, memory_exporter -): - """Ensures apply_async is properly patched if Celery 1.0 style tasks are - used even in newer versions. This should extend support to previous versions - of Celery.""" - - class CelerySuperClass(celery.task.Task): - abstract = True - - @classmethod - def apply_async(cls, args=None, kwargs=None, **kwargs_): - return super(CelerySuperClass, cls).apply_async( - args=args, kwargs=kwargs, **kwargs_ - ) - - def run(self, *args, **kwargs): - if "stop" in kwargs: - # avoid call loop - return - CelerySubClass.apply_async(args=[], kwargs={"stop": True}).get( - timeout=ASYNC_GET_TIMEOUT - ) - - class CelerySubClass(CelerySuperClass): - pass - - celery_worker.reload() - - task = CelerySubClass() - result = task.apply() - - spans = memory_exporter.get_finished_spans() - assert len(spans) == 3 - - async_span, async_run_span, run_span = spans - - assert run_span.status.is_ok is True - assert run_span.name == "run/test_celery_functional.CelerySubClass" - assert ( - run_span.attributes.get("celery.task_name") - == "test_celery_functional.CelerySubClass" - ) - assert run_span.attributes.get("celery.action") == "run" - assert run_span.attributes.get("celery.state") == "SUCCESS" - assert run_span.attributes.get("messaging.message_id") == result.task_id - - assert async_run_span.status.is_ok is True - assert async_run_span.name == "run/test_celery_functional.CelerySubClass" - assert ( - async_run_span.attributes.get("celery.task_name") - == "test_celery_functional.CelerySubClass" - ) - assert async_run_span.attributes.get("celery.action") == "run" - assert async_run_span.attributes.get("celery.state") == "SUCCESS" - assert ( - async_run_span.attributes.get("messaging.message_id") != result.task_id - ) - - assert async_span.status.is_ok is True - assert ( - async_span.name == "apply_async/test_celery_functional.CelerySubClass" - ) - assert ( - async_span.attributes.get("celery.task_name") - == "test_celery_functional.CelerySubClass" - ) - assert async_span.attributes.get("celery.action") == "apply_async" - assert async_span.attributes.get("messaging.message_id") != result.task_id - assert async_span.attributes.get( - "messaging.message_id" - ) == async_run_span.attributes.get("messaging.message_id") - - -def test_custom_tracer_provider(celery_app, memory_exporter): - @celery_app.task - def fn_task(): - return 42 - - resource = resources.Resource.create({}) - tracer_provider = TracerProvider(resource=resource) - span_processor = export.SimpleExportSpanProcessor(memory_exporter) - tracer_provider.add_span_processor(span_processor) - - trace_api.set_tracer_provider(tracer_provider) - - CeleryInstrumentor().uninstrument() - CeleryInstrumentor().instrument(tracer_provider=tracer_provider) - - fn_task.delay() - - spans_list = memory_exporter.get_finished_spans() - assert len(spans_list) == 1 - - span = spans_list[0] - assert span.resource == resource diff --git a/tests/opentelemetry-docker-tests/tests/check_availability.py b/tests/opentelemetry-docker-tests/tests/check_availability.py deleted file mode 100644 index 30825721934..00000000000 --- a/tests/opentelemetry-docker-tests/tests/check_availability.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import logging -import os -import time - -import mysql.connector -import psycopg2 -import pymongo -import redis - -MONGODB_COLLECTION_NAME = "test" -MONGODB_DB_NAME = os.getenv("MONGODB_DB_NAME", "opentelemetry-tests") -MONGODB_HOST = os.getenv("MONGODB_HOST", "localhost") -MONGODB_PORT = int(os.getenv("MONGODB_PORT", "27017")) -MYSQL_DB_NAME = os.getenv("MYSQL_DB_NAME ", "opentelemetry-tests") -MYSQL_HOST = os.getenv("MYSQL_HOST ", "localhost") -MYSQL_PORT = int(os.getenv("MYSQL_PORT ", "3306")) -MYSQL_USER = os.getenv("MYSQL_USER ", "testuser") -MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD ", "testpassword") -POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME", "opentelemetry-tests") -POSTGRES_HOST = os.getenv("POSTGRESQL_HOST", "localhost") -POSTGRES_PASSWORD = os.getenv("POSTGRESQL_HOST", "testpassword") -POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT", "5432")) -POSTGRES_USER = os.getenv("POSTGRESQL_HOST", "testuser") -REDIS_HOST = os.getenv("REDIS_HOST", "localhost") -REDIS_PORT = int(os.getenv("REDIS_PORT ", "6379")) -RETRY_COUNT = 8 -RETRY_INTERVAL = 5 # Seconds - -logger = logging.getLogger(__name__) - - -def retryable(func): - def wrapper(): - # Try to connect to DB - for i in range(RETRY_COUNT): - try: - func() - return - except Exception as ex: # pylint: disable=broad-except - logger.error( - "waiting for %s, retry %d/%d [%s]", - func.__name__, - i + 1, - RETRY_COUNT, - ex, - ) - time.sleep(RETRY_INTERVAL) - raise Exception("waiting for {} failed".format(func.__name__)) - - return wrapper - - -@retryable -def check_pymongo_connection(): - client = pymongo.MongoClient( - MONGODB_HOST, MONGODB_PORT, serverSelectionTimeoutMS=2000 - ) - db = client[MONGODB_DB_NAME] - collection = db[MONGODB_COLLECTION_NAME] - collection.find_one() - client.close() - - -@retryable -def check_mysql_connection(): - connection = mysql.connector.connect( - user=MYSQL_USER, - password=MYSQL_PASSWORD, - host=MYSQL_HOST, - port=MYSQL_PORT, - database=MYSQL_DB_NAME, - ) - connection.close() - - -@retryable -def check_postgres_connection(): - connection = psycopg2.connect( - dbname=POSTGRES_DB_NAME, - user=POSTGRES_USER, - password=POSTGRES_PASSWORD, - host=POSTGRES_HOST, - port=POSTGRES_PORT, - ) - connection.close() - - -@retryable -def check_redis_connection(): - connection = redis.Redis(host=REDIS_HOST, port=REDIS_PORT) - connection.hgetall("*") - - -def check_docker_services_availability(): - # Check if Docker services accept connections - check_pymongo_connection() - check_mysql_connection() - check_postgres_connection() - check_redis_connection() - - -check_docker_services_availability() diff --git a/tests/opentelemetry-docker-tests/tests/docker-compose.yml b/tests/opentelemetry-docker-tests/tests/docker-compose.yml index bbb005a02ef..c731f83da1a 100644 --- a/tests/opentelemetry-docker-tests/tests/docker-compose.yml +++ b/tests/opentelemetry-docker-tests/tests/docker-compose.yml @@ -1,44 +1,6 @@ version: '3' services: - otmongo: - ports: - - "27017:27017" - image: mongo:latest - otmysql: - ports: - - "3306:3306" - image: mysql:latest - restart: always - environment: - MYSQL_USER: testuser - MYSQL_PASSWORD: testpassword - MYSQL_ALLOW_EMPTY_PASSWORD: "yes" - MYSQL_DATABASE: opentelemetry-tests - otpostgres: - image: postgres - ports: - - "5432:5432" - environment: - POSTGRES_USER: testuser - POSTGRES_PASSWORD: testpassword - POSTGRES_DB: opentelemetry-tests - otredis: - image: redis:4.0-alpine - ports: - - "127.0.0.1:6379:6379" - otjaeger: - image: jaegertracing/all-in-one:1.8 - environment: - COLLECTOR_ZIPKIN_HTTP_PORT: "9411" - ports: - - "5775:5775/udp" - - "6831:6831/udp" - - "6832:6832/udp" - - "5778:5778" - - "16686:16686" - - "14268:14268" - - "9411:9411" otopencensus: image: omnition/opencensus-collector:0.1.11 command: --logging-exporter DEBUG diff --git a/tests/opentelemetry-docker-tests/tests/mysql/test_mysql_functional.py b/tests/opentelemetry-docker-tests/tests/mysql/test_mysql_functional.py deleted file mode 100644 index fc237fe12b0..00000000000 --- a/tests/opentelemetry-docker-tests/tests/mysql/test_mysql_functional.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import mysql.connector - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.mysql import MySQLInstrumentor -from opentelemetry.test.test_base import TestBase - -MYSQL_USER = os.getenv("MYSQL_USER ", "testuser") -MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD ", "testpassword") -MYSQL_HOST = os.getenv("MYSQL_HOST ", "localhost") -MYSQL_PORT = int(os.getenv("MYSQL_PORT ", "3306")) -MYSQL_DB_NAME = os.getenv("MYSQL_DB_NAME ", "opentelemetry-tests") - - -class TestFunctionalMysql(TestBase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls._connection = None - cls._cursor = None - cls._tracer = cls.tracer_provider.get_tracer(__name__) - MySQLInstrumentor().instrument() - - @classmethod - def tearDownClass(cls): - if cls._connection: - cls._connection.close() - MySQLInstrumentor().uninstrument() - - def setUp(self): - super().setUp() - self._connection = mysql.connector.connect( - user=MYSQL_USER, - password=MYSQL_PASSWORD, - host=MYSQL_HOST, - port=MYSQL_PORT, - database=MYSQL_DB_NAME, - ) - self._cursor = self._connection.cursor() - - def validate_spans(self): - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 2) - for span in spans: - if span.name == "rootSpan": - root_span = span - else: - db_span = span - self.assertIsInstance(span.start_time, int) - self.assertIsInstance(span.end_time, int) - self.assertIsNotNone(root_span) - self.assertIsNotNone(db_span) - self.assertEqual(root_span.name, "rootSpan") - self.assertEqual(db_span.name, "mysql.opentelemetry-tests") - self.assertIsNotNone(db_span.parent) - self.assertIs(db_span.parent, root_span.get_span_context()) - self.assertIs(db_span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual(db_span.attributes["db.instance"], MYSQL_DB_NAME) - self.assertEqual(db_span.attributes["net.peer.name"], MYSQL_HOST) - self.assertEqual(db_span.attributes["net.peer.port"], MYSQL_PORT) - - def test_execute(self): - """Should create a child span for execute""" - with self._tracer.start_as_current_span("rootSpan"): - self._cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") - self.validate_spans() - - def test_execute_with_connection_context_manager(self): - """Should create a child span for execute with connection context""" - with self._tracer.start_as_current_span("rootSpan"): - with self._connection as conn: - cursor = conn.cursor() - cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") - self.validate_spans() - - def test_execute_with_cursor_context_manager(self): - """Should create a child span for execute with cursor context""" - with self._tracer.start_as_current_span("rootSpan"): - with self._connection.cursor() as cursor: - cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") - self.validate_spans() - - def test_executemany(self): - """Should create a child span for executemany""" - with self._tracer.start_as_current_span("rootSpan"): - data = (("1",), ("2",), ("3",)) - stmt = "INSERT INTO test (id) VALUES (%s)" - self._cursor.executemany(stmt, data) - self.validate_spans() - - def test_callproc(self): - """Should create a child span for callproc""" - with self._tracer.start_as_current_span("rootSpan"), self.assertRaises( - Exception - ): - self._cursor.callproc("test", ()) - self.validate_spans() diff --git a/tests/opentelemetry-docker-tests/tests/postgres/test_aiopg_functional.py b/tests/opentelemetry-docker-tests/tests/postgres/test_aiopg_functional.py deleted file mode 100644 index d76cd702ee6..00000000000 --- a/tests/opentelemetry-docker-tests/tests/postgres/test_aiopg_functional.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright 2020, 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 asyncio -import os - -import aiopg -import psycopg2 -import pytest - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.aiopg import AiopgInstrumentor -from opentelemetry.test.test_base import TestBase - -POSTGRES_HOST = os.getenv("POSTGRESQL_HOST ", "localhost") -POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT ", "5432")) -POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME ", "opentelemetry-tests") -POSTGRES_PASSWORD = os.getenv("POSTGRESQL_HOST ", "testpassword") -POSTGRES_USER = os.getenv("POSTGRESQL_HOST ", "testuser") - - -def async_call(coro): - loop = asyncio.get_event_loop() - return loop.run_until_complete(coro) - - -class TestFunctionalAiopgConnect(TestBase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls._connection = None - cls._cursor = None - cls._tracer = cls.tracer_provider.get_tracer(__name__) - AiopgInstrumentor().instrument(tracer_provider=cls.tracer_provider) - cls._connection = async_call( - aiopg.connect( - dbname=POSTGRES_DB_NAME, - user=POSTGRES_USER, - password=POSTGRES_PASSWORD, - host=POSTGRES_HOST, - port=POSTGRES_PORT, - ) - ) - cls._cursor = async_call(cls._connection.cursor()) - - @classmethod - def tearDownClass(cls): - if cls._cursor: - cls._cursor.close() - if cls._connection: - cls._connection.close() - AiopgInstrumentor().uninstrument() - - def validate_spans(self): - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 2) - for span in spans: - if span.name == "rootSpan": - root_span = span - else: - child_span = span - self.assertIsInstance(span.start_time, int) - self.assertIsInstance(span.end_time, int) - self.assertIsNotNone(root_span) - self.assertIsNotNone(child_span) - self.assertEqual(root_span.name, "rootSpan") - self.assertEqual(child_span.name, "postgresql.opentelemetry-tests") - self.assertIsNotNone(child_span.parent) - self.assertIs(child_span.parent, root_span.get_span_context()) - self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual( - child_span.attributes["db.instance"], POSTGRES_DB_NAME - ) - self.assertEqual(child_span.attributes["net.peer.name"], POSTGRES_HOST) - self.assertEqual(child_span.attributes["net.peer.port"], POSTGRES_PORT) - - def test_execute(self): - """Should create a child span for execute method""" - with self._tracer.start_as_current_span("rootSpan"): - async_call( - self._cursor.execute( - "CREATE TABLE IF NOT EXISTS test (id integer)" - ) - ) - self.validate_spans() - - def test_executemany(self): - """Should create a child span for executemany""" - with pytest.raises(psycopg2.ProgrammingError): - with self._tracer.start_as_current_span("rootSpan"): - data = (("1",), ("2",), ("3",)) - stmt = "INSERT INTO test (id) VALUES (%s)" - async_call(self._cursor.executemany(stmt, data)) - self.validate_spans() - - def test_callproc(self): - """Should create a child span for callproc""" - with self._tracer.start_as_current_span("rootSpan"), self.assertRaises( - Exception - ): - async_call(self._cursor.callproc("test", ())) - self.validate_spans() - - -class TestFunctionalAiopgCreatePool(TestBase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls._connection = None - cls._cursor = None - cls._tracer = cls.tracer_provider.get_tracer(__name__) - AiopgInstrumentor().instrument(tracer_provider=cls.tracer_provider) - cls._pool = async_call( - aiopg.create_pool( - dbname=POSTGRES_DB_NAME, - user=POSTGRES_USER, - password=POSTGRES_PASSWORD, - host=POSTGRES_HOST, - port=POSTGRES_PORT, - ) - ) - cls._connection = async_call(cls._pool.acquire()) - cls._cursor = async_call(cls._connection.cursor()) - - @classmethod - def tearDownClass(cls): - if cls._cursor: - cls._cursor.close() - if cls._connection: - cls._connection.close() - if cls._pool: - cls._pool.close() - AiopgInstrumentor().uninstrument() - - def validate_spans(self): - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 2) - for span in spans: - if span.name == "rootSpan": - root_span = span - else: - child_span = span - self.assertIsInstance(span.start_time, int) - self.assertIsInstance(span.end_time, int) - self.assertIsNotNone(root_span) - self.assertIsNotNone(child_span) - self.assertEqual(root_span.name, "rootSpan") - self.assertEqual(child_span.name, "postgresql.opentelemetry-tests") - self.assertIsNotNone(child_span.parent) - self.assertIs(child_span.parent, root_span.get_span_context()) - self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual( - child_span.attributes["db.instance"], POSTGRES_DB_NAME - ) - self.assertEqual(child_span.attributes["net.peer.name"], POSTGRES_HOST) - self.assertEqual(child_span.attributes["net.peer.port"], POSTGRES_PORT) - - def test_execute(self): - """Should create a child span for execute method""" - with self._tracer.start_as_current_span("rootSpan"): - async_call( - self._cursor.execute( - "CREATE TABLE IF NOT EXISTS test (id integer)" - ) - ) - self.validate_spans() - - def test_executemany(self): - """Should create a child span for executemany""" - with pytest.raises(psycopg2.ProgrammingError): - with self._tracer.start_as_current_span("rootSpan"): - data = (("1",), ("2",), ("3",)) - stmt = "INSERT INTO test (id) VALUES (%s)" - async_call(self._cursor.executemany(stmt, data)) - self.validate_spans() - - def test_callproc(self): - """Should create a child span for callproc""" - with self._tracer.start_as_current_span("rootSpan"), self.assertRaises( - Exception - ): - async_call(self._cursor.callproc("test", ())) - self.validate_spans() diff --git a/tests/opentelemetry-docker-tests/tests/postgres/test_psycopg_functional.py b/tests/opentelemetry-docker-tests/tests/postgres/test_psycopg_functional.py deleted file mode 100644 index 28db4c064f9..00000000000 --- a/tests/opentelemetry-docker-tests/tests/postgres/test_psycopg_functional.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright 2020, 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 psycopg2 - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor -from opentelemetry.test.test_base import TestBase - -POSTGRES_HOST = os.getenv("POSTGRESQL_HOST ", "localhost") -POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT ", "5432")) -POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME ", "opentelemetry-tests") -POSTGRES_PASSWORD = os.getenv("POSTGRESQL_HOST ", "testpassword") -POSTGRES_USER = os.getenv("POSTGRESQL_HOST ", "testuser") - - -class TestFunctionalPsycopg(TestBase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls._connection = None - cls._cursor = None - cls._tracer = cls.tracer_provider.get_tracer(__name__) - Psycopg2Instrumentor().instrument(tracer_provider=cls.tracer_provider) - cls._connection = psycopg2.connect( - dbname=POSTGRES_DB_NAME, - user=POSTGRES_USER, - password=POSTGRES_PASSWORD, - host=POSTGRES_HOST, - port=POSTGRES_PORT, - ) - cls._connection.set_session(autocommit=True) - cls._cursor = cls._connection.cursor() - - @classmethod - def tearDownClass(cls): - if cls._cursor: - cls._cursor.close() - if cls._connection: - cls._connection.close() - Psycopg2Instrumentor().uninstrument() - - def validate_spans(self): - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 2) - for span in spans: - if span.name == "rootSpan": - root_span = span - else: - child_span = span - self.assertIsInstance(span.start_time, int) - self.assertIsInstance(span.end_time, int) - self.assertIsNotNone(root_span) - self.assertIsNotNone(child_span) - self.assertEqual(root_span.name, "rootSpan") - self.assertEqual(child_span.name, "postgresql.opentelemetry-tests") - self.assertIsNotNone(child_span.parent) - self.assertIs(child_span.parent, root_span.get_span_context()) - self.assertIs(child_span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual( - child_span.attributes["db.instance"], POSTGRES_DB_NAME - ) - self.assertEqual(child_span.attributes["net.peer.name"], POSTGRES_HOST) - self.assertEqual(child_span.attributes["net.peer.port"], POSTGRES_PORT) - - def test_execute(self): - """Should create a child span for execute method""" - with self._tracer.start_as_current_span("rootSpan"): - self._cursor.execute( - "CREATE TABLE IF NOT EXISTS test (id integer)" - ) - self.validate_spans() - - def test_execute_with_connection_context_manager(self): - """Should create a child span for execute with connection context""" - with self._tracer.start_as_current_span("rootSpan"): - with self._connection as conn: - cursor = conn.cursor() - cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") - self.validate_spans() - - def test_execute_with_cursor_context_manager(self): - """Should create a child span for execute with cursor context""" - with self._tracer.start_as_current_span("rootSpan"): - with self._connection.cursor() as cursor: - cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") - self.validate_spans() - self.assertTrue(cursor.closed) - - def test_executemany(self): - """Should create a child span for executemany""" - with self._tracer.start_as_current_span("rootSpan"): - data = (("1",), ("2",), ("3",)) - stmt = "INSERT INTO test (id) VALUES (%s)" - self._cursor.executemany(stmt, data) - self.validate_spans() - - def test_callproc(self): - """Should create a child span for callproc""" - with self._tracer.start_as_current_span("rootSpan"), self.assertRaises( - Exception - ): - self._cursor.callproc("test", ()) - self.validate_spans() diff --git a/tests/opentelemetry-docker-tests/tests/pymongo/test_pymongo_functional.py b/tests/opentelemetry-docker-tests/tests/pymongo/test_pymongo_functional.py deleted file mode 100644 index 577477a2aba..00000000000 --- a/tests/opentelemetry-docker-tests/tests/pymongo/test_pymongo_functional.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -from pymongo import MongoClient - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.pymongo import PymongoInstrumentor -from opentelemetry.test.test_base import TestBase - -MONGODB_HOST = os.getenv("MONGODB_HOST ", "localhost") -MONGODB_PORT = int(os.getenv("MONGODB_PORT ", "27017")) -MONGODB_DB_NAME = os.getenv("MONGODB_DB_NAME ", "opentelemetry-tests") -MONGODB_COLLECTION_NAME = "test" - - -class TestFunctionalPymongo(TestBase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls._tracer = cls.tracer_provider.get_tracer(__name__) - PymongoInstrumentor().instrument() - client = MongoClient( - MONGODB_HOST, MONGODB_PORT, serverSelectionTimeoutMS=2000 - ) - db = client[MONGODB_DB_NAME] - cls._collection = db[MONGODB_COLLECTION_NAME] - - def validate_spans(self): - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 2) - for span in spans: - if span.name == "rootSpan": - root_span = span - else: - pymongo_span = span - self.assertIsInstance(span.start_time, int) - self.assertIsInstance(span.end_time, int) - self.assertIsNot(root_span, None) - self.assertIsNot(pymongo_span, None) - self.assertIsNotNone(pymongo_span.parent) - self.assertIs(pymongo_span.parent, root_span.get_span_context()) - self.assertIs(pymongo_span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual( - pymongo_span.attributes["db.instance"], MONGODB_DB_NAME - ) - self.assertEqual( - pymongo_span.attributes["net.peer.name"], MONGODB_HOST - ) - self.assertEqual( - pymongo_span.attributes["net.peer.port"], MONGODB_PORT - ) - - def test_insert(self): - """Should create a child span for insert""" - with self._tracer.start_as_current_span("rootSpan"): - self._collection.insert_one( - {"name": "testName", "value": "testValue"} - ) - self.validate_spans() - - def test_update(self): - """Should create a child span for update""" - with self._tracer.start_as_current_span("rootSpan"): - self._collection.update_one( - {"name": "testName"}, {"$set": {"value": "someOtherValue"}} - ) - self.validate_spans() - - def test_find(self): - """Should create a child span for find""" - with self._tracer.start_as_current_span("rootSpan"): - self._collection.find_one() - self.validate_spans() - - def test_delete(self): - """Should create a child span for delete""" - with self._tracer.start_as_current_span("rootSpan"): - self._collection.delete_one({"name": "testName"}) - self.validate_spans() - - def test_uninstrument(self): - # check that integration is working - self._collection.find_one() - spans = self.memory_exporter.get_finished_spans() - self.memory_exporter.clear() - self.assertEqual(len(spans), 1) - - # uninstrument and check not new spans are created - PymongoInstrumentor().uninstrument() - self._collection.find_one() - spans = self.memory_exporter.get_finished_spans() - self.memory_exporter.clear() - self.assertEqual(len(spans), 0) - - # re-enable and check that it works again - PymongoInstrumentor().instrument() - self._collection.find_one() - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) diff --git a/tests/opentelemetry-docker-tests/tests/pymysql/test_pymysql_functional.py b/tests/opentelemetry-docker-tests/tests/pymysql/test_pymysql_functional.py deleted file mode 100644 index 7c09025551d..00000000000 --- a/tests/opentelemetry-docker-tests/tests/pymysql/test_pymysql_functional.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -import pymysql as pymy - -from opentelemetry import trace as trace_api -from opentelemetry.instrumentation.pymysql import PyMySQLInstrumentor -from opentelemetry.test.test_base import TestBase - -MYSQL_USER = os.getenv("MYSQL_USER ", "testuser") -MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD ", "testpassword") -MYSQL_HOST = os.getenv("MYSQL_HOST ", "localhost") -MYSQL_PORT = int(os.getenv("MYSQL_PORT ", "3306")) -MYSQL_DB_NAME = os.getenv("MYSQL_DB_NAME ", "opentelemetry-tests") - - -class TestFunctionalPyMysql(TestBase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls._connection = None - cls._cursor = None - cls._tracer = cls.tracer_provider.get_tracer(__name__) - PyMySQLInstrumentor().instrument() - cls._connection = pymy.connect( - user=MYSQL_USER, - password=MYSQL_PASSWORD, - host=MYSQL_HOST, - port=MYSQL_PORT, - database=MYSQL_DB_NAME, - ) - cls._cursor = cls._connection.cursor() - - @classmethod - def tearDownClass(cls): - if cls._connection: - cls._connection.close() - PyMySQLInstrumentor().uninstrument() - - def validate_spans(self): - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 2) - for span in spans: - if span.name == "rootSpan": - root_span = span - else: - db_span = span - self.assertIsInstance(span.start_time, int) - self.assertIsInstance(span.end_time, int) - self.assertIsNotNone(root_span) - self.assertIsNotNone(db_span) - self.assertEqual(root_span.name, "rootSpan") - self.assertEqual(db_span.name, "mysql.opentelemetry-tests") - self.assertIsNotNone(db_span.parent) - self.assertIs(db_span.parent, root_span.get_span_context()) - self.assertIs(db_span.kind, trace_api.SpanKind.CLIENT) - self.assertEqual(db_span.attributes["db.instance"], MYSQL_DB_NAME) - self.assertEqual(db_span.attributes["net.peer.name"], MYSQL_HOST) - self.assertEqual(db_span.attributes["net.peer.port"], MYSQL_PORT) - - def test_execute(self): - """Should create a child span for execute""" - with self._tracer.start_as_current_span("rootSpan"): - self._cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") - self.validate_spans() - - def test_execute_with_cursor_context_manager(self): - """Should create a child span for execute with cursor context""" - with self._tracer.start_as_current_span("rootSpan"): - with self._connection.cursor() as cursor: - cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)") - self.validate_spans() - - def test_executemany(self): - """Should create a child span for executemany""" - with self._tracer.start_as_current_span("rootSpan"): - data = (("1",), ("2",), ("3",)) - stmt = "INSERT INTO test (id) VALUES (%s)" - self._cursor.executemany(stmt, data) - self.validate_spans() - - def test_callproc(self): - """Should create a child span for callproc""" - with self._tracer.start_as_current_span("rootSpan"), self.assertRaises( - Exception - ): - self._cursor.callproc("test", ()) - self.validate_spans() diff --git a/tests/opentelemetry-docker-tests/tests/redis/test_redis_functional.py b/tests/opentelemetry-docker-tests/tests/redis/test_redis_functional.py deleted file mode 100644 index 8bdc120105b..00000000000 --- a/tests/opentelemetry-docker-tests/tests/redis/test_redis_functional.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import redis - -from opentelemetry import trace -from opentelemetry.instrumentation.redis import RedisInstrumentor -from opentelemetry.test.test_base import TestBase - - -class TestRedisInstrument(TestBase): - - test_service = "redis" - - def setUp(self): - super().setUp() - self.redis_client = redis.Redis(port=6379) - self.redis_client.flushall() - RedisInstrumentor().instrument(tracer_provider=self.tracer_provider) - - def tearDown(self): - super().tearDown() - RedisInstrumentor().uninstrument() - - def _check_span(self, span): - self.assertEqual(span.attributes["service"], self.test_service) - self.assertEqual(span.name, "redis.command") - self.assertIs(span.status.status_code, trace.status.StatusCode.UNSET) - self.assertEqual(span.attributes.get("db.instance"), 0) - self.assertEqual( - span.attributes.get("db.url"), "redis://localhost:6379" - ) - - def test_long_command(self): - self.redis_client.mget(*range(1000)) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self._check_span(span) - self.assertTrue( - span.attributes.get("db.statement").startswith("MGET 0 1 2 3") - ) - self.assertTrue(span.attributes.get("db.statement").endswith("...")) - - def test_basics(self): - self.assertIsNone(self.redis_client.get("cheese")) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self._check_span(span) - self.assertEqual(span.attributes.get("db.statement"), "GET cheese") - self.assertEqual(span.attributes.get("redis.args_length"), 2) - - def test_pipeline_traced(self): - with self.redis_client.pipeline(transaction=False) as pipeline: - pipeline.set("blah", 32) - pipeline.rpush("foo", "éé") - pipeline.hgetall("xxx") - pipeline.execute() - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self._check_span(span) - self.assertEqual( - span.attributes.get("db.statement"), - "SET blah 32\nRPUSH foo éé\nHGETALL xxx", - ) - self.assertEqual(span.attributes.get("redis.pipeline_length"), 3) - - def test_pipeline_immediate(self): - with self.redis_client.pipeline() as pipeline: - pipeline.set("a", 1) - pipeline.immediate_execute_command("SET", "b", 2) - pipeline.execute() - - spans = self.memory_exporter.get_finished_spans() - # expecting two separate spans here, rather than a - # single span for the whole pipeline - self.assertEqual(len(spans), 2) - span = spans[0] - self._check_span(span) - self.assertEqual(span.attributes.get("db.statement"), "SET b 2") - - def test_parent(self): - """Ensure OpenTelemetry works with redis.""" - ot_tracer = trace.get_tracer("redis_svc") - - with ot_tracer.start_as_current_span("redis_get"): - self.assertIsNone(self.redis_client.get("cheese")) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 2) - child_span, parent_span = spans[0], spans[1] - - # confirm the parenting - self.assertIsNone(parent_span.parent) - self.assertIs(child_span.parent, parent_span.get_span_context()) - - self.assertEqual(parent_span.name, "redis_get") - self.assertEqual(parent_span.instrumentation_info.name, "redis_svc") - - self.assertEqual( - child_span.attributes.get("service"), self.test_service - ) - self.assertEqual(child_span.name, "redis.command") diff --git a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/__init__.py b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/__init__.py deleted file mode 100644 index b0a6f428417..00000000000 --- a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/mixins.py b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/mixins.py deleted file mode 100644 index 72137f83e84..00000000000 --- a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/mixins.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import contextlib - -from sqlalchemy import Column, Integer, String, create_engine -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker - -from opentelemetry import trace -from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor -from opentelemetry.instrumentation.sqlalchemy.engine import _DB, _ROWS, _STMT -from opentelemetry.test.test_base import TestBase - -Base = declarative_base() - - -def _create_engine(engine_args): - # create a SQLAlchemy engine - config = dict(engine_args) - url = config.pop("url") - return create_engine(url, **config) - - -class Player(Base): - """Player entity used to test SQLAlchemy ORM""" - - __tablename__ = "players" - - id = Column(Integer, primary_key=True) - name = Column(String(20)) - - -class SQLAlchemyTestMixin(TestBase): - __test__ = False - - """SQLAlchemy test mixin that includes a complete set of tests - that must be executed for different engine. When a new test (or - a regression test) should be added to SQLAlchemy test suite, a new - entry must be appended here so that it will be executed for all - available and supported engines. If the test is specific to only - one engine, that test must be added to the specific `TestCase` - implementation. - - To support a new engine, create a new `TestCase` that inherits from - `SQLAlchemyTestMixin` and `TestCase`. Then you must define the following - static class variables: - * VENDOR: the database vendor name - * SQL_DB: the `db.type` tag that we expect (it's the name of the database available in the `.env` file) - * SERVICE: the service that we expect by default - * ENGINE_ARGS: all arguments required to create the engine - - To check specific tags in each test, you must implement the - `check_meta(self, span)` method. - """ - - VENDOR = None - SQL_DB = None - SERVICE = None - ENGINE_ARGS = None - - @contextlib.contextmanager - def connection(self): - # context manager that provides a connection - # to the underlying database - try: - conn = self.engine.connect() - yield conn - finally: - conn.close() - - def check_meta(self, span): - """function that can be implemented according to the - specific engine implementation - """ - - def setUp(self): - super().setUp() - # create an engine with the given arguments - self.engine = _create_engine(self.ENGINE_ARGS) - - # create the database / entities and prepare a session for the test - Base.metadata.drop_all(bind=self.engine) - Base.metadata.create_all(self.engine, checkfirst=False) - self.session = sessionmaker(bind=self.engine)() - # trace the engine - SQLAlchemyInstrumentor().instrument( - engine=self.engine, tracer_provider=self.tracer_provider - ) - self.memory_exporter.clear() - - def tearDown(self): - # pylint: disable=invalid-name - # clear the database and dispose the engine - self.session.close() - Base.metadata.drop_all(bind=self.engine) - self.engine.dispose() - SQLAlchemyInstrumentor().uninstrument() - super().tearDown() - - def _check_span(self, span): - self.assertEqual(span.name, "{}.query".format(self.VENDOR)) - self.assertEqual(span.attributes.get("service"), self.SERVICE) - self.assertEqual(span.attributes.get(_DB), self.SQL_DB) - self.assertIs(span.status.status_code, trace.status.StatusCode.UNSET) - self.assertGreater((span.end_time - span.start_time), 0) - - def test_orm_insert(self): - # ensures that the ORM session is traced - wayne = Player(id=1, name="wayne") - self.session.add(wayne) - self.session.commit() - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self._check_span(span) - self.assertIn("INSERT INTO players", span.attributes.get(_STMT)) - self.assertEqual(span.attributes.get(_ROWS), 1) - self.check_meta(span) - - def test_session_query(self): - # ensures that the Session queries are traced - out = list(self.session.query(Player).filter_by(name="wayne")) - self.assertEqual(len(out), 0) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self._check_span(span) - self.assertIn( - "SELECT players.id AS players_id, players.name AS players_name \nFROM players \nWHERE players.name", - span.attributes.get(_STMT), - ) - self.check_meta(span) - - def test_engine_connect_execute(self): - # ensures that engine.connect() is properly traced - with self.connection() as conn: - rows = conn.execute("SELECT * FROM players").fetchall() - self.assertEqual(len(rows), 0) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self._check_span(span) - self.assertEqual(span.attributes.get(_STMT), "SELECT * FROM players") - self.check_meta(span) - - def test_parent(self): - """Ensure that sqlalchemy works with opentelemetry.""" - tracer = self.tracer_provider.get_tracer("sqlalch_svc") - - with tracer.start_as_current_span("sqlalch_op"): - with self.connection() as conn: - rows = conn.execute("SELECT * FROM players").fetchall() - self.assertEqual(len(rows), 0) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 2) - child_span, parent_span = spans - - # confirm the parenting - self.assertIsNone(parent_span.parent) - self.assertIs(child_span.parent, parent_span.get_span_context()) - - self.assertEqual(parent_span.name, "sqlalch_op") - self.assertEqual(parent_span.instrumentation_info.name, "sqlalch_svc") - - self.assertEqual(child_span.name, "{}.query".format(self.VENDOR)) - self.assertEqual(child_span.attributes.get("service"), self.SERVICE) diff --git a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_instrument.py b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_instrument.py deleted file mode 100644 index c408c63d943..00000000000 --- a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_instrument.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import unittest - -import sqlalchemy - -from opentelemetry import trace -from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor -from opentelemetry.test.test_base import TestBase - -POSTGRES_CONFIG = { - "host": "127.0.0.1", - "port": int(os.getenv("TEST_POSTGRES_PORT", "5432")), - "user": os.getenv("TEST_POSTGRES_USER", "testuser"), - "password": os.getenv("TEST_POSTGRES_PASSWORD", "testpassword"), - "dbname": os.getenv("TEST_POSTGRES_DB", "opentelemetry-tests"), -} - - -class SQLAlchemyInstrumentTestCase(TestBase): - """TestCase that checks if the engine is properly traced - when the `instrument()` method is used. - """ - - def setUp(self): - # create a traced engine with the given arguments - SQLAlchemyInstrumentor().instrument() - dsn = ( - "postgresql://%(user)s:%(password)s@%(host)s:%(port)s/%(dbname)s" - % POSTGRES_CONFIG - ) - self.engine = sqlalchemy.create_engine(dsn) - - # prepare a connection - self.conn = self.engine.connect() - super().setUp() - - def tearDown(self): - # clear the database and dispose the engine - self.conn.close() - self.engine.dispose() - SQLAlchemyInstrumentor().uninstrument() - - def test_engine_traced(self): - # ensures that the engine is traced - rows = self.conn.execute("SELECT 1").fetchall() - self.assertEqual(len(rows), 1) - - traces = self.memory_exporter.get_finished_spans() - # trace composition - self.assertEqual(len(traces), 1) - span = traces[0] - # check subset of span fields - self.assertEqual(span.name, "postgres.query") - self.assertEqual(span.attributes.get("service"), "postgres") - self.assertIs(span.status.status_code, trace.status.StatusCode.UNSET) - self.assertGreater((span.end_time - span.start_time), 0) diff --git a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_mysql.py b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_mysql.py deleted file mode 100644 index 310cd91f732..00000000000 --- a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_mysql.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import unittest - -import pytest -from sqlalchemy.exc import ProgrammingError - -from opentelemetry import trace -from opentelemetry.instrumentation.sqlalchemy.engine import ( - _DB, - _HOST, - _PORT, - _ROWS, - _STMT, -) - -from .mixins import SQLAlchemyTestMixin - -MYSQL_CONFIG = { - "host": "127.0.0.1", - "port": int(os.getenv("TEST_MYSQL_PORT", "3306")), - "user": os.getenv("TEST_MYSQL_USER", "testuser"), - "password": os.getenv("TEST_MYSQL_PASSWORD", "testpassword"), - "database": os.getenv("TEST_MYSQL_DATABASE", "opentelemetry-tests"), -} - - -class MysqlConnectorTestCase(SQLAlchemyTestMixin): - """TestCase for mysql-connector engine""" - - __test__ = True - - VENDOR = "mysql" - SQL_DB = "opentelemetry-tests" - SERVICE = "mysql" - ENGINE_ARGS = { - "url": "mysql+mysqlconnector://%(user)s:%(password)s@%(host)s:%(port)s/%(database)s" - % MYSQL_CONFIG - } - - def check_meta(self, span): - # check database connection tags - self.assertEqual(span.attributes.get(_HOST), MYSQL_CONFIG["host"]) - self.assertEqual(span.attributes.get(_PORT), MYSQL_CONFIG["port"]) - - def test_engine_execute_errors(self): - # ensures that SQL errors are reported - with pytest.raises(ProgrammingError): - with self.connection() as conn: - conn.execute("SELECT * FROM a_wrong_table").fetchall() - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - # span fields - self.assertEqual(span.name, "{}.query".format(self.VENDOR)) - self.assertEqual(span.attributes.get("service"), self.SERVICE) - self.assertEqual( - span.attributes.get(_STMT), "SELECT * FROM a_wrong_table" - ) - self.assertEqual(span.attributes.get(_DB), self.SQL_DB) - self.assertIsNone(span.attributes.get(_ROWS)) - self.check_meta(span) - self.assertTrue(span.end_time - span.start_time > 0) - # check the error - self.assertIs( - span.status.status_code, trace.status.StatusCode.ERROR, - ) - self.assertIn("a_wrong_table", span.status.description) diff --git a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_postgres.py b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_postgres.py deleted file mode 100644 index 91fb123c97a..00000000000 --- a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_postgres.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import unittest - -import psycopg2 -import pytest -from sqlalchemy.exc import ProgrammingError - -from opentelemetry import trace -from opentelemetry.instrumentation.sqlalchemy.engine import ( - _DB, - _HOST, - _PORT, - _ROWS, - _STMT, -) - -from .mixins import SQLAlchemyTestMixin - -POSTGRES_CONFIG = { - "host": "127.0.0.1", - "port": int(os.getenv("TEST_POSTGRES_PORT", "5432")), - "user": os.getenv("TEST_POSTGRES_USER", "testuser"), - "password": os.getenv("TEST_POSTGRES_PASSWORD", "testpassword"), - "dbname": os.getenv("TEST_POSTGRES_DB", "opentelemetry-tests"), -} - - -class PostgresTestCase(SQLAlchemyTestMixin): - """TestCase for Postgres Engine""" - - __test__ = True - - VENDOR = "postgres" - SQL_DB = "opentelemetry-tests" - SERVICE = "postgres" - ENGINE_ARGS = { - "url": "postgresql://%(user)s:%(password)s@%(host)s:%(port)s/%(dbname)s" - % POSTGRES_CONFIG - } - - def check_meta(self, span): - # check database connection tags - self.assertEqual(span.attributes.get(_HOST), POSTGRES_CONFIG["host"]) - self.assertEqual(span.attributes.get(_PORT), POSTGRES_CONFIG["port"]) - - def test_engine_execute_errors(self): - # ensures that SQL errors are reported - with pytest.raises(ProgrammingError): - with self.connection() as conn: - conn.execute("SELECT * FROM a_wrong_table").fetchall() - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - # span fields - self.assertEqual(span.name, "{}.query".format(self.VENDOR)) - self.assertEqual(span.attributes.get("service"), self.SERVICE) - self.assertEqual( - span.attributes.get(_STMT), "SELECT * FROM a_wrong_table" - ) - self.assertEqual(span.attributes.get(_DB), self.SQL_DB) - self.assertIsNone(span.attributes.get(_ROWS)) - self.check_meta(span) - self.assertTrue(span.end_time - span.start_time > 0) - # check the error - self.assertIs( - span.status.status_code, trace.status.StatusCode.ERROR, - ) - self.assertIn("a_wrong_table", span.status.description) - - -class PostgresCreatorTestCase(PostgresTestCase): - """TestCase for Postgres Engine that includes the same tests set - of `PostgresTestCase`, but it uses a specific `creator` function. - """ - - VENDOR = "postgres" - SQL_DB = "opentelemetry-tests" - SERVICE = "postgres" - ENGINE_ARGS = { - "url": "postgresql://", - "creator": lambda: psycopg2.connect(**POSTGRES_CONFIG), - } diff --git a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_sqlite.py b/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_sqlite.py deleted file mode 100644 index 309dd73ccc7..00000000000 --- a/tests/opentelemetry-docker-tests/tests/sqlalchemy_tests/test_sqlite.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest - -import pytest -from sqlalchemy.exc import OperationalError - -from opentelemetry import trace -from opentelemetry.instrumentation.sqlalchemy.engine import _DB, _ROWS, _STMT - -from .mixins import SQLAlchemyTestMixin - - -class SQLiteTestCase(SQLAlchemyTestMixin): - """TestCase for the SQLite engine""" - - __test__ = True - - VENDOR = "sqlite" - SQL_DB = ":memory:" - SERVICE = "sqlite" - ENGINE_ARGS = {"url": "sqlite:///:memory:"} - - def test_engine_execute_errors(self): - # ensures that SQL errors are reported - with pytest.raises(OperationalError): - with self.connection() as conn: - conn.execute("SELECT * FROM a_wrong_table").fetchall() - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - # span fields - self.assertEqual(span.name, "{}.query".format(self.VENDOR)) - self.assertEqual(span.attributes.get("service"), self.SERVICE) - self.assertEqual( - span.attributes.get(_STMT), "SELECT * FROM a_wrong_table" - ) - self.assertEqual(span.attributes.get(_DB), self.SQL_DB) - self.assertIsNone(span.attributes.get(_ROWS)) - self.assertTrue((span.end_time - span.start_time) > 0) - # check the error - self.assertIs( - span.status.status_code, trace.status.StatusCode.ERROR, - ) - self.assertEqual( - span.status.description, "no such table: a_wrong_table" - ) diff --git a/tox.ini b/tox.ini index 806656ced2c..7af7f7ad296 100644 --- a/tox.ini +++ b/tox.ini @@ -28,71 +28,10 @@ envlist = py3{5,6,7,8}-test-instrumentation-example-app pypy3-test-instrumentation-example-app - ; opentelemetry-instrumentation-aiohttp-client - py3{5,6,7,8}-test-instrumentation-aiohttp-client - pypy3-test-instrumentation-aiohttp-client - - ; opentelemetry-instrumentation-aiopg - py3{5,6,7,8}-test-instrumentation-aiopg - ; instrumentation-aiopg intentionally excluded from pypy3 - - ; opentelemetry-instrumentation-botocore - py3{6,7,8}-test-instrumentation-botocore - pypy3-test-instrumentation-botocore - - ; opentelemetry-instrumentation-django - py3{5,6,7,8}-test-instrumentation-django - pypy3-test-instrumentation-django - - ; opentelemetry-instrumentation-dbapi - py3{5,6,7,8}-test-instrumentation-dbapi - pypy3-test-instrumentation-dbapi - - ; opentelemetry-instrumentation-boto - py3{5,6,7,8}-test-instrumentation-boto - pypy3-test-instrumentation-boto - - ; opentelemetry-instrumentation-elasticsearch - py3{5,6,7,8}-test-instrumentation-elasticsearch{2,5,6,7} - pypy3-test-instrumentation-elasticsearch{2,5,6,7} - - ; opentelemetry-instrumentation-falcon - py3{4,5,6,7,8}-test-instrumentation-falcon - pypy3-test-instrumentation-falcon - - ; opentelemetry-instrumentation-fastapi - ; fastapi only supports 3.6 and above. - py3{6,7,8}-test-instrumentation-fastapi - pypy3-test-instrumentation-fastapi - - ; opentelemetry-instrumentation-flask - py3{5,6,7,8}-test-instrumentation-flask - pypy3-test-instrumentation-flask - - ; opentelemetry-instrumentation-requests - py3{5,6,7,8}-test-instrumentation-requests - pypy3-test-instrumentation-requests - - ; opentelemetry-instrumentation-starlette. - ; starlette only supports 3.6 and above. - py3{6,7,8}-test-instrumentation-starlette - pypy3-test-instrumentation-starlette - - ; opentelemetry-instrumentation-jinja2 - py3{5,6,7,8}-test-instrumentation-jinja2 - pypy3-test-instrumentation-jinja2 - ; opentelemetry-exporter-jaeger py3{5,6,7,8}-test-exporter-jaeger pypy3-test-exporter-jaeger - ; opentelemetry-exporter-datadog - py3{5,6,7,8}-test-exporter-datadog - - ; opentelemetry-instrumentation-mysql - py3{5,6,7,8}-test-instrumentation-mysql - pypy3-test-instrumentation-mysql - ; opentelemetry-exporter-opencensus py3{5,6,7,8}-test-exporter-opencensus ; exporter-opencensus intentionally excluded from pypy3 @@ -105,42 +44,6 @@ envlist = py3{5,6,7,8}-test-exporter-prometheus pypy3-test-exporter-prometheus - ; opentelemetry-instrumentation-psycopg2 - py3{5,6,7,8}-test-instrumentation-psycopg2 - ; ext-psycopg2 intentionally excluded from pypy3 - - ; opentelemetry-instrumentation-pymemcache - py3{5,6,7,8}-test-instrumentation-pymemcache - pypy3-test-instrumentation-pymemcache - - ; opentelemetry-instrumentation-pymongo - py3{5,6,7,8}-test-instrumentation-pymongo - pypy3-test-instrumentation-pymongo - - ; opentelemetry-instrumentation-pymysql - py3{5,6,7,8}-test-instrumentation-pymysql - pypy3-test-instrumentation-pymysql - - ; opentelemetry-instrumentation-pyramid - py3{5,6,7,8}-test-instrumentation-pyramid - pypy3-test-instrumentation-pyramid - - ; opentelemetry-instrumentation-asgi - py3{5,6,7,8}-test-instrumentation-asgi - pypy3-test-instrumentation-asgi - - ; opentelemetry-instrumentation-asyncpg - py3{5,6,7,8}-test-instrumentation-asyncpg - ; ext-asyncpg intentionally excluded from pypy3 - - ; opentelemetry-instrumentation-sqlite3 - py3{5,6,7,8}-test-instrumentation-sqlite3 - pypy3-test-instrumentation-sqlite3 - - ; opentelemetry-instrumentation-wsgi - py3{5,6,7,8}-test-instrumentation-wsgi - pypy3-test-instrumentation-wsgi - ; opentelemetry-exporter-zipkin py3{5,6,7,8}-test-exporter-zipkin pypy3-test-exporter-zipkin @@ -149,31 +52,6 @@ envlist = py3{5,6,7,8}-test-core-opentracing-shim pypy3-test-core-opentracing-shim - ; opentelemetry-instrumentation-grpc - py3{5,6,7,8}-test-instrumentation-grpc - - ; opentelemetry-instrumentation-sqlalchemy - py3{5,6,7,8}-test-instrumentation-sqlalchemy - pypy3-test-instrumentation-sqlalchemy - - ; opentelemetry-instrumentation-redis - py3{5,6,7,8}-test-instrumentation-redis - pypy3-test-instrumentation-redis - - ; opentelemetry-instrumentation-celery - py3{5,6,7,8}-test-instrumentation-celery - pypy3-test-instrumentation-celery - - ; opentelemetry-instrumentation-system-metrics - py3{5,6,7,8}-test-instrumentation-system-metrics - ; instrumentation-system-metrics intentionally excluded from pypy3 - ; known limitation: gc.get_count won't work under pypy - - ; opentelemetry-instrumentation-tornado - ; instrumentation supports >=6 on Py 3.5 and above. - py3{5,6,7,8}-test-instrumentation-tornado - pypy3-test-instrumentation-tornado - lint py38-tracecontext py38-{mypy,mypyinstalled} @@ -187,14 +65,6 @@ deps = coverage: pytest coverage: pytest-cov mypy,mypyinstalled: mypy - elasticsearch2: elasticsearch-dsl>=2.0,<3.0 - elasticsearch2: elasticsearch>=2.0,<3.0 - elasticsearch5: elasticsearch-dsl>=5.0,<6.0 - elasticsearch5: elasticsearch>=5.0,<6.0 - elasticsearch6: elasticsearch-dsl>=6.0,<7.0 - elasticsearch6: elasticsearch>=6.0,<7.0 - elasticsearch7: elasticsearch-dsl>=7.0,<8.0 - elasticsearch7: elasticsearch>=7.0,<8.0 setenv = mypy: MYPYPATH={toxinidir}/opentelemetry-api/src/ @@ -204,41 +74,8 @@ changedir = test-core-proto: opentelemetry-proto/tests test-core-instrumentation: opentelemetry-instrumentation/tests test-core-getting-started: docs/getting_started/tests - test-core-opentracing-shim: instrumentation/opentelemetry-instrumentation-opentracing-shim/tests - - test-instrumentation-aiohttp-client: instrumentation/opentelemetry-instrumentation-aiohttp-client/tests - test-instrumentation-aiopg: instrumentation/opentelemetry-instrumentation-aiopg/tests - test-instrumentation-asgi: instrumentation/opentelemetry-instrumentation-asgi/tests - test-instrumentation-asyncpg: instrumentation/opentelemetry-instrumentation-asyncpg/tests - test-instrumentation-boto: instrumentation/opentelemetry-instrumentation-boto/tests - test-instrumentation-botocore: instrumentation/opentelemetry-instrumentation-botocore/tests - test-instrumentation-celery: instrumentation/opentelemetry-instrumentation-celery/tests - test-instrumentation-dbapi: instrumentation/opentelemetry-instrumentation-dbapi/tests - test-instrumentation-django: instrumentation/opentelemetry-instrumentation-django/tests - test-instrumentation-example-app: docs/examples/opentelemetry-example-app/tests - test-instrumentation-elasticsearch{2,5,6,7}: instrumentation/opentelemetry-instrumentation-elasticsearch/tests - test-instrumentation-falcon: instrumentation/opentelemetry-instrumentation-falcon/tests - test-instrumentation-fastapi: instrumentation/opentelemetry-instrumentation-fastapi/tests - test-instrumentation-flask: instrumentation/opentelemetry-instrumentation-flask/tests - test-instrumentation-grpc: instrumentation/opentelemetry-instrumentation-grpc/tests - test-instrumentation-jinja2: instrumentation/opentelemetry-instrumentation-jinja2/tests - test-instrumentation-mysql: instrumentation/opentelemetry-instrumentation-mysql/tests - test-instrumentation-psycopg2: instrumentation/opentelemetry-instrumentation-psycopg2/tests - test-instrumentation-pymemcache: instrumentation/opentelemetry-instrumentation-pymemcache/tests - test-instrumentation-pymongo: instrumentation/opentelemetry-instrumentation-pymongo/tests - test-instrumentation-pymysql: instrumentation/opentelemetry-instrumentation-pymysql/tests - test-instrumentation-pyramid: instrumentation/opentelemetry-instrumentation-pyramid/tests - test-instrumentation-redis: instrumentation/opentelemetry-instrumentation-redis/tests - test-instrumentation-requests: instrumentation/opentelemetry-instrumentation-requests/tests - test-instrumentation-sqlalchemy: instrumentation/opentelemetry-instrumentation-sqlalchemy/tests - test-instrumentation-sqlite3: instrumentation/opentelemetry-instrumentation-sqlite3/tests - test-instrumentation-starlette: instrumentation/opentelemetry-instrumentation-starlette/tests - test-instrumentation-system-metrics: instrumentation/opentelemetry-instrumentation-system-metrics/tests - test-instrumentation-tornado: instrumentation/opentelemetry-instrumentation-tornado/tests - test-instrumentation-wsgi: instrumentation/opentelemetry-instrumentation-wsgi/tests test-exporter-jaeger: exporter/opentelemetry-exporter-jaeger/tests - test-exporter-datadog: exporter/opentelemetry-exporter-datadog/tests test-exporter-opencensus: exporter/opentelemetry-exporter-opencensus/tests test-exporter-otlp: exporter/opentelemetry-exporter-otlp/tests test-exporter-prometheus: exporter/opentelemetry-exporter-prometheus/tests @@ -254,35 +91,9 @@ commands_pre = test-core-proto: pip install {toxinidir}/opentelemetry-proto instrumentation: pip install {toxinidir}/opentelemetry-instrumentation - example-app: pip install {toxinidir}/opentelemetry-instrumentation {toxinidir}/instrumentation/opentelemetry-instrumentation-requests {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi {toxinidir}/instrumentation/opentelemetry-instrumentation-flask {toxinidir}/docs/examples/opentelemetry-example-app - - getting-started: pip install -e {toxinidir}/opentelemetry-instrumentation -e {toxinidir}/instrumentation/opentelemetry-instrumentation-requests -e {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi -e {toxinidir}/instrumentation/opentelemetry-instrumentation-flask - - celery: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-celery[test] + example-app: pip install {toxinidir}/opentelemetry-instrumentation {toxinidir}/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-requests {toxinidir}/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-wsgi {toxinidir}/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-flask {toxinidir}/docs/examples/opentelemetry-example-app - grpc: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc[test] - - wsgi,falcon,flask,django,pyramid: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi - asgi,starlette,fastapi: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi - - asyncpg: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg - - boto: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore[test] - boto: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-boto[test] - - falcon: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-falcon[test] - - flask: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-flask[test] - - botocore: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-botocore[test] - - dbapi: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi[test] - - django: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-django[test] - - fastapi: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-fastapi[test] - - mysql: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql[test] + getting-started: pip install -e {toxinidir}/opentelemetry-instrumentation -e {toxinidir}/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-requests -e {toxinidir}/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-wsgi -e {toxinidir}/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-flask opencensus: pip install {toxinidir}/exporter/opentelemetry-exporter-opencensus @@ -291,47 +102,10 @@ commands_pre = prometheus: pip install {toxinidir}/exporter/opentelemetry-exporter-prometheus - pymemcache: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-pymemcache[test] - - pymongo: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo[test] - - psycopg2: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2[test] - - pymysql: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi {toxinidir}/instrumentation/opentelemetry-instrumentation-pymysql[test] - - pyramid: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-pyramid[test] - - sqlite3: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlite3[test] - - redis: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-redis[test] - - requests: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-requests[test] - - starlette: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-starlette[test] - - tornado: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-tornado - - jinja2: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-jinja2[test] - - aiohttp-client: pip install {toxinidir}/opentelemetry-sdk {toxinidir}/instrumentation/opentelemetry-instrumentation-aiohttp-client - - aiopg: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg[test] - jaeger: pip install {toxinidir}/exporter/opentelemetry-exporter-jaeger - opentracing-shim: pip install {toxinidir}/opentelemetry-sdk - opentracing-shim: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-opentracing-shim - - datadog: pip install {toxinidir}/opentelemetry-sdk {toxinidir}/exporter/opentelemetry-exporter-datadog - zipkin: pip install {toxinidir}/exporter/opentelemetry-exporter-zipkin - sqlalchemy: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy - - system-metrics: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics[test] - - elasticsearch{2,5,6,7}: pip install {toxinidir}/opentelemetry-instrumentation {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch[test] - ; In order to get a healthy coverage report, ; we have to install packages in editable mode. coverage: python {toxinidir}/scripts/eachdist.py install --editable @@ -398,9 +172,8 @@ commands_pre = pip install -e {toxinidir}/opentelemetry-api \ -e {toxinidir}/opentelemetry-sdk \ -e {toxinidir}/opentelemetry-instrumentation \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-requests \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-flask + -e {toxinidir}/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-requests \ + -e {toxinidir}/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-wsgi commands = {toxinidir}/scripts/tracecontext-integration-test.sh @@ -408,41 +181,12 @@ commands = [testenv:docker-tests] deps = pytest - asyncpg==0.20.1 - docker-compose >= 1.25.2 - mysql-connector-python ~= 8.0 - pymongo ~= 3.1 - pymysql ~= 0.9.3 - psycopg2-binary ~= 2.8.4 - aiopg >= 0.13.0 - sqlalchemy ~= 1.3.16 - redis ~= 3.3.11 - celery ~= 4.0, != 4.4.4 changedir = tests/opentelemetry-docker-tests/tests commands_pre = - pip install -e {toxinidir}/opentelemetry-api \ - -e {toxinidir}/opentelemetry-sdk \ - -e {toxinidir}/opentelemetry-instrumentation \ - -e {toxinidir}/tests/util \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-celery \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2 \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-pymongo \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-pymysql \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-redis \ - -e {toxinidir}/instrumentation/opentelemetry-instrumentation-system-metrics \ - -e {toxinidir}/exporter/opentelemetry-exporter-opencensus - docker-compose up -d - python check_availability.py + pip install -e {toxinidir}/exporter/opentelemetry-exporter-opencensus commands = pytest {posargs} -commands_post = - docker-compose down -v