Skip to content

Commit b45a7c8

Browse files
committed
Tweak some formatting and merge issues
1 parent b01ac4b commit b45a7c8

File tree

9 files changed

+154
-245
lines changed

9 files changed

+154
-245
lines changed

newrelic/api/opentelemetry.py

Lines changed: 52 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,24 @@
1313
# limitations under the License.
1414

1515
import sys
16+
import logging
1617
from contextlib import contextmanager
1718

1819
from opentelemetry import trace as otel_api_trace
20+
1921
from newrelic.api.application import application_instance, register_application
20-
from newrelic.api.web_transaction import WebTransaction
2122
from newrelic.api.background_task import BackgroundTask
22-
from newrelic.api.message_transaction import MessageTransaction
23-
from newrelic.api.function_trace import FunctionTrace
2423
from newrelic.api.datastore_trace import DatastoreTrace
25-
from newrelic.api.message_trace import MessageTrace
2624
from newrelic.api.external_trace import ExternalTrace
25+
from newrelic.api.function_trace import FunctionTrace
26+
from newrelic.api.message_trace import MessageTrace
27+
from newrelic.api.message_transaction import MessageTransaction
2728
from newrelic.api.time_trace import current_trace, notice_error
28-
from newrelic.api.transaction import (
29-
current_transaction,
30-
record_custom_metric,
31-
record_dimensional_metric,
32-
Sentinel,
33-
)
29+
from newrelic.api.transaction import Sentinel, current_transaction
30+
from newrelic.api.web_transaction import WebTransaction
31+
from newrelic.core.otlp_utils import create_resource
32+
33+
_logger = logging.getLogger(__name__)
3434

3535
# Attributes that help distinguish span types are
3636
# sometimes added after span creation and sometimes
@@ -64,15 +64,16 @@
6464
# Custom OTel Spans and Traces
6565
# ----------------------------------------------
6666

67-
# TracerProvider: we can think of this as the agent instance. Only one can exist (in both NR and Otel)
67+
# TracerProvider: we can think of this as the agent instance. Only one can exist
6868
# SpanProcessor: we can think of this as an application. In NR, we can have multiple applications
6969
# though right now, we can only do SpanProcessor and SynchronousMultiSpanProcessor
7070
# Tracer: we can think of this as the transaction.
7171
# Span: we can think of this as the trace.
72-
# Links functionality has now been enabled. Links are relationships between spans, but
73-
# lateral in hierarchy. In NR we only have parent-child relationships. We might want
74-
# to preserve this information with a custom attribute. We can also add this as a new
75-
# attribute in a trace, but it will still not be seen in the UI other than a trace attribute.
72+
# Links functionality has now been enabled but not implemented yet. Links are relationships
73+
# between spans, but lateral in hierarchy. In NR we only have parent-child relationships.
74+
# We may want to preserve this information with a custom attribute. We can also add this
75+
# as a new attribute in a trace, but it will still not be seen in the UI other than a trace
76+
# attribute.
7677

7778

7879
class Span(otel_api_trace.Span):
@@ -83,7 +84,7 @@ def __init__(
8384
resource=None,
8485
attributes=None,
8586
kind=otel_api_trace.SpanKind.INTERNAL,
86-
nr_transaction=current_transaction(), # This attribute is purely to prevent garbage collection
87+
nr_transaction=None,
8788
nr_trace_type=FunctionTrace,
8889
instrumenting_module=None,
8990
*args,
@@ -93,23 +94,17 @@ def __init__(
9394
self.otel_parent = parent
9495
self.attributes = attributes or {}
9596
self.kind = kind
96-
self.nr_transaction = nr_transaction
97+
self.nr_transaction = (
98+
nr_transaction or current_transaction()
99+
) # This attribute is purely to prevent garbage collection
97100
self.nr_trace = None
98101
self.instrumenting_module = instrumenting_module
99102

100-
# We should never reach this point. Leave this in
101-
# for debug purposes but eventually take this out.
102-
if not self.nr_transaction:
103-
raise ValueError("HOW DID WE GET HERE??")
104-
105103
self.nr_parent = None
106104
current_nr_trace = current_trace()
107105
if (
108106
not self.otel_parent
109-
or (
110-
self.otel_parent
111-
and self.otel_parent.span_id == int(current_nr_trace.guid, 16)
112-
)
107+
or (self.otel_parent and self.otel_parent.span_id == int(current_nr_trace.guid, 16))
113108
or (self.otel_parent and isinstance(current_nr_trace, Sentinel))
114109
):
115110
# Expected to come here if one of three scenarios have occured:
@@ -124,14 +119,13 @@ def __init__(
124119
else:
125120
# Not sure if there is a usecase where we could get in here
126121
# but for debug purposes, we will raise an error
122+
_logger.debug("Otel span and NR trace do not match nor correspond to a remote span")
123+
_logger.debug("otel span: %s\nnewrelic trace: %s", self.otel_parent, current_nr_trace)
127124
raise ValueError("Unexpected span parent scenario encountered")
125+
128126

129127
if nr_trace_type == FunctionTrace:
130-
trace_kwargs = {
131-
"name": self.name,
132-
"params": self.attributes,
133-
"parent": self.nr_parent,
134-
}
128+
trace_kwargs = {"name": self.name, "params": self.attributes, "parent": self.nr_parent}
135129
self.nr_trace = nr_trace_type(**trace_kwargs)
136130
elif nr_trace_type == DatastoreTrace:
137131
trace_kwargs = {
@@ -161,43 +155,31 @@ def __init__(
161155
}
162156
self.nr_trace = nr_trace_type(**trace_kwargs)
163157
else:
164-
# TODO: Still need to implement GraphQLOperationTrace
165-
# and GraphQLResolverTrace
166-
trace_kwargs = {
167-
"name": self.name,
168-
"params": self.attributes,
169-
"parent": self.nr_parent,
170-
}
158+
# TODO: Still need to implement GraphQLOperationTrace and GraphQLResolverTrace
159+
trace_kwargs = {"name": self.name, "params": self.attributes, "parent": self.nr_parent}
171160
self.nr_trace = nr_trace_type(**trace_kwargs)
172161

173162
self.nr_trace.__enter__()
174163

175164
def _is_sampled(self):
176165
# Uses NR to determine if the trace is sampled
177-
166+
178167
# transaction.sampled can be None, True, False.
179168
# If None, this has not been computed by NR which
180169
# can also mean the following:
181170
# 1. There was not a context passed in that explicitly has sampling disabled.
182171
# This flag would be found in the traceparent or traceparent and tracespan headers.
183172
# 2. Transaction was not created where DT headers are accepted during __init__
184173
# Therefore, we will treat a value of `None` as `True` for now.
185-
174+
186175
if self.otel_parent:
187176
return bool(self.otel_parent.trace_flags)
188177
else:
189-
return bool(
190-
self.nr_transaction
191-
and (
192-
self.nr_transaction.sampled
193-
or
194-
(self.nr_transaction._sampled is None)
195-
)
196-
)
178+
return bool(self.nr_transaction and (self.nr_transaction.sampled or (self.nr_transaction._sampled is None)))
197179

198180
def _is_remote(self):
199181
# Remote span denotes if propagated from a remote parent
200-
if (self.otel_parent and self.otel_parent.is_remote):
182+
if self.otel_parent and self.otel_parent.is_remote:
201183
return True
202184
return False
203185

@@ -246,28 +228,21 @@ def update_name(self, name):
246228

247229
def is_recording(self):
248230
return self._is_sampled() and not (
249-
hasattr(self, "nr_trace")
250-
and hasattr(self.nr_trace, "end_time")
251-
and self.nr_trace.end_time
231+
hasattr(self, "nr_trace") and hasattr(self.nr_trace, "end_time") and self.nr_trace.end_time
252232
)
253233

254234
def set_status(self, status, description=None):
255235
# TODO: not implemented yet
256236
pass
257237

258-
def record_exception(
259-
self, exception, attributes=None, timestamp=None, escaped=False
260-
):
238+
def record_exception(self, exception, attributes=None, timestamp=None, escaped=False):
261239
if not hasattr(self, "nr_trace"):
262240
if exception:
263241
notice_error((type(exception), exception, exception.__traceback__))
264242
else:
265243
notice_error(sys.exc_info(), attributes=attributes)
266244
else:
267-
self.nr_trace.notice_error(
268-
(type(exception), exception, exception.__traceback__),
269-
attributes=attributes,
270-
)
245+
self.nr_trace.notice_error((type(exception), exception, exception.__traceback__), attributes=attributes)
271246

272247
def end(self, end_time=None, *args, **kwargs):
273248
# We will ignore the end_time parameter and use NR's end_time
@@ -289,7 +264,7 @@ def end(self, end_time=None, *args, **kwargs):
289264
# Add attributes as Trace parameters
290265
self._set_attributes_in_nr(self.attributes)
291266

292-
# For each kind of NR Trace, we will need to add
267+
# For each kind of NR Trace, we will need to add
293268
# specific attributes since they were likely not
294269
# available at the time of the trace's creation.
295270
if self.instrumenting_module in ("Redis", "Mongodb"):
@@ -313,25 +288,16 @@ class Tracer(otel_api_trace.Tracer):
313288
def __init__(self, resource=None, instrumentation_library=None, *args, **kwargs):
314289
self.resource = resource
315290
self.instrumentation_library = instrumentation_library.split(".")[-1].capitalize()
316-
self.nr_application = application_instance(
317-
activate=False
318-
) or register_application("OtelTracer")
319-
self.global_settings = (
320-
self.nr_application and self.nr_application.global_settings
321-
)
291+
self.nr_application = application_instance(activate=False) or register_application("OtelTracer")
292+
self.global_settings = self.nr_application and self.nr_application.global_settings
322293

323-
if (
324-
self.nr_application
325-
and self.global_settings.enabled
326-
and self.nr_application.enabled
327-
):
294+
if self.nr_application and self.global_settings.enabled and self.nr_application.enabled:
328295
self._settings = self.nr_application.settings
329296
if not self._settings:
330297
self.nr_application.activate()
331298
self._settings = self.nr_application.settings
332299
else:
333-
# Unable to find or register application. We should log this.
334-
pass
300+
_logger.warning("Unable to find or register New Relic application for Otel Tracer")
335301

336302
def start_span(
337303
self,
@@ -376,33 +342,25 @@ def start_span(
376342
request_path=request_path,
377343
headers=headers,
378344
)
379-
elif kind in (
380-
otel_api_trace.SpanKind.PRODUCER,
381-
otel_api_trace.SpanKind.INTERNAL,
382-
):
345+
elif kind in (otel_api_trace.SpanKind.PRODUCER, otel_api_trace.SpanKind.INTERNAL):
383346
transaction = BackgroundTask(self.nr_application, name=name)
384347
elif kind == otel_api_trace.SpanKind.CONSUMER:
385348
# NOTE: NR uses MessageTransaction for Pika, RabbitMQ, Kafka
386349
if (
387350
self.instrumentation_library in INSTRUMENTING_MODULE_TYPE
388-
and INSTRUMENTING_MODULE_TYPE[self.instrumentation_library]
389-
== "message"
351+
and INSTRUMENTING_MODULE_TYPE[self.instrumentation_library] == "message"
390352
):
391353
transaction = MessageTransaction(
392354
library=self.instrumentation_library,
393355
destination_type="Topic",
394356
destination_name=name,
395357
application=self.nr_application,
396358
transport_type=self.instrumentation_library,
397-
headers=_headers,
359+
headers=headers,
398360
)
399361
else:
400-
transaction = BackgroundTask(
401-
self.nr_application,
402-
name=name,
403-
group="Celery",
404-
)
405-
362+
transaction = BackgroundTask(self.nr_application, name=name, group="Celery")
363+
406364
transaction.__enter__()
407365

408366
# If not parent_span_context or not parent_span_context.is_remote
@@ -432,7 +390,7 @@ def start_span(
432390
request_method=request_method,
433391
request_path=request_path,
434392
headers=headers,
435-
)
393+
)
436394
transaction.__enter__()
437395
elif kind == otel_api_trace.SpanKind.INTERNAL:
438396
if transaction:
@@ -443,12 +401,8 @@ def start_span(
443401
if transaction:
444402
if (
445403
(self.instrumentation_library in INSTRUMENTING_MODULE_TYPE)
446-
and (
447-
INSTRUMENTING_MODULE_TYPE[self.instrumentation_library]
448-
== "db"
449-
)
450-
or (attributes and ("db.system" in attributes))
451-
):
404+
and (INSTRUMENTING_MODULE_TYPE[self.instrumentation_library] == "db")
405+
) or (attributes and ("db.system" in attributes)):
452406
nr_trace_type = DatastoreTrace
453407
else:
454408
nr_trace_type = ExternalTrace
@@ -461,23 +415,18 @@ def start_span(
461415
# NOTE: NR uses MessageTransaction for Pika, RabbitMQ, Kafka
462416
if (
463417
self.instrumentation_library in INSTRUMENTING_MODULE_TYPE
464-
and INSTRUMENTING_MODULE_TYPE[self.instrumentation_library]
465-
== "message"
418+
and INSTRUMENTING_MODULE_TYPE[self.instrumentation_library] == "message"
466419
):
467420
transaction = MessageTransaction(
468421
library=self.instrumentation_library,
469422
destination_type="Topic",
470423
destination_name=name,
471424
application=self.nr_application,
472425
transport_type=self.instrumentation_library,
473-
headers=_headers,
426+
headers=headers,
474427
)
475428
else:
476-
transaction = BackgroundTask(
477-
self.nr_application,
478-
name=name,
479-
group="Celery",
480-
)
429+
transaction = BackgroundTask(self.nr_application, name=name, group="Celery")
481430
transaction.__enter__()
482431
elif kind == otel_api_trace.SpanKind.PRODUCER:
483432
if transaction:
@@ -501,7 +450,6 @@ def start_span(
501450

502451
return span
503452

504-
505453
@contextmanager
506454
def start_as_current_span(
507455
self,
@@ -523,9 +471,7 @@ def start_as_current_span(
523471
set_status_on_exception=set_status_on_exception,
524472
)
525473

526-
with otel_api_trace.use_span(
527-
span, end_on_exit=end_on_exit, record_exception=record_exception
528-
) as current_span:
474+
with otel_api_trace.use_span(span, end_on_exit=end_on_exit, record_exception=record_exception) as current_span:
529475
yield current_span
530476

531477

@@ -542,8 +488,4 @@ def get_tracer(
542488
*args,
543489
**kwargs,
544490
):
545-
return Tracer(
546-
resource=self._resource, instrumentation_library=instrumenting_module_name
547-
)
548-
549-
491+
return Tracer(resource=self._resource, instrumentation_library=instrumenting_module_name)

newrelic/config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,7 @@ def _process_configuration(section):
683683
_process_setting(section, "instrumentation.middleware.django.enabled", "getboolean", None)
684684
_process_setting(section, "instrumentation.middleware.django.exclude", "get", _map_inc_excl_middleware)
685685
_process_setting(section, "instrumentation.middleware.django.include", "get", _map_inc_excl_middleware)
686+
_process_setting(section, "otel_bridge.enabled", "getboolean", None)
686687

687688

688689
# Loading of configuration from specified file and for specified
@@ -4359,6 +4360,15 @@ def _process_module_builtin_defaults():
43594360
"pyzeebe.worker.job_executor", "newrelic.hooks.external_pyzeebe", "instrument_pyzeebe_worker_job_executor"
43604361
)
43614362

4363+
# Hybrid Agent Hooks
4364+
_process_module_definition(
4365+
"opentelemetry.trace", "newrelic.hooks.hybridagent_opentelemetry", "instrument_trace_api"
4366+
)
4367+
4368+
_process_module_definition(
4369+
"opentelemetry.instrumentation.utils", "newrelic.hooks.hybridagent_opentelemetry", "instrument_utils"
4370+
)
4371+
43624372

43634373
def _process_module_entry_points():
43644374
try:

0 commit comments

Comments
 (0)