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

Added support for setting the framework using the public api (#1908) #1909

Merged
merged 8 commits into from
Jul 13, 2021
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Elasticsearch's REST client - {pull}1883[#1883]
service map and downstream service in the dependencies table - {pull}1898[#1898]
* Basic support for com.sun.net.httpserver.HttpServer - {pull}1854[#1854]
* Update to async-profiler 1.8.6 {pull}1907[#1907]
* Added support for setting the framework using the public api (#1908) - {pull}1909[#1909]

[float]
===== Bug fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,22 @@ public Transaction setName(String name) {
return this;
}



@Nonnull
@Override
public Transaction setType(String type) {
// noop
return this;
}

@Nonnull
@Override
public Transaction setFrameworkName(String frameworkName) {
// noop
return this;
}

@Nonnull
@Deprecated
@Override
Expand Down
10 changes: 10 additions & 0 deletions apm-agent-api/src/main/java/co/elastic/apm/api/Transaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ public interface Transaction extends Span {
@Nonnull
Transaction setType(String type);

/**
* Provides a way to manually set the {@code service.framework.name} field.
* Any value set through this method will take precedence over automatically-set value for supported frameworks.

* @param frameworkName The name of the framework. {@code null} and empty values will cause the exclusion of the
* framework name from the transaction context.
*/
@Nonnull
Transaction setFrameworkName(String frameworkName);

/**
* {@inheritDoc}
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ public Transaction setName(String name) {
return this;
}

@Nonnull
@Override
public Transaction setFrameworkName(String frameworkName) {
// co.elastic.apm.agent.pluginapi.TransactionInstrumentation$SetFrameworkNameInstrumentation
return this;
}

@Nonnull
@Override
public Transaction setType(String type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@
import org.HdrHistogram.WriterReaderPhaser;

import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* Data captured by an agent representing an event occurring in a monitored service
Expand Down Expand Up @@ -93,6 +90,8 @@ protected Labels.Mutable initialValue() {
@Nullable
private String frameworkName;

private boolean frameworkNameSetByUser;

@Nullable
private String frameworkVersion;

Expand Down Expand Up @@ -319,9 +318,21 @@ protected void recycle() {
}

public void setFrameworkName(@Nullable String frameworkName) {
if (frameworkNameSetByUser) {
return;
}
this.frameworkName = frameworkName;
}

public void setUserFrameworkName(@Nullable String frameworkName) {
if (frameworkName != null && frameworkName.isEmpty()) {
this.frameworkName = null;
} else {
this.frameworkName = frameworkName;
}
this.frameworkNameSetByUser = true;
}

@Nullable
public String getFrameworkName() {
return this.frameworkName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,24 @@ void testSpanDestinationContextSerialization() {
assertThat(service.get("type").textValue()).isEqualTo("");
}

@Test
void testTransactionNullFrameworkNameSerialization() {
Transaction transaction = new Transaction(MockTracer.create());
transaction.getTraceContext().setServiceName("service-name");
transaction.setUserFrameworkName(null);
JsonNode transactionJson = readJsonString(serializer.toJsonString(transaction));
assertThat(transactionJson.get("context").get("service").get("framework")).isNull();
}

@Test
void testTransactionEmptyFrameworkNameSerialization() {
Transaction transaction = new Transaction(MockTracer.create());
transaction.getTraceContext().setServiceName("service-name");
transaction.setUserFrameworkName("");
JsonNode transactionJson = readJsonString(serializer.toJsonString(transaction));
assertThat(transactionJson.get("context").get("service").get("framework")).isNull();
}

@Test
void testSpanInvalidDestinationSerialization() {
Span span = new Span(MockTracer.create());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ public ElementMatcher<? super MethodDescription> getMethodMatcher() {
return methodMatcher;
}

public static class SetFrameworkNameInstrumentation extends TransactionInstrumentation {
public SetFrameworkNameInstrumentation() {
super(named("setFrameworkName"));
}

@Advice.OnMethodEnter(suppress = Throwable.class, inline = false)
public static void setFrameworkName(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Object transaction,
@Advice.Argument(0) String frameworkName) {
if (transaction instanceof Transaction) {
((Transaction) transaction).setUserFrameworkName(frameworkName);
}
}
}

public static class SetUserInstrumentation extends TransactionInstrumentation {
public SetUserInstrumentation() {
super(named("setUser"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ co.elastic.apm.agent.pluginapi.ElasticApmApiInstrumentation$StartTransactionWith
co.elastic.apm.agent.pluginapi.ElasticApmApiInstrumentation$CurrentTransactionInstrumentation
co.elastic.apm.agent.pluginapi.ElasticApmApiInstrumentation$CurrentSpanInstrumentation
co.elastic.apm.agent.pluginapi.ElasticApmApiInstrumentation$CaptureExceptionInstrumentation
co.elastic.apm.agent.pluginapi.TransactionInstrumentation$SetFrameworkNameInstrumentation
co.elastic.apm.agent.pluginapi.TransactionInstrumentation$SetUserInstrumentation
co.elastic.apm.agent.pluginapi.TransactionInstrumentation$EnsureParentIdInstrumentation
co.elastic.apm.agent.pluginapi.TransactionInstrumentation$SetResultInstrumentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@

import co.elastic.apm.AbstractApiTest;
import co.elastic.apm.agent.impl.TracerInternalApiUtils;
import co.elastic.apm.api.AbstractSpanImplAccessor;
import co.elastic.apm.api.ElasticApm;
import co.elastic.apm.api.Outcome;
import co.elastic.apm.api.Span;
import co.elastic.apm.api.Transaction;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import javax.annotation.Nullable;
import java.security.SecureRandom;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -57,6 +61,44 @@ void testFrameworkName() {
assertThat(reporter.getFirstTransaction().getFrameworkName()).isEqualTo("API");
}

@Test
void testSetUserFrameworkValidNameBeforeSetByInternalAPI() {
transaction.setFrameworkName("foo");
AbstractSpanImplAccessor.accessTransaction(transaction).setFrameworkName("bar");
endTransaction();
assertThat(reporter.getFirstTransaction().getFrameworkName()).isEqualTo("foo");
}

@Test
void testSetUserFrameworkValidNameAfterSetByInternalAPI() {
AbstractSpanImplAccessor.accessTransaction(transaction).setFrameworkName("bar");
transaction.setFrameworkName("foo");
endTransaction();
assertThat(reporter.getFirstTransaction().getFrameworkName()).isEqualTo("foo");
}

static String[] invalidFrameworkNames() {
return new String[]{null, ""};
}

@ParameterizedTest
@MethodSource("invalidFrameworkNames")
void testSetUserFrameworkInvalidNameBeforeSetByInternalAPI(@Nullable String frameworkName) {
transaction.setFrameworkName(frameworkName);
AbstractSpanImplAccessor.accessTransaction(transaction).setFrameworkName("bar");
endTransaction();
assertThat(reporter.getFirstTransaction().getFrameworkName()).isNull();
}

@ParameterizedTest
@MethodSource("invalidFrameworkNames")
void testSetUserFrameworkInvalidNameAfterSetByInternalAPI(@Nullable String frameworkName) {
AbstractSpanImplAccessor.accessTransaction(transaction).setFrameworkName("bar");
transaction.setFrameworkName(frameworkName);
endTransaction();
assertThat(reporter.getFirstTransaction().getFrameworkName()).isNull();
}

@Test
void testSetType() {
transaction.setType("foo");
Expand Down Expand Up @@ -129,7 +171,6 @@ private void checkResult(String expected) {
}



@Test
void testChaining() {
int randomInt = random.nextInt();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package co.elastic.apm.api;

public class AbstractSpanImplAccessor {

public static co.elastic.apm.agent.impl.transaction.Transaction accessTransaction(Transaction t) {
return (co.elastic.apm.agent.impl.transaction.Transaction) ((TransactionImpl) t).span;
}
}
18 changes: 18 additions & 0 deletions docs/public-api.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,24 @@ transaction.setType(Transaction.TYPE_REQUEST);

* `type`: The type of the transaction

[float]
[[api-transaction-set-framework-name]]
==== `Transaction setFrameworkName(String frameworkName)` added[1.25.0]
Provides a way to manually set the `service.framework.name` field.
For supported frameworks,
the framework name is determined automatically,
and can be overridden using this function.
`null` or the empty string will make the agent omit this field.

Example:

[source,java]
----
transaction.setFrameworkName("My Framework");
----

* `frameworkName`: The name of the framework

[float]
[[api-transaction-add-tag]]
==== `Transaction setLabel(String key, value)` added[1.5.0 as `addLabel`,Number and boolean labels require APM Server 6.7]
Expand Down