Skip to content

Conversation

@lrafeei
Copy link
Contributor

@lrafeei lrafeei commented Nov 19, 2025

This PR contains a barebones tracing implementation.

Implementation for different transaction types, Distributed Tracing/context propagation, tracer configuration, instrumentation scope attributes, and meters will come in subsequent PRs

@mergify mergify bot added the merge-conflicts Merge conflicts detected. label Nov 19, 2025
@mergify mergify bot removed the merge-conflicts Merge conflicts detected. label Nov 19, 2025
@github-actions
Copy link

github-actions bot commented Nov 19, 2025

MegaLinter analysis: Success

Descriptor Linter Files Fixed Errors Warnings Elapsed time
✅ ACTION actionlint 7 0 0 0.96s
✅ MARKDOWN markdownlint 7 0 0 0 1.33s
✅ PYTHON ruff 955 1 0 0 1.02s
✅ PYTHON ruff-format 955 1 0 0 0.41s
✅ YAML prettier 15 0 0 0 1.54s
✅ YAML v8r 15 0 0 5.72s
✅ YAML yamllint 15 0 0 0.71s

See detailed reports in MegaLinter artifacts

MegaLinter is graciously provided by OX Security

@mergify mergify bot added the tests-failing Tests failing in CI. label Nov 19, 2025
@codecov-commenter
Copy link

codecov-commenter commented Nov 19, 2025

Codecov Report

❌ Patch coverage is 51.11940% with 131 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (develop-hybrid-core-tracing@afbc9f2). Learn more about missing BASE report.

Files with missing lines Patch % Lines
newrelic/api/opentelemetry.py 46.15% 83 Missing and 15 partials ⚠️
newrelic/hooks/hybridagent_opentelemetry.py 56.57% 28 Missing and 5 partials ⚠️
Additional details and impacted files
@@                      Coverage Diff                       @@
##             develop-hybrid-core-tracing    #1587   +/-   ##
==============================================================
  Coverage                               ?   79.94%           
==============================================================
  Files                                  ?      210           
  Lines                                  ?    24488           
  Branches                               ?     3886           
==============================================================
  Hits                                   ?    19578           
  Misses                                 ?     3548           
  Partials                               ?     1362           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lrafeei lrafeei force-pushed the hybrid-agent-trace-base branch from 13df939 to b45a7c8 Compare November 19, 2025 21:33
@lrafeei lrafeei marked this pull request as ready for review November 19, 2025 22:04
@lrafeei lrafeei requested a review from a team as a code owner November 19, 2025 22:04
Copy link
Contributor

@hmstepanek hmstepanek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just reviewed this one class so far but wanted to get you the feedback now rather than waiting until next week.

# but for debug purposes, we will raise an error
_logger.debug("Otel span and NR trace do not match nor correspond to a remote span")
_logger.debug("otel span: %s\nnewrelic trace: %s", self.otel_parent, current_nr_trace)
raise ValueError("Unexpected span parent scenario encountered")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this error caught somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, I cannot think of a scenario where this would happen, so I wanted to keep this here for debug purposes.

_logger.debug("otel span: %s\nnewrelic trace: %s", self.otel_parent, current_nr_trace)
raise ValueError("Unexpected span parent scenario encountered")

if nr_trace_type == FunctionTrace:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a lot of questions about these. Mainly; shouldn't we be pulling all the attributes out of the OTEL attr list and mapping the here into the appropriate NR equivalents unless there isn't a spot for them in which case they are supposed to become user attributes according to the spec?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That part will come in a separate PR. For now, the attributes are added as custom attributes, but the actual attribute mapping per SpanKind will come in separate PRs. Of course, most of the time, the attributes are not added during initialization, but rather throughout the span's operation, so I will be adding logic to add these when the span is ending.

elif nr_trace_type == DatastoreTrace:
trace_kwargs = {
"product": self.instrumenting_module,
"target": None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like target maps to OTEL's db.name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does! The issue with the OTel attributes is that the db.name attribute is usually not set during the creation of the span, but rather later on in the span's operation.

def record_exception(self, exception, attributes=None, timestamp=None, escaped=False):
if not hasattr(self, "nr_trace"):
if exception:
notice_error((type(exception), exception, exception.__traceback__))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be passing attributes here too?

else:
notice_error(sys.exc_info(), attributes=attributes)
else:
self.nr_trace.notice_error((type(exception), exception, exception.__traceback__), attributes=attributes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if there's no exception here?

# TODO: not implemented yet
pass

def record_exception(self, exception, attributes=None, timestamp=None, escaped=False):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be doing something with timestamp and escaped here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not seem like we will do anything with the escaped argument, and the timestamp argument will be used for the events/log events (which I have not implemented yet)

# We will ignore the end_time parameter and use NR's end_time

# Check to see if New Relic trace ever existed or,
# if it does, that trace has already ended
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe store this value and simplify the logic rather than calling hasattr a bunch:

nr_trace = hasattr(self, "nr_trace", None)
if not nr_trace or nr_trace and getattr(nr_trace, "end_time", None):
    return

@lrafeei lrafeei requested a review from hmstepanek November 22, 2025 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tests-failing Tests failing in CI.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants