Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@

package com.newrelic.agent.bridge;

import com.newrelic.agent.bridge.opentelemetry.SpanLink;
import com.newrelic.api.agent.ExternalParameters;
import com.newrelic.api.agent.InboundHeaders;
import com.newrelic.api.agent.OutboundHeaders;

import java.util.Collections;
import java.util.List;
import java.util.Map;

public final class NoOpTracedMethod implements TracedMethod {
Expand All @@ -28,6 +31,15 @@ public void setMetricName(String... metricNameParts) {
public void nameTransaction(TransactionNamePriority namePriority) {
}

@Override
public void addSpanLink(SpanLink link) {
}

@Override
public List<SpanLink> getSpanLinks() {
return Collections.emptyList();
}

@Override
public TracedMethod getParentTracedMethod() {
return null;
Expand Down Expand Up @@ -100,7 +112,8 @@ public boolean isTrackCallbackRunnable() {
}

@Override
public void excludeLeaf(){}
public void excludeLeaf() {
}

@Override
public void addOutboundRequestHeaders(OutboundHeaders outboundHeaders) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@

package com.newrelic.agent.bridge;

import com.newrelic.agent.bridge.opentelemetry.SpanLink;
import com.newrelic.api.agent.ExternalParameters;
import com.newrelic.api.agent.InboundHeaders;
import com.newrelic.api.agent.OutboundHeaders;

import java.util.List;

/**
* The internal bridge version of TracedMethod.
*/
Expand All @@ -19,9 +22,29 @@ public interface TracedMethod extends com.newrelic.api.agent.TracedMethod {
default String getTraceId() {
return "0000000000000000";
}

default String getSpanId() {
return "0000000000000000";
}

/**
* Add a SpanLink to a collection stored on the traced method.
* <p>
* This is used to support the OpenTelemetry concept of a SpanLink,
* which allows a Span from one trace to link to a Span from
* a different trace, defining a relationship between the traces.
*
* @param link a SpanLink
*/
void addSpanLink(SpanLink link);

/**
* Get a list of SpanLinks associated with this traced method.
*
* @return list of SpanLinks
*/
List<SpanLink> getSpanLinks();

/**
* Returns the parent of this traced method, or null if this is the root tracer.
*
Expand All @@ -38,19 +61,19 @@ default String getSpanId() {

/**
* Add a rollup metric name that reports the exclusive time for <b>both</b> total and exclusive times.
*
* <p>
* The reason for this is that external and database charts on APM show stacked graphs based on total duration of
* our rollup metrics, when the intent seems to be to show stacked graphs of exclusive durations. Previous tracer
* implementations like AbstractExternalComponentTracer called ResponseTimeStats.recordResponseTime(
* getExclusiveDuration(), TimeUnit.NANOSECONDS), presumably to "enable" this "feature". So this method only exists
* in the bridge API to replicate the old behavior.
*
* <p>
* More investigation is necessary. Use with care.
*
* @param metricNameParts The segments of the metric name. These values will be concatenated together separated by a
* `/` char.
* `/` char.
*/
void addExclusiveRollupMetricName(String ... metricNameParts);
void addExclusiveRollupMetricName(String... metricNameParts);

/**
* Names the current transaction using this traced method. This is called by code injected as a result of xml
Expand Down Expand Up @@ -96,12 +119,12 @@ default String getSpanId() {
/**
* Do not use. Use
* {@link com.newrelic.api.agent.TracedMethod#addOutboundRequestHeaders(OutboundHeaders)} instead.
*
* <p>
* To be called when performing an outbound external request using HTTP or JMS. This method must be called before
* any headers are written to the output stream. This method is generally used in conjunction with reportAsExternal.
*
* @param outboundHeaders The headers that will be written to the output stream for the external request. This also
* determines if the external call is HTTP or JMS.
* determines if the external call is HTTP or JMS.
* @since 3.26.0
*/
@Deprecated
Expand All @@ -118,16 +141,15 @@ default String getSpanId() {
void readInboundResponseHeaders(InboundHeaders inboundResponseHeaders);

/**
* @param externalParameters The appropriate input parameters depending on the type external. Use the
* {@link com.newrelic.api.agent.ExternalParametersFactory} to create input parameters. For example,
* {@link com.newrelic.api.agent.ExternalParametersFactory}'s createForDatastore to report this TracedMethod as a datastore.
* @Deprecated Do not use. Use
* {@link com.newrelic.api.agent.TracedMethod#reportAsExternal(ExternalParameters)} instead.
*
* <p>
* Used to report this traced method as an HTTP external call, datastore external call, or general external call.
* Use {@link com.newrelic.api.agent.ExternalParametersFactory} to create the input externalParameters. If you are performing an external
* HTTP call, be sure to call addOutboundRequestHeaders prior to the request being sent.
*
* @param externalParameters The appropriate input parameters depending on the type external. Use the
* {@link com.newrelic.api.agent.ExternalParametersFactory} to create input parameters. For example,
* {@link com.newrelic.api.agent.ExternalParametersFactory}'s createForDatastore to report this TracedMethod as a datastore.
* @since 3.26.0
*/
@Deprecated
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
*
* * Copyright 2025 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.newrelic.agent.bridge.opentelemetry;

import java.util.Collections;
import java.util.Map;

/**
* Representation of an OpenTelemetry SpanLink.
*/
public class SpanLink {

// If a timestamp is not available in the OTel spanlink data model, we can use the start time of the span containing the span link as the timestamp value. This timestamp must be in the expected New Relic timestamp format.
private final long timestamp;

// The span id of the span containing the span link.
private final String id;

// The trace id of the span containing the span link.
private final String traceId;

// The span id of the upstream span defined on the span link.
private final String linkedSpanId;

// The trace id of the upstream span defined on the span link.
private final String linkedTraceId;

// Map of attributes associated with the span link.
private final Map<String, Object> userAttributes;

public SpanLink(long timestamp, String id, String traceId, String linkedSpanId, String linkedTraceId, Map<String, Object> userAttributes) {
this.timestamp = timestamp;
this.id = id;
this.traceId = traceId;
this.linkedSpanId = linkedSpanId;
this.linkedTraceId = linkedTraceId;
this.userAttributes = userAttributes;
}

// This is the event type that should be stored in NRDB.
public String getType() {
return "SpanLink";
}

public long getTimestamp() {
return timestamp;
}

public String getId() {
return id;
}

public String getTraceId() {
return traceId;
}

public String getLinkedSpanId() {
return linkedSpanId;
}

public String getLinkedTraceId() {
return linkedTraceId;
}

public Map<String, ?> getUserAttributes() {
if (userAttributes == null || userAttributes.isEmpty()) {
return Collections.emptyMap();
}
return userAttributes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
*
* * Copyright 2025 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.newrelic.agent.bridge.opentelemetry;

import junit.framework.TestCase;

import java.util.Collections;
import java.util.Map;

public class SpanLinkTest extends TestCase {
long timestamp = System.currentTimeMillis();
String type = "SpanLink";
String id = "id";
String traceId = "traceId";
String linkedSpanId = "linkedSpanId";
String linkedTraceId = "linkedTraceId";
Map<String, Object> userAttributes = Collections.singletonMap("foo", "bar");
SpanLink spanLink = new SpanLink(timestamp, id, traceId, linkedSpanId, linkedTraceId, userAttributes);

public void testGetType() {
assertEquals(type, spanLink.getType());
}

public void testGetTimestamp() {
assertEquals(timestamp, spanLink.getTimestamp());
}

public void testGetId() {
assertEquals(id, spanLink.getId());
}

public void testGetTraceId() {
assertEquals(traceId, spanLink.getTraceId());
}

public void testGetLinkedSpanId() {
assertEquals(linkedSpanId, spanLink.getLinkedSpanId());
}

public void testGetLinkedTraceId() {
assertEquals(linkedTraceId, spanLink.getLinkedTraceId());
}

public void testGetUserAttributes() {
assertEquals(userAttributes, spanLink.getUserAttributes());
}
}
Loading