Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NH-91749: use the new extended span processor #267

Merged
merged 1 commit into from
Sep 30, 2024
Merged
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
10 changes: 10 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,16 @@ jobs:
cd smoke-tests
./gradlew build -x test

- name: Build webmvc jar
run: |
cd smoke-tests
./gradlew :spring-boot-webmvc:build

- name: Build webmvc image
run: |
cd smoke-tests/spring-boot-webmvc
docker image build --tag smt:webmvc .

- name: Docker login
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $GITHUB_USERNAME --password-stdin

Expand Down
1 change: 1 addition & 0 deletions custom/lambda/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies {

compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:${versions.opentelemetryJavaagent}")
compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:${versions.opentelemetryJavaagentAlpha}")
compileOnly project(path: ":bootstrap")

testImplementation project(path: ":custom:shared")
testImplementation "org.json:json:${versions.json}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.internal.ExtendedSpanProcessor;
import io.opentelemetry.semconv.SemanticAttributes;

public class InboundMeasurementMetricsGenerator implements SpanProcessor {
public class InboundMeasurementMetricsGenerator implements ExtendedSpanProcessor {
private LongHistogram responseTime;

private static final Logger logger = LoggerFactory.getLogger();
Expand Down Expand Up @@ -109,4 +109,18 @@ public void onEnd(ReadableSpan span) {
public boolean isEndRequired() {
return true;
}

@Override
public void onEnding(ReadWriteSpan span) {
SpanData spanData = span.toSpanData();
final SpanContext parentSpanContext = spanData.getParentSpanContext();
if (!parentSpanContext.isValid() || parentSpanContext.isRemote()) {
span.setAttribute(TRANSACTION_NAME_KEY, TransactionNameManager.getTransactionName(spanData));
}
}

@Override
public boolean isOnEndingRequired() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.solarwinds.joboe.logging.Logger;
import com.solarwinds.joboe.logging.LoggerFactory;
import com.solarwinds.joboe.sampling.SettingsManager;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.trace.samplers.Sampler;
Expand All @@ -42,14 +41,6 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk openTelemetrySdk) {
SettingsManager.initialize(
new AwsLambdaSettingsFetcher(new FileSettingsReader("/tmp/solarwinds-apm-settings.json")),
SamplingConfigProvider.getSamplingConfiguration());

TransactionNameManager.setRuntimeNameGenerator(
spanData ->
new TransactionNameManager.TransactionNameResult(
spanData
.getAttributes()
.get(AttributeKey.stringKey(SharedNames.TRANSACTION_NAME_KEY)),
true));
logger.info("Successfully submitted SolarwindsAPM OpenTelemetry extensions settings");
} else {
logger.info("SolarwindsAPM OpenTelemetry extensions is disabled");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.Value;

public class TransactionNameManager {
private static final Logger logger = LoggerFactory.getLogger();
Expand All @@ -64,11 +60,6 @@ public class TransactionNameManager {

private static NamingScheme namingScheme = new DefaultNamingScheme(null);

@Getter @Setter
private static RuntimeNameGenerator runtimeNameGenerator =
(SpanData spanData) ->
new TransactionNameResult(CustomTransactionNameDict.get(spanData.getTraceId()), false);

static {
customTransactionNamePattern = getTransactionNamePattern();
addNameCountChangeListener();
Expand Down Expand Up @@ -128,12 +119,7 @@ static String[] parseTransactionNamePattern(String pattern) {
* @return transaction name
*/
public static String getTransactionName(SpanData spanData) {
TransactionNameResult transactionNameResult = buildTransactionName(spanData);
String transactionName = transactionNameResult.name;

if (transactionNameResult.isLambda()) {
return transactionName;
}
String transactionName = buildTransactionName(spanData);

if (transactionName != null) {
Boolean domainPrefixedTransactionName =
Expand Down Expand Up @@ -203,34 +189,34 @@ static String transformTransactionName(String inputTransactionName) {
return transactionName;
}

static TransactionNameResult buildTransactionName(SpanData spanData) {
static String buildTransactionName(SpanData spanData) {
Attributes spanAttributes = spanData.getAttributes();
TransactionNameResult transactionNameResult = runtimeNameGenerator.generateName(spanData);
String custName = CustomTransactionNameDict.get(spanData.getTraceId());

if (transactionNameResult.name != null) {
logger.trace(String.format("Using custom transaction name -> %s", transactionNameResult));
return transactionNameResult;
if (custName != null) {
logger.trace(String.format("Using custom transaction name -> %s", custName));
return custName;
}

String name = namingScheme.createName(spanAttributes);
if (name != null && !name.isEmpty()) {
logger.trace(String.format("Using scheme derived transaction name -> %s", name));
return new TransactionNameResult(name, false);
return name;
}

// use HandlerName which may be injected by some MVC instrumentations (currently only Spring
// MVC)
String handlerName = spanAttributes.get(AttributeKey.stringKey("HandlerName"));
if (handlerName != null) {
logger.trace(String.format("Using HandlerName(%s) as the transaction name", handlerName));
return new TransactionNameResult(handlerName, false);
return handlerName;
}

// use "http.route"
String httpRoute = spanAttributes.get(SemanticAttributes.HTTP_ROUTE);
if (httpRoute != null) {
logger.trace(String.format("Using http.route (%s) as the transaction name", httpRoute));
return new TransactionNameResult(httpRoute, false);
return httpRoute;
}

// get transaction name from url
Expand All @@ -246,7 +232,7 @@ static TransactionNameResult buildTransactionName(SpanData spanData) {
String.format(
"Using custom configure pattern to extract transaction name: (%s)",
transactionName));
return new TransactionNameResult(transactionName, false);
return transactionName;
}
}

Expand All @@ -261,12 +247,12 @@ static TransactionNameResult buildTransactionName(SpanData spanData) {
logger.trace(
String.format(
"Using token name pattern to extract transaction name: (%s)", transactionNameByUrl));
return new TransactionNameResult(transactionNameByUrl, false);
return transactionNameByUrl;
}

String spanName = spanData.getName();
logger.trace(String.format("Using span name as the transaction name: (%s)", spanName));
return new TransactionNameResult(spanName, false);
return spanName;
}

/**
Expand Down Expand Up @@ -379,16 +365,4 @@ static void reset() {
URL_TRANSACTION_NAME_CACHE.invalidateAll();
maxNameCount = DEFAULT_MAX_NAME_COUNT;
}

@Value
@RequiredArgsConstructor
public static class TransactionNameResult {
String name;
boolean lambda;
}

@FunctionalInterface
public interface RuntimeNameGenerator {
TransactionNameResult generateName(SpanData spanData);
}
}
3 changes: 2 additions & 1 deletion smoke-tests/k6/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,8 @@ function silence(fn) {
}

export default function () {
silence(verify_that_span_data_is_persisted_0)

if (`${__ENV.LAMBDA}` === "true") {
const request_count = (measurement) => check(measurement, {"request_count": mrs => mrs.value > 0})
const tracecount = (measurement) => check(measurement, {"tracecount": mrs => mrs.value > 0})
Expand Down Expand Up @@ -585,7 +587,6 @@ export default function () {
})
silence(verify_logs_export)
silence(verify_that_specialty_path_is_not_sampled)
silence(verify_that_span_data_is_persisted_0)

silence(verify_that_span_data_is_persisted)
silence(verify_that_trace_is_persisted)
Expand Down
48 changes: 29 additions & 19 deletions smoke-tests/src/test/java/com/solarwinds/LambdaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,32 @@

package com.solarwinds;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import com.solarwinds.agents.Agent;
import com.solarwinds.agents.SwoAgentResolver;
import com.solarwinds.agents.SwoLambdaAgentResolver;
import com.solarwinds.config.Configs;
import com.solarwinds.config.TestConfig;
import com.solarwinds.containers.K6Container;
import com.solarwinds.containers.PetClinicRestContainer;
import com.solarwinds.containers.PostgresContainer;
import com.solarwinds.containers.SpringBootWebMvcContainer;
import com.solarwinds.results.ResultsCollector;
import com.solarwinds.util.LogStreamAnalyzer;
import com.solarwinds.util.NamingConventions;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.output.OutputFrame;
import org.testcontainers.containers.output.Slf4jLogConsumer;

import java.io.IOException;
import java.nio.file.Files;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

@EnabledIfEnvironmentVariable(named = "LAMBDA", matches = "true")
public class LambdaTest {
private static final Network NETWORK = Network.newNetwork();
Expand All @@ -64,34 +64,37 @@ static void runTestConfig() {
.forEach(
agent -> {
try {
runAppOnce(config, agent);
runAppOnce(agent);
} catch (Exception e) {
fail("Unhandled exception in " + config.name(), e);
}
});
}

static void runAppOnce(TestConfig config, Agent agent) throws Exception {
static void runAppOnce(Agent agent) throws Exception {
GenericContainer<?> postgres = new PostgresContainer(NETWORK).build();
postgres.start();

GenericContainer<?> petClinic = new PetClinicRestContainer(new SwoLambdaAgentResolver(), NETWORK,
agent, namingConventions).build();
GenericContainer<?> webMvc = new SpringBootWebMvcContainer(new SwoLambdaAgentResolver(), NETWORK, agent).build();
webMvc.start();

GenericContainer<?> petClinic = new PetClinicRestContainer(new SwoLambdaAgentResolver(), NETWORK, agent).build();
petClinic.start();
petClinic.followOutput(logStreamAnalyzer);

GenericContainer<?> k6 = new K6Container(NETWORK, agent, config, namingConventions).build();
GenericContainer<?> k6 = new K6Container(NETWORK, agent, namingConventions).build();
k6.start();
k6.followOutput(new Slf4jLogConsumer(LoggerFactory.getLogger("k6")));

petClinic.execInContainer("kill", "1");
webMvc.execInContainer("kill", "1");
postgres.stop();
}

@Test
void assertThatRequestCountMetricIsReported() throws IOException {
String resultJson = new String(
Files.readAllBytes(namingConventions.local.k6Results(Configs.E2E.config.agents().get(0))));
Files.readAllBytes(namingConventions.local.k6Results(Configs.LAMBDA_E2E.config.agents().get(0))));

double passes = ResultsCollector.read(resultJson, "$.root_group.checks.['request_count'].passes");
assertTrue(passes > 1, "Expects a count > 1 ");
Expand All @@ -100,7 +103,7 @@ void assertThatRequestCountMetricIsReported() throws IOException {
@Test
void assertThatTraceCountMetricIsReported() throws IOException {
String resultJson = new String(
Files.readAllBytes(namingConventions.local.k6Results(Configs.E2E.config.agents().get(0))));
Files.readAllBytes(namingConventions.local.k6Results(Configs.LAMBDA_E2E.config.agents().get(0))));

double passes = ResultsCollector.read(resultJson, "$.root_group.checks.['tracecount'].passes");
assertTrue(passes > 1, "Expects a count > 1 ");
Expand All @@ -109,7 +112,7 @@ void assertThatTraceCountMetricIsReported() throws IOException {
@Test
void assertThatSampleCountMetricIsReported() throws IOException {
String resultJson = new String(
Files.readAllBytes(namingConventions.local.k6Results(Configs.E2E.config.agents().get(0))));
Files.readAllBytes(namingConventions.local.k6Results(Configs.LAMBDA_E2E.config.agents().get(0))));

double passes = ResultsCollector.read(resultJson, "$.root_group.checks.['samplecount'].passes");
assertTrue(passes > 1, "Expects a count > 1 ");
Expand All @@ -118,7 +121,7 @@ void assertThatSampleCountMetricIsReported() throws IOException {
@Test
void assertThatResponseTimeMetricIsReported() throws IOException {
String resultJson = new String(
Files.readAllBytes(namingConventions.local.k6Results(Configs.E2E.config.agents().get(0))));
Files.readAllBytes(namingConventions.local.k6Results(Configs.LAMBDA_E2E.config.agents().get(0))));

double passes = ResultsCollector.read(resultJson, "$.root_group.checks.['response_time'].passes");
assertTrue(passes > 1, "Expects a count > 1 ");
Expand All @@ -127,10 +130,10 @@ void assertThatResponseTimeMetricIsReported() throws IOException {
@Test
void assertThatCustomTransactionNameTakesEffect() throws IOException {
String resultJson = new String(
Files.readAllBytes(namingConventions.local.k6Results(Configs.E2E.config.agents().get(0))));
Files.readAllBytes(namingConventions.local.k6Results(Configs.LAMBDA_E2E.config.agents().get(0))));

double passes = ResultsCollector.read(resultJson, "$.root_group.checks.['transaction-name'].passes");
assertTrue(passes > 1, "Expects a count > 1 ");
assertTrue(passes > 1, "Environment based transaction naming is broken ");
}

@Test
Expand All @@ -144,4 +147,11 @@ void assertThatJDBCInstrumentationIsApplied() {
Boolean actual = logStreamAnalyzer.getAnswer().get("Applying instrumentation: sw-jdbc");
assertTrue(actual, "sw-jdbc instrumentation is not applied");
}

@Test
void assertSDKTransactionNaming() throws IOException {
String resultJson = new String(Files.readAllBytes(namingConventions.local.k6Results(Configs.LAMBDA_E2E.config.agents().get(0))));
double passes = ResultsCollector.read(resultJson, "$.root_group.checks.['custom transaction name'].passes");
assertTrue(passes > 1, "SDK transaction naming is broken");
}
}
8 changes: 4 additions & 4 deletions smoke-tests/src/test/java/com/solarwinds/SmokeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ static void runTestConfig() {
.forEach(
agent -> {
try {
runAppOnce(config, agent);
runAppOnce(agent);
} catch (Exception e) {
fail("Unhandled exception in " + config.name(), e);
}
});
}

static void runAppOnce(TestConfig config, Agent agent) throws Exception {
static void runAppOnce(Agent agent) throws Exception {
GenericContainer<?> webMvc = new SpringBootWebMvcContainer(new SwoAgentResolver(), NETWORK, agent).build();
webMvc.start();
webMvc.followOutput(logStreamAnalyzer);
Expand All @@ -91,11 +91,11 @@ static void runAppOnce(TestConfig config, Agent agent) throws Exception {
GenericContainer<?> postgres = new PostgresContainer(NETWORK).build();
postgres.start();

GenericContainer<?> petClinic = new PetClinicRestContainer(new SwoAgentResolver(), NETWORK, agent, namingConventions).build();
GenericContainer<?> petClinic = new PetClinicRestContainer(new SwoAgentResolver(), NETWORK, agent).build();
petClinic.start();
petClinic.followOutput(logStreamAnalyzer);

GenericContainer<?> k6 = new K6Container(NETWORK, agent, config, namingConventions).build();
GenericContainer<?> k6 = new K6Container(NETWORK, agent, namingConventions).build();
k6.start();
k6.followOutput(new Slf4jLogConsumer(LoggerFactory.getLogger("k6")), OutputFrame.OutputType.STDOUT);

Expand Down
Loading