Skip to content

Commit

Permalink
Merge branch 'main' into saxon-db-attr-pr-dup
Browse files Browse the repository at this point in the history
  • Loading branch information
obenkenobi authored Sep 28, 2023
2 parents f163ec0 + 1537c79 commit 245e9a7
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 16 deletions.
17 changes: 13 additions & 4 deletions agent-model/src/main/java/com/newrelic/agent/model/SpanEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class SpanEvent extends AnalyticsEvent implements JSONStreamAware {

public static final String SPAN = "Span";
static final String SPAN_KIND = "client";
static final String CLIENT_SPAN_KIND = "client";

private final String appName;
private final Map<String, Object> intrinsics;
Expand Down Expand Up @@ -120,6 +118,7 @@ public static class Builder {
private float priority;
private boolean decider;
private long timestamp;
private Object spanKind;

public Builder appName(String appName) {
this.appName = appName;
Expand Down Expand Up @@ -177,9 +176,19 @@ public Builder putAgentAttribute(String key, Object value) {
return this;
}

public Builder spanKind(Object spanKind) {
putIntrinsic("span.kind", spanKind);
this.spanKind = spanKind;
return this;
}

public boolean isClientSpan() {
return CLIENT_SPAN_KIND.equals(spanKind);
}

public Object getSpanKindFromUserAttributes() {
Object result = userAttributes.get("span.kind");
return result == null ? SPAN_KIND : result;
return result == null ? CLIENT_SPAN_KIND : result;
}

public Builder decider(boolean decider) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@ public final class AttributeNames {
public static final String TIMEOUT_CAUSE = "nr.timeoutCause";
public static final String ERROR_EXPECTED = "error.expected";

public static final String CODE_STACKTRACE = "code.stacktrace";
public static final String COMPONENT = "component";
public static final String HTTP_METHOD = "http.method";
public static final String HTTP_STATUS_CODE = "http.statusCode";
public static final String HTTP_STATUS_TEXT = "http.statusText";

public static final String LOCK_THREAD_NAME = "jvm.lock_thread_name";
public static final String THREAD_NAME = "jvm.thread_name";
public static final String THREAD_ID = "thread.id";

public static final String MESSAGE_REQUEST_PREFIX = "message.parameters.";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@
import com.newrelic.agent.model.SpanError;
import com.newrelic.agent.model.SpanEvent;
import com.newrelic.agent.service.ServiceFactory;
import com.newrelic.agent.tracers.DefaultTracer;
import com.newrelic.agent.util.ExternalsUtil;
import com.newrelic.agent.util.StackTraces;
import com.newrelic.api.agent.DatastoreParameters;
import com.newrelic.api.agent.ExternalParameters;
import com.newrelic.api.agent.HttpParameters;
import com.newrelic.api.agent.SlowQueryDatastoreParameters;

import java.net.URI;
import java.text.MessageFormat;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
Expand All @@ -40,6 +43,7 @@ public class SpanEventFactory {
private static final Joiner TRACE_STATE_VENDOR_JOINER = Joiner.on(",");
// Truncate `db.statement` at 2000 characters
private static final int DB_STATEMENT_TRUNCATE_LENGTH = 2000;
private static final int MAX_EVENT_ATTRIBUTE_STRING_LENGTH = 4095;

public static final Supplier<Long> DEFAULT_SYSTEM_TIMESTAMP_SUPPLIER = System::currentTimeMillis;

Expand Down Expand Up @@ -115,10 +119,31 @@ public SpanEventFactory putAllAgentAttributes(Map<String, ?> agentAttributes) {
return this;
}

/**
* This should be called after the span kind is set.
*/
public SpanEventFactory setStackTraceAttributes(Map<String, Object> agentAttributes) {
if (builder.isClientSpan()) {
final List<StackTraceElement> stackTraceList = (List<StackTraceElement>) agentAttributes.get(DefaultTracer.BACKTRACE_PARAMETER_NAME);
if (stackTraceList != null) {
final List<StackTraceElement> preStackTraces = StackTraces.scrubAndTruncate(stackTraceList);
final List<String> postParentRemovalTrace = StackTraces.toStringList(preStackTraces);

putAgentAttribute(AttributeNames.CODE_STACKTRACE, truncateWithEllipsis(
Joiner.on(',').join(postParentRemovalTrace), MAX_EVENT_ATTRIBUTE_STRING_LENGTH));
}
}
return this;
}

public SpanEventFactory setClmAttributes(Map<String, Object> agentAttributes) {
if (agentAttributes == null || agentAttributes.isEmpty()) {
return this;
}
final Object threadId = agentAttributes.get(AttributeNames.THREAD_ID);
if (threadId != null) {
builder.putIntrinsic(AttributeNames.THREAD_ID, threadId);
}
if (agentAttributes.containsKey(AttributeNames.CLM_NAMESPACE) && agentAttributes.containsKey(AttributeNames.CLM_FUNCTION)) {
builder.putAgentAttribute(AttributeNames.CLM_NAMESPACE, agentAttributes.get(AttributeNames.CLM_NAMESPACE));
builder.putAgentAttribute(AttributeNames.CLM_FUNCTION, agentAttributes.get(AttributeNames.CLM_FUNCTION));
Expand All @@ -132,6 +157,12 @@ public SpanEventFactory putAllUserAttributes(Map<String, ?> userAttributes) {
return this;
}


public SpanEventFactory putAllUserAttributesIfAbsent(Map<String, ?> userAttributes) {
builder.putAllUserAttributesIfAbsent(filter.filterUserAttributes(appName, userAttributes));
return this;
}

public SpanEventFactory putAgentAttribute(String key, Object value) {
builder.putAgentAttribute(key, value);
return this;
Expand Down Expand Up @@ -160,8 +191,7 @@ public SpanEventFactory setCategory(SpanCategory category) {
}

public SpanEventFactory setKindFromUserAttributes() {
Object spanKind = builder.getSpanKindFromUserAttributes();
builder.putIntrinsic("span.kind", spanKind);
builder.spanKind(builder.getSpanKindFromUserAttributes());
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public SpanEvent createSpanEvent(Tracer tracer, TransactionData transactionData,
.setTimestamp(tracer.getStartTimeInMillis())
.setPriority(transactionData.getPriority())
.setExternalParameterAttributes(tracer.getExternalParameters())
.setStackTraceAttributes(tracer.getAgentAttributes())
.setIsRootSpanEvent(isRoot)
.setDecider(inboundPayload == null || inboundPayload.priority == null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@ public class DefaultTracer extends AbstractTracer {
public static final int DEFAULT_TRACER_FLAGS = TracerFlags.TRANSACTION_TRACER_SEGMENT
| TracerFlags.GENERATE_SCOPED_METRIC;

private static final String COMPONENT_PARAMETER_NAME = "component";
private static final String HTTP_METHOD_PARAMETER_NAME = "http.method";

private final long startTime;
private final long timestamp;
private long duration;
Expand Down Expand Up @@ -290,6 +287,7 @@ public void performFinishWork(long finishTime, int opcode, Object returnValue) {
}

try {
setAgentAttribute(AttributeNames.THREAD_ID, getTransactionActivity().getThreadId());
if (classMethodSignature != null && getTransaction() != null &&
ServiceFactory.getConfigService().getDefaultAgentConfig().getCodeLevelMetricsConfig().isEnabled()) {
String className = classMethodSignature.getClassName();
Expand Down Expand Up @@ -712,9 +710,9 @@ private void recordExternalMetricsHttp(HttpParameters externalParameters) {
String uriStr = uri == null ? ExternalMetrics.UNKNOWN_HOST : uri.toString();

String library = externalParameters.getLibrary();
setAgentAttribute(COMPONENT_PARAMETER_NAME, library);
setAgentAttribute(AttributeNames.COMPONENT, library);
String procedure = externalParameters.getProcedure();
setAgentAttribute(HTTP_METHOD_PARAMETER_NAME, procedure);
setAgentAttribute(AttributeNames.HTTP_METHOD, procedure);

ExternalMetrics.makeExternalComponentTrace(transaction.isWebTransaction(), this, host, library, true,
uriStr, procedure);
Expand Down
7 changes: 6 additions & 1 deletion newrelic-agent/src/main/resources/META-INF/excludes
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,9 @@
# exclusions for Open Liberty 21+ so transactions start properly
^com/ibm/ws/security/jaspi/JaspiServletFilter
# Websphere specific servlet wrapper class
^com/ibm/ws/webcontainer/servlet/ServletWrapperImpl
^com/ibm/ws/webcontainer/servlet/ServletWrapperImpl
# Sonarqube9.9 ClassCircularityErrors
^java/util/AbstractList\$RandomAccessSpliterator
^java/util/stream/MatchOps\$MatchOp
^java/util/stream/MatchOps\$BooleanTerminalSink
^javax/security/auth/Subject\$SecureSet\$1
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ public static JsonTraceSegment createTraceSegment(JSONArray segmentArray, String
seg.className = (String) segmentArray.get(5);
seg.methodName = (String) segmentArray.get(6);

// attributes added to all traces
seg.requestParams.put("code.namespace", seg.className);
seg.requestParams.put("code.function", seg.methodName);
seg.requestParams.put("thread.id", "*");
return seg;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,30 @@

package com.newrelic.agent.service.analytics;

import com.google.common.collect.ImmutableMap;
import com.newrelic.agent.MockConfigService;
import com.newrelic.agent.MockServiceManager;
import com.newrelic.agent.attributes.AttributeNames;
import com.newrelic.agent.config.AgentConfig;
import com.newrelic.agent.model.AttributeFilter;
import com.newrelic.agent.model.SpanCategory;
import com.newrelic.agent.model.SpanError;
import com.newrelic.agent.model.SpanEvent;
import com.newrelic.agent.service.ServiceFactory;
import com.newrelic.agent.tracers.DefaultTracer;
import com.newrelic.api.agent.DatastoreParameters;
import com.newrelic.api.agent.HttpParameters;
import org.junit.Test;

import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;

import static com.newrelic.agent.service.analytics.SpanEventFactory.DEFAULT_SYSTEM_TIMESTAMP_SUPPLIER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -190,6 +199,36 @@ public void shouldSetDataStoreParameters() {
assertEquals("dbserver:3306", target.getAgentAttributes().get("peer.address"));
}

@Test
public void shouldStoreStackTrace() {
SpanEventFactory spanEventFactory = new SpanEventFactory("MyApp", new AttributeFilter.PassEverythingAttributeFilter(),
DEFAULT_SYSTEM_TIMESTAMP_SUPPLIER);
spanEventFactory.setKindFromUserAttributes();
MockServiceManager serviceManager = new MockServiceManager();
serviceManager.setConfigService(new MockConfigService(mock(AgentConfig.class)));
ServiceFactory.setServiceManager(serviceManager);
spanEventFactory.setStackTraceAttributes(
ImmutableMap.of(DefaultTracer.BACKTRACE_PARAMETER_NAME, Arrays.asList(Thread.currentThread().getStackTrace())));

final Object stackTrace = spanEventFactory.build().getAgentAttributes().get(AttributeNames.CODE_STACKTRACE);
assertNotNull(stackTrace);
}

@Test
public void shouldSetCLMParameters() {
Map<String, Object> agentAttributes = ImmutableMap.of(
AttributeNames.CLM_NAMESPACE, "nr",
AttributeNames.CLM_FUNCTION, "process",
AttributeNames.THREAD_ID, 666
);

SpanEvent target = spanEventFactory.setClmAttributes(agentAttributes).build();

assertEquals("nr", target.getAgentAttributes().get(AttributeNames.CLM_NAMESPACE));
assertEquals("process", target.getAgentAttributes().get(AttributeNames.CLM_FUNCTION));
assertEquals(666, target.getIntrinsics().get(AttributeNames.THREAD_ID));
}

@Test
public void shouldFilterUserAttributes() {
SpanEventFactory target = new SpanEventFactory("blerb", new AttributeFilter.PassEverythingAttributeFilter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ public void testHighSecurityNormalFinish() throws Exception {
SqlObfuscator sqlObfuscator = ServiceFactory.getDatabaseService().getDefaultSqlObfuscator();
TransactionSegment segment = new TransactionSegment(ttConfig, sqlObfuscator, 0, tracer);

assertEquals(7, tracer.getAgentAttributes().size()); // exclusive_duration_millis, sql, sql_obfuscated, host, port_path_or_id, code.namespace, code.function
assertEquals(8, tracer.getAgentAttributes().size()); // exclusive_duration_millis, sql, sql_obfuscated, host, port_path_or_id, code.namespace, code.function, thread.id
assertEquals(inputSql, tracer.getAgentAttributes().get("sql")); // shouldn't be obfuscated yet
assertClm(tracer);

Expand Down Expand Up @@ -220,7 +220,7 @@ public void testHighSecurityErrorFinish() throws Exception {
SqlObfuscator sqlObfuscator = ServiceFactory.getDatabaseService().getDefaultSqlObfuscator();
TransactionSegment segment = new TransactionSegment(ttConfig, sqlObfuscator, 0, tracer);

assertEquals(4, tracer.getAgentAttributes().size()); // exclusive_duration_millis, sql, code.namespace, code.function
assertEquals(5, tracer.getAgentAttributes().size()); // exclusive_duration_millis, sql, code.namespace, code.function, thread.id
assertEquals(inputSql, (String) tracer.getAgentAttributes().get("sql")); // shouldn't be obfuscated yet
assertClm(tracer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ public void testHighSecurityNormalFinish() throws Exception {
SqlObfuscator sqlObfuscator = ServiceFactory.getDatabaseService().getDefaultSqlObfuscator();
TransactionSegment segment = new TransactionSegment(ttConfig, sqlObfuscator, 0, tracer);

assertEquals(5, tracer.getAgentAttributes().size()); // exclusive_duration_millis, sql, sql_obfuscated, host, port_path_or_id
assertEquals(6, tracer.getAgentAttributes().size()); // exclusive_duration_millis, sql, sql_obfuscated, host, port_path_or_id, thread.id
assertEquals(inputSql, (String) tracer.getAgentAttributes().get("sql")); // shouldn't be obfuscated yet

JSONArray json = (JSONArray) AgentHelper.serializeJSON(segment);
Expand Down Expand Up @@ -198,7 +198,7 @@ public void testHighSecurityErrorFinish() throws Exception {
SqlObfuscator sqlObfuscator = ServiceFactory.getDatabaseService().getDefaultSqlObfuscator();
TransactionSegment segment = new TransactionSegment(ttConfig, sqlObfuscator, 0, tracer);

assertEquals(2, tracer.getAgentAttributes().size()); // exclusive_duration_millis, sql
assertEquals(3, tracer.getAgentAttributes().size()); // exclusive_duration_millis, sql, thread.id
assertEquals(inputSql, (String) tracer.getAgentAttributes().get("sql")); // shouldn't be obfuscated yet

JSONArray json = (JSONArray) AgentHelper.serializeJSON(segment);
Expand Down

0 comments on commit 245e9a7

Please sign in to comment.