def _get_resource( service_name: Optional[str], attributes: Optional[Dict[str, Union[str, bool, int, float]]], ) -> Resource: attributes = attributes or {} if service_name: attributes[_SERVICE_NAME_ATTR] = service_name attributes.update({ _TELEMETRY_VERSION_ATTR: auto_instrumentation_version, _SPLUNK_DISTRO_VERSION_ATTR: __version__, }) resource = Resource.create(attributes) if (resource.attributes.get( _SERVICE_NAME_ATTR, _DEFAULT_OTEL_SERVICE_NAME) == _DEFAULT_OTEL_SERVICE_NAME): logger.warning(_NO_SERVICE_NAME_WARNING) resource = resource.merge( Resource({_SERVICE_NAME_ATTR: _DEFAULT_SERVICE_NAME})) return resource
def _create_app(self): # instrumentation is handled by the instrument call resource = Resource.create({"key1": "value1", "key2": "value2"}) result = self.create_tracer_provider(resource=resource) tracer_provider, exporter = result self.memory_exporter = exporter self._instrumentor.instrument(tracer_provider=tracer_provider) return self._create_starlette_app()
def test_basic_wsgi_call(self): resource = Resource.create({"service-key": "service-value"}) result = TestBase.create_tracer_provider(resource=resource) tracer_provider, exporter = result app = otel_wsgi.OpenTelemetryMiddleware( simple_wsgi, tracer_provider=tracer_provider ) response = app(self.environ, self.start_response) self.validate_response(response, exporter)
def test_unique_identifier(self): client = mock.Mock() exporter1 = CloudMonitoringMetricsExporter( project_id=self.project_id, client=client, add_unique_identifier=True, ) exporter2 = CloudMonitoringMetricsExporter( project_id=self.project_id, client=client, add_unique_identifier=True, ) exporter1.project_name = self.project_name exporter2.project_name = self.project_name client.create_metric_descriptor.return_value = MetricDescriptor( **{ "name": None, "type": "custom.googleapis.com/OpenTelemetry/name", "display_name": "name", "description": "description", "labels": [ LabelDescriptor(key=UNIQUE_IDENTIFIER_KEY, value_type="STRING"), ], "metric_kind": "CUMULATIVE", "value_type": "DOUBLE", }) sum_agg_one = SumAggregator() sum_agg_one.update(1) metric_record = ExportRecord(MockMetric(), (), sum_agg_one, Resource.create_empty()) exporter1.export([metric_record]) exporter2.export([metric_record]) ( first_call, second_call, ) = client.create_metric_descriptor.call_args_list self.assertEqual(first_call[0][1].labels[0].key, UNIQUE_IDENTIFIER_KEY) self.assertEqual(second_call[0][1].labels[0].key, UNIQUE_IDENTIFIER_KEY) first_call, second_call = client.create_time_series.call_args_list self.assertNotEqual( first_call[0][1][0].metric.labels[UNIQUE_IDENTIFIER_KEY], second_call[0][1][0].metric.labels[UNIQUE_IDENTIFIER_KEY], )
def configure_tracing(configuration: dict): # OTLP Exporter configuration if configuration['exporter'] == 'otlp': service_name = {'service.name': configuration['service_name']} resource = Resource(service_name) trace.set_tracer_provider(TracerProvider(resource=resource)) exporter = OTLPSpanExporter( endpoint=configuration['exporter_endpoint'], insecure=configuration['exporter_insecure']) trace.get_tracer(__name__) span_processor = BatchExportSpanProcessor(exporter) trace.get_tracer_provider().add_span_processor(span_processor) # Jaeger HTTP Exporter configuration elif configuration['exporter'] == 'jaeger_http': exporter = JaegerSpanExporter( service_name=configuration['service_name'], collector_endpoint=configuration['exporter_endpoint'], ) trace.set_tracer_provider(TracerProvider()) trace.get_tracer(__name__) span_processor = BatchExportSpanProcessor(exporter) trace.get_tracer_provider().add_span_processor(span_processor) # Jaeger Thrifth Compact Exporter configuration elif configuration['exporter'] == 'jaeger_thrift': exporter = JaegerSpanExporter( service_name=configuration['service_name'], agent_host_name=configuration['exporter_host'], agent_port=configuration['exporter_port'], ) trace.set_tracer_provider(TracerProvider()) trace.get_tracer(__name__) span_processor = BatchExportSpanProcessor(exporter) trace.get_tracer_provider().add_span_processor(span_processor) # Zipkin Exporter configuration elif configuration['exporter'] == 'zipkin': exporter = ZipkinSpanExporter( service_name=configuration['service_name'], url=configuration['exporter_endpoint']) trace.set_tracer_provider(TracerProvider()) trace.get_tracer(__name__) span_processor = BatchExportSpanProcessor(exporter) trace.get_tracer_provider().add_span_processor(span_processor) # Console Exporter configuration elif configuration['exporter'] == 'console': trace.set_tracer_provider(TracerProvider()) trace.get_tracer_provider().add_span_processor( SimpleExportSpanProcessor(ConsoleSpanExporter())) else: raise ValueError( 'Only Otlp, Jaeger Thrift/HTTP and Zipkin exporters are supported. ' 'Please check your configuration.')
def test_extract_unsupported_gcp_resources(self): resource = Resource( labels={ "cloud.account.id": "123", "host.id": "host", "extra_info": "extra", "not_gcp_resource": "value", "gcp.resource_type": "unsupported_gcp_resource", "cloud.provider": "gcp", }) self.assertEqual(_extract_resources(resource), {})
def post_fork(server, worker): server.log.info("Worker spawned (pid: %s)", worker.pid) resource = Resource.create(attributes={"service.name": "api-service"}) trace.set_tracer_provider(TracerProvider(resource=resource)) # This uses insecure connection for the purpose of example. Please see the # OTLP Exporter documentation for other options. span_processor = BatchSpanProcessor( OTLPSpanExporter(endpoint="localhost:4317", insecure=True)) trace.get_tracer_provider().add_span_processor(span_processor)
def test_extract_malformed_resources(self): # This resource doesn't have all the fields required for a gce_instance # Specifically its missing "host.id", "cloud.zone", "cloud.account.id" resource = Resource( labels={ "gcp.resource_type": "gce_instance", "cloud.provider": "gcp", } ) # Should throw when passed a malformed GCP resource dict self.assertRaises(KeyError, _extract_resources, resource)
def __init__( self, name: str, context: trace_api.SpanContext, parent: Optional[trace_api.SpanContext] = None, sampler: Optional[sampling.Sampler] = None, trace_config: None = None, # TODO resource: Resource = Resource.create({}), attributes: types.Attributes = None, events: Sequence[Event] = None, links: Sequence[trace_api.Link] = (), kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, span_processor: SpanProcessor = SpanProcessor(), instrumentation_info: InstrumentationInfo = None, record_exception: bool = True, set_status_on_exception: bool = True, ) -> None: super().__init__( name=name, context=context, parent=parent, kind=kind, resource=resource, instrumentation_info=instrumentation_info, ) self._sampler = sampler self._trace_config = trace_config self._record_exception = record_exception self._set_status_on_exception = set_status_on_exception self._span_processor = span_processor self._lock = threading.Lock() _filter_attribute_values(attributes) if not attributes: self._attributes = self._new_attributes() else: self._attributes = BoundedDict.from_map( SPAN_ATTRIBUTE_COUNT_LIMIT, attributes ) self._events = self._new_events() if events: for event in events: _filter_attribute_values(event.attributes) # pylint: disable=protected-access event._attributes = _create_immutable_attributes( event.attributes ) self._events.append(event) if links is None: self._links = self._new_links() else: self._links = BoundedList.from_seq(SPAN_LINK_COUNT_LIMIT, links)
def __init__( self, name: str, context: trace_api.SpanContext, parent: Optional[trace_api.SpanContext] = None, sampler: Optional[sampling.Sampler] = None, trace_config: None = None, # TODO resource: Resource = Resource.create({}), attributes: types.Attributes = None, # TODO events: Sequence[Event] = None, # TODO links: Sequence[trace_api.Link] = (), kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, span_processor: SpanProcessor = SpanProcessor(), instrumentation_info: InstrumentationInfo = None, set_status_on_exception: bool = True, ) -> None: self.name = name self.context = context self.parent = parent self.sampler = sampler self.trace_config = trace_config self.resource = resource self.kind = kind self._set_status_on_exception = set_status_on_exception self.span_processor = span_processor self.status = None self._lock = threading.Lock() _filter_attribute_values(attributes) if not attributes: self.attributes = self._new_attributes() else: self.attributes = BoundedDict.from_map(MAX_NUM_ATTRIBUTES, attributes) self.events = self._new_events() if events: for event in events: _filter_attribute_values(event.attributes) # pylint: disable=protected-access event._attributes = _create_immutable_attributes( event.attributes) self.events.append(event) if links is None: self.links = self._new_links() else: self.links = BoundedList.from_seq(MAX_NUM_LINKS, links) self._end_time = None # type: Optional[int] self._start_time = None # type: Optional[int] self.instrumentation_info = instrumentation_info
def test_extract_unsupported_provider_resources(self): # Resources with currently unsupported providers will be ignored resource = Resource( attributes={ "cloud.account.id": "123", "host.id": "host", "extra_info": "extra", "not_gcp_resource": "value", "cloud.provider": "aws", }) self.assertEqual(_extract_resources(resource), {})
def detect(self) -> "Resource": lambda_name = environ.get("AWS_LAMBDA_FUNCTION_NAME") aws_region = environ.get("AWS_REGION") function_version = environ.get("AWS_LAMBDA_FUNCTION_VERSION") env_resource_map = { "cloud.region": aws_region, "cloud.provider": "aws", "faas.name": lambda_name, "faas.version": function_version } return Resource(env_resource_map)
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")
def test_valid_convert_to_timeseries(self): test_records = [ ExportRecord( Counter("testname", "testdesc", "testunit", int, None), None, SumAggregator(), Resource({}), ), ExportRecord( Counter("testname", "testdesc", "testunit", int, None), None, MinMaxSumCountAggregator(), Resource({}), ), ExportRecord( Counter("testname", "testdesc", "testunit", int, None), None, HistogramAggregator(), Resource({}), ), ExportRecord( Counter("testname", "testdesc", "testunit", int, None), None, LastValueAggregator(), Resource({}), ), ExportRecord( Counter("testname", "testdesc", "testunit", int, None), None, ValueObserverAggregator(), Resource({}), ), ] for record in test_records: record.aggregator.update(5) record.aggregator.take_checkpoint() data = self.exporter._convert_to_timeseries(test_records) self.assertIsInstance(data, list) self.assertEqual(len(data), 13) for timeseries in data: self.assertIsInstance(timeseries, TimeSeries)
def __init__( self, instrumentation_info: "InstrumentationInfo", stateful: bool, resource: Resource = Resource.create_empty(), ): self.instrumentation_info = instrumentation_info self.metrics = set() self.observers = set() self.batcher = UngroupedBatcher(stateful) self.observers_lock = threading.Lock() self.resource = resource
def test_export_span_service_name(self): resource = Resource.create({SERVICE_NAME: "test"}) span = trace._Span("test_span", context=self.context, resource=resource) span.start() span.end() client_mock = mock.Mock() exporter = JaegerExporter() exporter._grpc_client = client_mock exporter.export([span]) self.assertEqual(exporter.service_name, "test")
def test_max_tag_value_length(self): span = trace._Span( name="span", resource=Resource(attributes={ "key_resource": "some_resource some_resource some_more_resource" }), context=trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x00000000DEADBEF0, is_remote=False, ), ) span.start() span.set_attribute("key_bool", False) span.set_attribute("key_string", "hello_world hello_world hello_world") span.set_attribute("key_float", 111.22) span.set_attribute("key_int", 1100) span.set_attribute("key_tuple", ("tuple_element", "tuple_element2")) span.end() translate = Translate([span]) # does not truncate by default # pylint: disable=protected-access spans = translate._translate(pb_translator.ProtobufTranslator("svc")) tags_by_keys = { tag.key: tag.v_str for tag in spans[0].tags if tag.v_type == model_pb2.ValueType.STRING } self.assertEqual("hello_world hello_world hello_world", tags_by_keys["key_string"]) self.assertEqual("('tuple_element', 'tuple_element2')", tags_by_keys["key_tuple"]) self.assertEqual( "some_resource some_resource some_more_resource", tags_by_keys["key_resource"], ) # truncates when max_tag_value_length is passed # pylint: disable=protected-access spans = translate._translate( pb_translator.ProtobufTranslator("svc", max_tag_value_length=5)) tags_by_keys = { tag.key: tag.v_str for tag in spans[0].tags if tag.v_type == model_pb2.ValueType.STRING } self.assertEqual("hello", tags_by_keys["key_string"]) self.assertEqual("('tup", tags_by_keys["key_tuple"]) self.assertEqual("some_", tags_by_keys["key_resource"])
def setUpClass(cls): # pylint: disable=protected-access cls._resource_labels = { "key_with_str_value": "some string", "key_with_int_val": 321, "key_with_true": True, } metrics.set_meter_provider( MeterProvider(resource=Resource(cls._resource_labels))) cls._meter = metrics.get_meter(__name__) cls._labels = {"environment": "staging", "number": 321} cls._key_labels = get_dict_as_key(cls._labels)
def detect(self) -> "Resource": lambda_handler = os.environ.get("ORIG_HANDLER", os.environ.get("_HANDLER")) aws_region = os.environ["AWS_REGION"] env_resource_map = { "cloud.region": aws_region, "cloud.provider": "aws", "faas.name": lambda_handler, # faas.id is in lambda context, can to be extracted before lambda handler. # 'faas.id': self._ctx_invoked_function_arn, } return Resource(env_resource_map)
def test_valid_export(self, mock_post): mock_post.return_value.configure_mock(**{"status_code": 200}) test_metric = Counter("testname", "testdesc", "testunit", int, None) labels = get_dict_as_key({"environment": "testing"}) record = ExportRecord(test_metric, labels, SumAggregator(), Resource({})) result = self.exporter.export([record]) self.assertIs(result, MetricsExportResult.SUCCESS) self.assertEqual(mock_post.call_count, 1) result = self.exporter.export([]) self.assertIs(result, MetricsExportResult.SUCCESS)
def test_log_to_envelope_partA_default(self): exporter = self._exporter old_resource = self._log_data.log_record.resource resource = Resource({"service.name": "testServiceName"}) self._log_data.log_record.resource = resource envelope = exporter._log_to_envelope(self._log_data) self.assertEqual(envelope.tags.get("ai.cloud.role"), "testServiceName") self.assertEqual(envelope.tags.get("ai.cloud.roleInstance"), platform.node()) self.assertEqual(envelope.tags.get("ai.internal.nodeName"), envelope.tags.get("ai.cloud.roleInstance")) self._log_data.log_record.resource = old_resource
def post_fork(server, worker): server.log.info("Worker spawned (pid: %s)", worker.pid) resource = Resource.create(attributes={"service.name": "job-server"}) trace.set_tracer_provider(TracerProvider(resource=resource)) span_processor = BatchSpanProcessor( OTLPSpanExporter(endpoint="https://api.honeycomb.io")) trace.get_tracer_provider().add_span_processor(span_processor) from opentelemetry.instrumentation.auto_instrumentation import ( # noqa: F401 sitecustomize, )
def setUp(self): super().setUp() resource = Resource.create({"service.name": "flask-api"}) result = self.create_tracer_provider(resource=resource) tracer_provider, exporter = result self.memory_exporter = exporter self.app = Flask(__name__) FlaskInstrumentor().instrument_app(self.app, tracer_provider=tracer_provider) self._common_initialization()
def test_extract_unsupported_gcp_resources(self): # Unsupported gcp resources will be ignored resource = Resource( attributes={ "cloud.account.id": "123", "host.id": "host", "extra_info": "extra", "not_gcp_resource": "value", "gcp.resource_type": "unsupported_gcp_resource", "cloud.provider": "gcp", }) self.assertEqual(_extract_resources(resource), {})
def __init__( self, sampler: sampling.Sampler = trace_api.sampling.ALWAYS_ON, resource: Resource = Resource.create_empty(), shutdown_on_exit: bool = True, ): self._active_span_processor = MultiSpanProcessor() self.resource = resource self.sampler = sampler self._atexit_handler = None if shutdown_on_exit: self._atexit_handler = atexit.register(self.shutdown)
def detect(self) -> "Resource": try: if not os.environ.get( "ECS_CONTAINER_METADATA_URI" ) and not os.environ.get("ECS_CONTAINER_METADATA_URI_V4"): raise RuntimeError( "Missing ECS_CONTAINER_METADATA_URI therefore process is not on ECS." ) container_id = "" try: with open( "/proc/self/cgroup", encoding="utf8" ) as container_info_file: for raw_line in container_info_file.readlines(): line = raw_line.strip() # Subsequent IDs should be the same, exit if found one if len(line) > _CONTAINER_ID_LENGTH: container_id = line[-_CONTAINER_ID_LENGTH:] break except FileNotFoundError as exception: logger.warning( "Failed to get container ID on ECS: %s.", exception ) return Resource( { ResourceAttributes.CLOUD_PROVIDER: CloudProviderValues.AWS.value, ResourceAttributes.CLOUD_PLATFORM: CloudPlatformValues.AWS_ECS.value, ResourceAttributes.CONTAINER_NAME: socket.gethostname(), ResourceAttributes.CONTAINER_ID: container_id, } ) # pylint: disable=broad-except except Exception as exception: if self.raise_on_error: raise exception logger.warning("%s failed: %s", self.__class__.__name__, exception) return Resource.get_empty()
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" }), )
def __init__( self, stateful=True, resource: Resource = Resource.create({}), shutdown_on_exit: bool = True, ): self.stateful = stateful self.resource = resource self._controllers = [] self._exporters = set() self._atexit_handler = None if shutdown_on_exit: self._atexit_handler = atexit.register(self.shutdown)
def init_otel(): global tracer, session_name, service_name, insecure LOGGER.debug('Init Otel : {}'.format(service_name)) trace.set_tracer_provider( TracerProvider(resource=Resource.create({SERVICE_NAME: service_name}), )) otel_exporter = OTLPSpanExporter(insecure=insecure) trace.get_tracer_provider().add_span_processor( BatchSpanProcessor(otel_exporter)) tracer = trace.get_tracer(session_name)
def __init__( self, resource: Resource = Resource.create(), shutdown_on_exit: bool = True, multi_log_processor: Union[SynchronousMultiLogProcessor, ConcurrentMultiLogProcessor] = None, ): self._resource = resource self._multi_log_processor = (multi_log_processor or SynchronousMultiLogProcessor()) self._at_exit_handler = None if shutdown_on_exit: self._at_exit_handler = atexit.register(self.shutdown)