Merge pull request #71 from gpolaert/operation-name

Override the operation name
This commit is contained in:
Emanuele Palazzetti 2017-08-01 12:30:50 +02:00 committed by GitHub
commit 7667fb6021
15 changed files with 167 additions and 109 deletions

View File

@ -5,6 +5,7 @@ import com.mongodb.MongoClientOptions;
import com.mongodb.event.CommandStartedEvent;
import io.opentracing.Span;
import io.opentracing.contrib.mongo.TracingCommandListener;
import io.opentracing.tag.Tags;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -50,8 +51,14 @@ public class MongoHelper extends DDAgentTracingHelper<MongoClientOptions.Builder
public void decorate(final Span span, final CommandStartedEvent event) {
try {
// normalize the Mongo command so that parameters are removed from the string
final BsonDocument normalized = norm(event.getCommand());
span.setTag(DDTags.RESOURCE_NAME, normalized.toString());
final String mongoCmd = normalized.toString();
// add specific resource name and replace the `db.statement` OpenTracing
// tag with the quantized version of the Mongo command
span.setTag(DDTags.RESOURCE_NAME, mongoCmd);
span.setTag(Tags.DB_STATEMENT.getKey(), mongoCmd);
} catch (final Throwable e) {
log.warn("Couldn't decorate the mongo query: " + e.getMessage(), e);
}

View File

@ -1,6 +1,6 @@
package com.datadoghq.trace;
import com.datadoghq.trace.integration.DDSpanContextDecorator;
import com.datadoghq.trace.integration.AbstractDecorator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.collect.Maps;
import io.opentracing.tag.Tags;
@ -28,28 +28,27 @@ public class DDSpanContext implements io.opentracing.SpanContext {
private final long parentId;
private final String threadName = Thread.currentThread().getName();
private final long threadId = Thread.currentThread().getId();
private Map<String, String> baggageItems;
/** The collection of all span related to this one */
private final Queue<DDBaseSpan<?>> trace;
// DD attributes
/** For technical reasons, the ref to the original tracer */
private final DDTracer tracer;
private Map<String, String> baggageItems;
/** The service name is required, otherwise the span are dropped by the agent */
private String serviceName;
/** The resource associated to the service (server_web, database, etc.) */
private String resourceName;
/** True indicates that the span reports an error */
private boolean errorFlag;
/** The type of the span. If null, the Datadog Agent will report as a custom */
private String spanType;
/** The collection of all span related to this one */
private final Queue<DDBaseSpan<?>> trace;
/** Each span have an operation name describing the current span */
private String operationName;
// Others attributes
/** Tags are associated to the current span, they will not propagate to the children span */
private Map<String, Object> tags;
// Others attributes
/** For technical reasons, the ref to the original tracer */
private final DDTracer tracer;
public DDSpanContext(
final long traceId,
@ -109,12 +108,20 @@ public class DDSpanContext implements io.opentracing.SpanContext {
return serviceName;
}
public void setServiceName(final String serviceName) {
this.serviceName = serviceName;
}
public String getResourceName() {
return this.resourceName == null || this.resourceName.isEmpty()
? this.operationName
: this.resourceName;
}
public void setResourceName(final String resourceName) {
this.resourceName = resourceName;
}
public boolean getErrorFlag() {
return errorFlag;
}
@ -127,6 +134,10 @@ public class DDSpanContext implements io.opentracing.SpanContext {
return spanType;
}
public void setSpanType(final String spanType) {
this.spanType = spanType;
}
public void setBaggageItem(final String key, final String value) {
if (this.baggageItems.isEmpty()) {
this.baggageItems = new HashMap<String, String>();
@ -184,9 +195,9 @@ public class DDSpanContext implements io.opentracing.SpanContext {
this.tags.put(tag, value);
//Call decorators
final List<DDSpanContextDecorator> decorators = tracer.getSpanContextDecorators(tag);
final List<AbstractDecorator> decorators = tracer.getSpanContextDecorators(tag);
if (decorators != null) {
for (final DDSpanContextDecorator decorator : decorators) {
for (final AbstractDecorator decorator : decorators) {
decorator.afterSetTag(this, tag, value);
}
}
@ -223,23 +234,11 @@ public class DDSpanContext implements io.opentracing.SpanContext {
.toString();
}
public void setOperationName(final String operationName) {
this.operationName = operationName;
}
public String getOperationName() {
return operationName;
}
public void setServiceName(final String serviceName) {
this.serviceName = serviceName;
}
public void setResourceName(final String resourceName) {
this.resourceName = resourceName;
}
public void setSpanType(final String spanType) {
this.spanType = spanType;
public void setOperationName(final String operationName) {
this.operationName = operationName;
}
}

View File

@ -1,6 +1,6 @@
package com.datadoghq.trace;
import com.datadoghq.trace.integration.DDSpanContextDecorator;
import com.datadoghq.trace.integration.AbstractDecorator;
import com.datadoghq.trace.propagation.Codec;
import com.datadoghq.trace.propagation.HTTPCodec;
import com.datadoghq.trace.sampling.AllSampler;
@ -39,7 +39,7 @@ public class DDTracer extends ThreadLocalActiveSpanSource implements io.opentrac
private final String defaultServiceName;
/** Span context decorators */
private final Map<String, List<DDSpanContextDecorator>> spanContextDecorators = new HashMap<>();
private final Map<String, List<AbstractDecorator>> spanContextDecorators = new HashMap<>();
private final CodecRegistry registry;
@ -70,18 +70,18 @@ public class DDTracer extends ThreadLocalActiveSpanSource implements io.opentrac
*
* @return the list of span context decorators
*/
public List<DDSpanContextDecorator> getSpanContextDecorators(final String tag) {
public List<AbstractDecorator> getSpanContextDecorators(final String tag) {
return spanContextDecorators.get(tag);
}
/**
* Add a new decorator in the list ({@link DDSpanContextDecorator})
* Add a new decorator in the list ({@link AbstractDecorator})
*
* @param decorator The decorator in the list
*/
public void addDecorator(final DDSpanContextDecorator decorator) {
public void addDecorator(final AbstractDecorator decorator) {
List<DDSpanContextDecorator> list = spanContextDecorators.get(decorator.getMatchingTag());
List<AbstractDecorator> list = spanContextDecorators.get(decorator.getMatchingTag());
if (list == null) {
list = new ArrayList<>();
}

View File

@ -7,7 +7,7 @@ import com.datadoghq.trace.DDTags;
* Span decorators are called when new tags are written and proceed to various remappings and
* enrichments
*/
public abstract class DDSpanContextDecorator {
public abstract class AbstractDecorator {
private String matchingTag;
@ -17,10 +17,10 @@ public abstract class DDSpanContextDecorator {
private String setValue;
public boolean afterSetTag(DDSpanContext context, String tag, Object value) {
public boolean afterSetTag(final DDSpanContext context, final String tag, final Object value) {
if ((this.getMatchingValue() == null || value.equals(this.getMatchingValue()))) {
String targetTag = getSetTag() == null ? tag : getSetTag();
String targetValue = getSetValue() == null ? String.valueOf(value) : getSetValue();
final String targetTag = getSetTag() == null ? tag : getSetTag();
final String targetValue = getSetValue() == null ? String.valueOf(value) : getSetValue();
if (targetTag.equals(DDTags.SERVICE_NAME)) {
context.setServiceName(targetValue);
@ -41,7 +41,7 @@ public abstract class DDSpanContextDecorator {
return matchingTag;
}
public void setMatchingTag(String tag) {
public void setMatchingTag(final String tag) {
this.matchingTag = tag;
}
@ -49,7 +49,7 @@ public abstract class DDSpanContextDecorator {
return matchingValue;
}
public void setMatchingValue(String value) {
public void setMatchingValue(final String value) {
this.matchingValue = value;
}
@ -57,7 +57,7 @@ public abstract class DDSpanContextDecorator {
return setTag;
}
public void setSetTag(String targetTag) {
public void setSetTag(final String targetTag) {
this.setTag = targetTag;
}
@ -65,7 +65,7 @@ public abstract class DDSpanContextDecorator {
return setValue;
}
public void setSetValue(String targetValue) {
public void setSetValue(final String targetValue) {
this.setValue = targetValue;
}
}

View File

@ -3,42 +3,11 @@ package com.datadoghq.trace.integration;
import com.datadoghq.trace.DDTags;
import io.opentracing.tag.Tags;
public class DBStatementAsResourceName extends DDSpanContextDecorator {
public class DBStatementAsResourceName extends AbstractDecorator {
public DBStatementAsResourceName() {
super();
this.setMatchingTag(Tags.DB_STATEMENT.getKey());
this.setSetTag(DDTags.RESOURCE_NAME);
}
//{ "insert" : "calls", "ordered" : true, "documents" : [{ "_id" : { "$oid" : "5979bbb0ed6fed5749cc9e7c" }, "name" : "MongoDB", "type" : "database", "identifier" : "10", "versions" : ["v3.2", "v3.0", "v2.6"], "info" : { "x" : 203, "y" : 102 } }] }
private void normalizeFilter(final Object f) {}
}
/*
def normalize_filter(f=None):
if f is None:
return {}
elif isinstance(f, list):
# normalize lists of filters
# e.g. {$or: [ { age: { $lt: 30 } }, { type: 1 } ]}
return [normalize_filter(s) for s in f]
elif isinstance(f, dict):
# normalize dicts of filters
# {$or: [ { age: { $lt: 30 } }, { type: 1 } ]})
out = {}
for k, v in iteritems(f):
if k == "$in" or k == "$nin":
# special case $in queries so we don't loop over lists.
out[k] = "?"
elif isinstance(v, list) or isinstance(v, dict):
# RECURSION ALERT: needs to move to the agent
out[k] = normalize_filter(v)
else:
# NOTE: this shouldn't happen, but let's have a safeguard.
out[k] = '?'
return out
else:
# FIXME[matt] unexpected type. not sure this should ever happen, but at
# least it won't crash.
return {}*/

View File

@ -8,9 +8,9 @@ import io.opentracing.tag.Tags;
* This span decorator leverages DB tags. It allows the dev to define a custom service name and
* retrieves some DB meta such as the statement
*/
public class DBComponent extends DDSpanContextDecorator {
public class DBTypeDecorator extends AbstractDecorator {
public DBComponent() {
public DBTypeDecorator() {
super();
this.setMatchingTag(Tags.DB_TYPE.getKey());
this.setSetTag(DDTags.SERVICE_NAME);
@ -22,6 +22,8 @@ public class DBComponent extends DDSpanContextDecorator {
if (super.afterSetTag(context, tag, value)) {
//Assign span type to DB
context.setSpanType("db");
// Works for: mongo, cassandra, jdbc
context.setOperationName(String.valueOf(value) + ".query");
return true;
}
return false;

View File

@ -3,18 +3,18 @@ package com.datadoghq.trace.integration;
import com.datadoghq.trace.DDSpanContext;
import io.opentracing.tag.Tags;
public class ErrorFlag extends DDSpanContextDecorator {
public class ErrorFlag extends AbstractDecorator {
public ErrorFlag() {
super();
this.setMatchingTag(Tags.ERROR.getKey());
}
@Override
public boolean afterSetTag(DDSpanContext context, String tag, Object value) {
public boolean afterSetTag(final DDSpanContext context, final String tag, final Object value) {
//Assign resource name
try {
context.setErrorFlag(Boolean.parseBoolean(String.valueOf(value)));
} catch (Throwable t) {
} catch (final Throwable t) {
//DO NOTHING
}
return true;

View File

@ -8,7 +8,7 @@ import io.opentracing.tag.Tags;
* This span decorator leverages HTTP tags. It allows the dev to define a custom service name and
* retrieves some HTTP meta such as the request path
*/
public class HTTPComponent extends DDSpanContextDecorator {
public class HTTPComponent extends AbstractDecorator {
public HTTPComponent() {
super();
@ -17,7 +17,7 @@ public class HTTPComponent extends DDSpanContextDecorator {
}
@Override
public boolean afterSetTag(DDSpanContext context, String tag, Object value) {
public boolean afterSetTag(final DDSpanContext context, final String tag, final Object value) {
//Assign service name
if (super.afterSetTag(context, tag, value)) {
//Assign span type to WEB

View File

@ -0,0 +1,43 @@
package com.datadoghq.trace.integration;
import com.datadoghq.trace.DDSpanContext;
import io.opentracing.tag.Tags;
import java.util.HashMap;
import java.util.Map;
/**
* This span decorator is a simple mapping to override the operation DB tags. The operation name of
* DB integration are handled by the DBTypeDecorator
*/
public class OperationDecorator extends AbstractDecorator {
static final Map<String, String> MAPPINGS =
new HashMap<String, String>() {
{
// Component name <> Operation name
put("apache-httpclient", "apache.http");
put("java-aws-sdk", "aws.http");
// Jetty + Tomcat (same integration used)
put("java-web-servlet", "servlet.request");
// FIXME: JMS ops card is low (jms-send or jms-receive), may be this mapping is useless
put("java-jms", "jms");
put("okhttp", "okhttp.http");
// Cassandra, Mongo, JDBC are set via DBTypeDecorator
}
};
public OperationDecorator() {
super();
this.setMatchingTag(Tags.COMPONENT.getKey());
}
@Override
public boolean afterSetTag(final DDSpanContext context, final String tag, final Object value) {
if (MAPPINGS.containsKey(String.valueOf(value))) {
context.setOperationName(MAPPINGS.get(String.valueOf(value)));
return true;
}
return false;
}
}

View File

@ -5,7 +5,7 @@ import com.datadoghq.trace.DDTags;
import io.opentracing.tag.Tags;
import java.net.MalformedURLException;
public class URLAsResourceName extends DDSpanContextDecorator {
public class URLAsResourceName extends AbstractDecorator {
public URLAsResourceName() {
super();
@ -14,12 +14,12 @@ public class URLAsResourceName extends DDSpanContextDecorator {
}
@Override
public boolean afterSetTag(DDSpanContext context, String tag, Object value) {
public boolean afterSetTag(final DDSpanContext context, final String tag, final Object value) {
//Assign resource name
try {
String path = new java.net.URL(String.valueOf(value)).getPath();
final String path = new java.net.URL(String.valueOf(value)).getPath();
context.setResourceName(path);
} catch (MalformedURLException e) {
} catch (final MalformedURLException e) {
context.setResourceName(String.valueOf(value));
}
return true;

View File

@ -1,6 +1,6 @@
package com.datadoghq.trace.resolver;
import com.datadoghq.trace.integration.DDSpanContextDecorator;
import com.datadoghq.trace.integration.AbstractDecorator;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
@ -9,9 +9,8 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DDDecoratorsFactory {
public static String DECORATORS_PACKAGE = "com.datadoghq.trace.integration.";
public static final String CONFIG_PATH = "dd-trace-decorators";
public static String DECORATORS_PACKAGE = "com.datadoghq.trace.integration.";
/**
* Create decorators from configuration
@ -19,9 +18,8 @@ public class DDDecoratorsFactory {
* @param decoratorsConfig
* @return the list of instanciated and configured decorators
*/
public static List<DDSpanContextDecorator> create(
final List<DDSpanDecoratorConfig> decoratorsConfig) {
final List<DDSpanContextDecorator> decorators = new ArrayList<>();
public static List<AbstractDecorator> create(final List<DDSpanDecoratorConfig> decoratorsConfig) {
final List<AbstractDecorator> decorators = new ArrayList<>();
for (final DDSpanDecoratorConfig decoratorConfig : decoratorsConfig) {
if (decoratorConfig.getType() == null) {
log.warn("Cannot create decorator without type from configuration {}", decoratorConfig);
@ -39,9 +37,9 @@ public class DDDecoratorsFactory {
continue;
}
DDSpanContextDecorator decorator = null;
AbstractDecorator decorator = null;
try {
decorator = (DDSpanContextDecorator) decoratorClass.getConstructor().newInstance();
decorator = (AbstractDecorator) decoratorClass.getConstructor().newInstance();
} catch (final Exception e) {
log.warn(
"Cannot create decorator as we could not invoke the default constructor. Provided configuration {}",
@ -68,8 +66,8 @@ public class DDDecoratorsFactory {
return decorators;
}
public static List<DDSpanContextDecorator> createFromResources() {
List<DDSpanContextDecorator> result = new ArrayList<>();
public static List<AbstractDecorator> createFromResources() {
List<AbstractDecorator> result = new ArrayList<>();
final TracerConfig config =
FactoryUtils.loadConfigFromResource(CONFIG_PATH, TracerConfig.class);
if (config != null) {

View File

@ -1,7 +1,7 @@
package com.datadoghq.trace.resolver;
import com.datadoghq.trace.DDTracer;
import com.datadoghq.trace.integration.DDSpanContextDecorator;
import com.datadoghq.trace.integration.AbstractDecorator;
import com.datadoghq.trace.sampling.AbstractSampler;
import com.datadoghq.trace.sampling.AllSampler;
import com.datadoghq.trace.sampling.RateSampler;
@ -102,8 +102,8 @@ public class DDTracerFactory {
}
//Create decorators from resource files
final List<DDSpanContextDecorator> decorators = DDDecoratorsFactory.createFromResources();
for (final DDSpanContextDecorator decorator : decorators) {
final List<AbstractDecorator> decorators = DDDecoratorsFactory.createFromResources();
for (final AbstractDecorator decorator : decorators) {
tracer.addDecorator(decorator);
}

View File

@ -4,9 +4,9 @@ decorators:
- type: HTTPComponent
matchingValue: java-okhttp
setValue: http-client
- type: DBComponent
- type: HTTPComponent
matchingValue: java-aws-sdk
setValue: aws-client
# - type: DBStatementAsResourceName
- type: ErrorFlag
- type: DBTypeDecorator
- type: OperationDecorator

View File

@ -4,6 +4,7 @@ import com.datadoghq.trace.DDSpanContext
import com.datadoghq.trace.DDTracer
import com.datadoghq.trace.SpanFactory
import io.opentracing.tag.StringTag
import io.opentracing.tag.Tags
import spock.lang.Specification
class SpanDecoratorTest extends Specification {
@ -11,7 +12,7 @@ class SpanDecoratorTest extends Specification {
def "adding span personalisation using Decorators"() {
setup:
def tracer = new DDTracer()
def decorator = new DDSpanContextDecorator() {
def decorator = new AbstractDecorator() {
@Override
boolean afterSetTag(DDSpanContext context, String tag, Object value) {
@ -32,4 +33,44 @@ class SpanDecoratorTest extends Specification {
span.getTags().containsKey("newFoo")
span.getTags().get("newFoo") == "newBar"
}
def "override operation with OperationDecorator"() {
setup:
def tracer = new DDTracer()
def span = SpanFactory.newSpanOf(tracer)
tracer.addDecorator(new OperationDecorator())
when:
Tags.COMPONENT.set(span, component)
then:
span.getOperationName() == operationName
where:
component << OperationDecorator.MAPPINGS.keySet()
operationName << OperationDecorator.MAPPINGS.values()
}
def "override operation with DBTypeDecorator"() {
setup:
def tracer = new DDTracer()
def span = SpanFactory.newSpanOf(tracer)
tracer.addDecorator(new DBTypeDecorator())
when:
Tags.DB_TYPE.set(span, type)
then:
span.getOperationName() == type + ".query"
span.context().getSpanType() == "db"
where:
type = "foo"
}
}

View File

@ -3,7 +3,7 @@ package com.datadoghq.trace.resolver;
import static org.assertj.core.api.Assertions.assertThat;
import com.datadoghq.trace.DDTracer;
import com.datadoghq.trace.integration.DDSpanContextDecorator;
import com.datadoghq.trace.integration.AbstractDecorator;
import com.datadoghq.trace.integration.HTTPComponent;
import com.datadoghq.trace.integration.URLAsResourceName;
import io.opentracing.NoopTracerFactory;
@ -19,17 +19,16 @@ public class TracerResolverTest {
@Test
public void testResolve() {
DDTracerResolver tracerResolver = new DDTracerResolver();
DDTracer tracer = (DDTracer) tracerResolver.resolve();
final DDTracerResolver tracerResolver = new DDTracerResolver();
final DDTracer tracer = (DDTracer) tracerResolver.resolve();
// for HTTP decorators
List<DDSpanContextDecorator> decorators =
tracer.getSpanContextDecorators(Tags.COMPONENT.getKey());
List<AbstractDecorator> decorators = tracer.getSpanContextDecorators(Tags.COMPONENT.getKey());
assertThat(decorators.size()).isEqualTo(2);
DDSpanContextDecorator decorator = decorators.get(0);
AbstractDecorator decorator = decorators.get(0);
assertThat(decorator.getClass()).isEqualTo(HTTPComponent.class);
HTTPComponent httpServiceDecorator = (HTTPComponent) decorator;
final HTTPComponent httpServiceDecorator = (HTTPComponent) decorator;
assertThat(httpServiceDecorator.getMatchingTag()).isEqualTo("component");
assertThat(httpServiceDecorator.getMatchingValue()).isEqualTo("hello");
assertThat(httpServiceDecorator.getSetValue()).isEqualTo("world");
@ -44,13 +43,13 @@ public class TracerResolverTest {
@Test
public void testResolveTracer() throws Exception {
Field tracerField = GlobalTracer.class.getDeclaredField("tracer");
final Field tracerField = GlobalTracer.class.getDeclaredField("tracer");
tracerField.setAccessible(true);
tracerField.set(null, NoopTracerFactory.create());
assertThat(GlobalTracer.isRegistered()).isFalse();
Tracer tracer = TracerResolver.resolveTracer();
final Tracer tracer = TracerResolver.resolveTracer();
assertThat(GlobalTracer.isRegistered()).isFalse();
assertThat(tracer).isInstanceOf(DDTracer.class);
@ -58,7 +57,7 @@ public class TracerResolverTest {
@Test
public void testRegisterTracer() throws Exception {
Field tracerField = GlobalTracer.class.getDeclaredField("tracer");
final Field tracerField = GlobalTracer.class.getDeclaredField("tracer");
tracerField.setAccessible(true);
tracerField.set(null, NoopTracerFactory.create());