Allow config to pull from env vars and sys props
This commit is contained in:
parent
d036512318
commit
0fb057dbcc
|
@ -0,0 +1,63 @@
|
|||
package com.datadoghq.trace;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Config gives priority to system properties and falls back to environment variables.
|
||||
*
|
||||
* <p>System properties are {@link DDTraceConfig#PREFIX}'ed. Environment variables are the same as
|
||||
* the system property, but uppercased with '.' -> '_'.
|
||||
*/
|
||||
public class DDTraceConfig extends Properties {
|
||||
/** Config keys bel */
|
||||
private static final String PREFIX = "dd.";
|
||||
|
||||
public static final String SERVICE_NAME = "service.name";
|
||||
public static final String WRITER_TYPE = "writer.type";
|
||||
public static final String AGENT_HOST = "agent.host";
|
||||
public static final String AGENT_PORT = "agent.port";
|
||||
public static final String SAMPLER_TYPE = "sampler.type";
|
||||
public static final String SAMPLER_RATE = "sampler.rate";
|
||||
|
||||
private final String serviceName = getPropOrEnv(PREFIX + SERVICE_NAME);
|
||||
private final String writerType = getPropOrEnv(PREFIX + WRITER_TYPE);
|
||||
private final String agentHost = getPropOrEnv(PREFIX + AGENT_HOST);
|
||||
private final String agentPort = getPropOrEnv(PREFIX + AGENT_PORT);
|
||||
private final String samplerType = getPropOrEnv(PREFIX + SAMPLER_TYPE);
|
||||
private final String samplerRate = getPropOrEnv(PREFIX + SAMPLER_RATE);
|
||||
|
||||
public DDTraceConfig() {
|
||||
super();
|
||||
|
||||
final Properties defaults = new Properties();
|
||||
defaults.setProperty(SERVICE_NAME, DDTracer.UNASSIGNED_DEFAULT_SERVICE_NAME);
|
||||
super.defaults = defaults;
|
||||
|
||||
final Properties baseValues = new Properties(defaults);
|
||||
setIfNotNull(SERVICE_NAME, serviceName);
|
||||
setIfNotNull(WRITER_TYPE, writerType);
|
||||
setIfNotNull(AGENT_HOST, agentHost);
|
||||
setIfNotNull(AGENT_PORT, agentPort);
|
||||
setIfNotNull(SAMPLER_TYPE, samplerType);
|
||||
setIfNotNull(SAMPLER_RATE, samplerRate);
|
||||
}
|
||||
|
||||
public DDTraceConfig(final String serviceName) {
|
||||
super();
|
||||
put(SERVICE_NAME, serviceName);
|
||||
}
|
||||
|
||||
private void setIfNotNull(final String key, final String value) {
|
||||
if (value != null) {
|
||||
setProperty(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private String getPropOrEnv(final String name) {
|
||||
return System.getProperty(name, System.getenv(propToEnvName(name)));
|
||||
}
|
||||
|
||||
static String propToEnvName(final String name) {
|
||||
return name.toUpperCase().replace(".", "_");
|
||||
}
|
||||
}
|
|
@ -3,9 +3,10 @@ package com.datadoghq.trace;
|
|||
import com.datadoghq.trace.integration.AbstractDecorator;
|
||||
import com.datadoghq.trace.propagation.Codec;
|
||||
import com.datadoghq.trace.propagation.HTTPCodec;
|
||||
import com.datadoghq.trace.resolver.DDDecoratorsFactory;
|
||||
import com.datadoghq.trace.sampling.AllSampler;
|
||||
import com.datadoghq.trace.sampling.Sampler;
|
||||
import com.datadoghq.trace.writer.LoggingWriter;
|
||||
import com.datadoghq.trace.writer.DDAgentWriter;
|
||||
import com.datadoghq.trace.writer.Writer;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.opentracing.ActiveSpan;
|
||||
|
@ -19,6 +20,7 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -28,16 +30,16 @@ import lombok.extern.slf4j.Slf4j;
|
|||
public class DDTracer extends ThreadLocalActiveSpanSource implements io.opentracing.Tracer {
|
||||
|
||||
public static final String UNASSIGNED_DEFAULT_SERVICE_NAME = "unnamed-java-app";
|
||||
public static final Writer UNASSIGNED_WRITER = new LoggingWriter();
|
||||
public static final Writer UNASSIGNED_WRITER = new DDAgentWriter();
|
||||
public static final Sampler UNASSIGNED_SAMPLER = new AllSampler();
|
||||
|
||||
/** Writer is an charge of reporting traces and spans to the desired endpoint */
|
||||
private final Writer writer;
|
||||
final Writer writer;
|
||||
/** Sampler defines the sampling policy in order to reduce the number of traces for instance */
|
||||
private final Sampler sampler;
|
||||
final Sampler sampler;
|
||||
|
||||
/** Default service name if none provided on the trace or span */
|
||||
private final String defaultServiceName;
|
||||
final String serviceName;
|
||||
|
||||
/** Span context decorators */
|
||||
private final Map<String, List<AbstractDecorator>> spanContextDecorators = new HashMap<>();
|
||||
|
@ -45,9 +47,39 @@ public class DDTracer extends ThreadLocalActiveSpanSource implements io.opentrac
|
|||
private final CodecRegistry registry;
|
||||
private final Map<String, Service> services = new HashMap<>();
|
||||
|
||||
/** Default constructor, trace/spans are logged, no trace/span dropped */
|
||||
/** By default, report to local agent and collect all traces. */
|
||||
public DDTracer() {
|
||||
this(UNASSIGNED_WRITER);
|
||||
this(new DDTraceConfig());
|
||||
}
|
||||
|
||||
public DDTracer(final String serviceName) {
|
||||
this(new DDTraceConfig(serviceName));
|
||||
}
|
||||
|
||||
public DDTracer(final Properties config) {
|
||||
this(
|
||||
config.getProperty(DDTraceConfig.SERVICE_NAME),
|
||||
Writer.Builder.forConfig(config),
|
||||
Sampler.Builder.forConfig(config));
|
||||
log.debug("Using config: {}", config);
|
||||
|
||||
// Create decorators from resource files
|
||||
final List<AbstractDecorator> decorators = DDDecoratorsFactory.createFromResources();
|
||||
for (final AbstractDecorator decorator : decorators) {
|
||||
log.debug("Loading decorator: {}", decorator.getClass().getSimpleName());
|
||||
addDecorator(decorator);
|
||||
}
|
||||
}
|
||||
|
||||
public DDTracer(final String serviceName, final Writer writer, final Sampler sampler) {
|
||||
this.serviceName = serviceName;
|
||||
this.writer = writer;
|
||||
this.writer.start();
|
||||
this.sampler = sampler;
|
||||
registry = new CodecRegistry();
|
||||
registry.register(Format.Builtin.HTTP_HEADERS, new HTTPCodec());
|
||||
registry.register(Format.Builtin.TEXT_MAP, new HTTPCodec());
|
||||
log.info("New instance: {}", this);
|
||||
}
|
||||
|
||||
public DDTracer(final Writer writer) {
|
||||
|
@ -58,21 +90,6 @@ public class DDTracer extends ThreadLocalActiveSpanSource implements io.opentrac
|
|||
this(UNASSIGNED_DEFAULT_SERVICE_NAME, writer, sampler);
|
||||
}
|
||||
|
||||
public DDTracer(final String defaultServiceName, final Writer writer, final Sampler sampler) {
|
||||
this.defaultServiceName = defaultServiceName;
|
||||
this.writer = writer;
|
||||
this.writer.start();
|
||||
this.sampler = sampler;
|
||||
registry = new CodecRegistry();
|
||||
registry.register(Format.Builtin.HTTP_HEADERS, new HTTPCodec());
|
||||
registry.register(Format.Builtin.TEXT_MAP, new HTTPCodec());
|
||||
log.debug(
|
||||
"New tracer instance, default-service={}, writer={}, sampler={}",
|
||||
defaultServiceName,
|
||||
writer.getClass().getSimpleName(),
|
||||
sampler.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of span context decorators
|
||||
*
|
||||
|
@ -147,7 +164,15 @@ public class DDTracer extends ThreadLocalActiveSpanSource implements io.opentrac
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DDTracer{" + "writer=" + writer + ", sampler=" + sampler + '}';
|
||||
return "DDTracer-"
|
||||
+ Integer.toHexString(hashCode())
|
||||
+ "{ service-name="
|
||||
+ serviceName
|
||||
+ ", writer="
|
||||
+ writer
|
||||
+ ", sampler="
|
||||
+ sampler
|
||||
+ '}';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -368,7 +393,7 @@ public class DDTracer extends ThreadLocalActiveSpanSource implements io.opentrac
|
|||
}
|
||||
|
||||
if (serviceName == null) {
|
||||
serviceName = defaultServiceName;
|
||||
serviceName = DDTracer.this.serviceName;
|
||||
}
|
||||
|
||||
final String operationName =
|
||||
|
|
|
@ -33,7 +33,7 @@ public class DDTracerFactory {
|
|||
* @param config
|
||||
* @return the corresponding tracer
|
||||
*/
|
||||
public static DDTracer create(final TracerConfig config) {
|
||||
static DDTracer create(final TracerConfig config) {
|
||||
final String defaultServiceName =
|
||||
config.getDefaultServiceName() != null
|
||||
? config.getDefaultServiceName()
|
||||
|
@ -88,11 +88,15 @@ public class DDTracerFactory {
|
|||
return new DDTracer(defaultServiceName, writer, sampler);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static DDTracer createFromConfigurationFile() {
|
||||
final TracerConfig tracerConfig =
|
||||
FactoryUtils.loadConfigFromFilePropertyOrResource(
|
||||
SYSTEM_PROPERTY_CONFIG_PATH, CONFIG_PATH, TracerConfig.class);
|
||||
|
||||
log.warn("DDTracerFactory is deprecated and will be removed in the next release.");
|
||||
log.warn("Use the constructor on DDTrace directly and env vars/sys props for config.");
|
||||
|
||||
DDTracer tracer = null;
|
||||
log.trace("Tracer configuration: \n{}", tracerConfig);
|
||||
if (tracerConfig == null) {
|
||||
|
|
|
@ -29,11 +29,16 @@ public class DDTracerResolver extends TracerResolver {
|
|||
protected Tracer resolve() {
|
||||
log.info("Creating the Datadog Tracer from the resolver");
|
||||
|
||||
// Find a resource file named dd-trace.yml
|
||||
DDTracer tracer = null;
|
||||
// Create tracer from resource files
|
||||
tracer = DDTracerFactory.createFromConfigurationFile();
|
||||
final TracerConfig tracerConfig =
|
||||
FactoryUtils.loadConfigFromFilePropertyOrResource(
|
||||
DDTracerFactory.SYSTEM_PROPERTY_CONFIG_PATH,
|
||||
DDTracerFactory.CONFIG_PATH,
|
||||
TracerConfig.class);
|
||||
|
||||
return tracer;
|
||||
if (tracerConfig != null) {
|
||||
return DDTracerFactory.createFromConfigurationFile();
|
||||
} else {
|
||||
return new DDTracer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ public class RateSampler extends AbstractSampler {
|
|||
/** The sample rate used */
|
||||
private final double sampleRate;
|
||||
|
||||
public RateSampler(final String sampleRate) {
|
||||
this(sampleRate == null ? 1 : Double.valueOf(sampleRate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an instance of the sampler. The Sample rate is fixed for each instance.
|
||||
*
|
||||
|
@ -45,4 +49,9 @@ public class RateSampler extends AbstractSampler {
|
|||
public double getSampleRate() {
|
||||
return this.sampleRate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RateSampler { sampleRate=" + sampleRate + " }";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
package com.datadoghq.trace.sampling;
|
||||
|
||||
import com.datadoghq.trace.DDBaseSpan;
|
||||
import com.datadoghq.trace.DDTraceConfig;
|
||||
import com.datadoghq.trace.DDTracer;
|
||||
import java.util.Properties;
|
||||
|
||||
/** Main interface to sample a collection of traces. */
|
||||
public interface Sampler {
|
||||
static final String ALL_SAMPLER_TYPE = AllSampler.class.getSimpleName();
|
||||
static final String RATE_SAMPLER_TYPE = RateSampler.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* Sample a collection of traces based on the parent span
|
||||
|
@ -12,4 +17,26 @@ public interface Sampler {
|
|||
* @return true when the trace/spans has to be reported/written
|
||||
*/
|
||||
boolean sample(DDBaseSpan<?> span);
|
||||
|
||||
final class Builder {
|
||||
public static Sampler forConfig(final Properties config) {
|
||||
final Sampler sampler;
|
||||
|
||||
if (config != null) {
|
||||
final String configuredType = config.getProperty(DDTraceConfig.SAMPLER_TYPE);
|
||||
if (RATE_SAMPLER_TYPE.equals(configuredType)) {
|
||||
sampler = new RateSampler(config.getProperty(DDTraceConfig.SAMPLER_RATE));
|
||||
} else if (ALL_SAMPLER_TYPE.equals(configuredType)) {
|
||||
sampler = new AllSampler();
|
||||
} else {
|
||||
sampler = DDTracer.UNASSIGNED_SAMPLER;
|
||||
}
|
||||
} else {
|
||||
sampler = DDTracer.UNASSIGNED_SAMPLER;
|
||||
}
|
||||
return sampler;
|
||||
}
|
||||
|
||||
private Builder() {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,6 +140,11 @@ public class DDAgentWriter implements Writer {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DDAgentWriter { api=" + api + " }";
|
||||
}
|
||||
|
||||
/** Infinite tasks blocking until some spans come in the blocking queue. */
|
||||
class TracesSendingTask implements Runnable {
|
||||
|
||||
|
|
|
@ -126,4 +126,9 @@ public class DDApi {
|
|||
httpCon.setRequestProperty("Datadog-Meta-Tracer-Version", DDTraceInfo.VERSION);
|
||||
return httpCon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DDApi { tracesEndpoint=" + tracesEndpoint + " }";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
package com.datadoghq.trace.writer;
|
||||
|
||||
import com.datadoghq.trace.DDBaseSpan;
|
||||
import com.datadoghq.trace.DDTraceConfig;
|
||||
import com.datadoghq.trace.DDTracer;
|
||||
import com.datadoghq.trace.Service;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/** A writer is responsible to send collected spans to some place */
|
||||
public interface Writer {
|
||||
static final String DD_AGENT_WRITER_TYPE = DDAgentWriter.class.getSimpleName();
|
||||
static final String LOGGING_WRITER_TYPE = LoggingWriter.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* Write a trace represented by the entire list of all the finished spans
|
||||
|
@ -30,4 +35,31 @@ public interface Writer {
|
|||
* connections and tasks
|
||||
*/
|
||||
void close();
|
||||
|
||||
final class Builder {
|
||||
public static Writer forConfig(final Properties config) {
|
||||
final Writer writer;
|
||||
|
||||
if (config != null) {
|
||||
final String configuredType = config.getProperty(DDTraceConfig.WRITER_TYPE);
|
||||
if (DD_AGENT_WRITER_TYPE.equals(configuredType)) {
|
||||
writer =
|
||||
new DDAgentWriter(
|
||||
new DDApi(
|
||||
config.getProperty(DDTraceConfig.AGENT_HOST),
|
||||
Integer.parseInt(config.getProperty(DDTraceConfig.AGENT_PORT))));
|
||||
} else if (LOGGING_WRITER_TYPE.equals(configuredType)) {
|
||||
writer = new LoggingWriter();
|
||||
} else {
|
||||
writer = DDTracer.UNASSIGNED_WRITER;
|
||||
}
|
||||
} else {
|
||||
writer = DDTracer.UNASSIGNED_WRITER;
|
||||
}
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
private Builder() {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
package com.datadoghq.trace
|
||||
|
||||
import com.datadoghq.trace.sampling.AllSampler
|
||||
import com.datadoghq.trace.sampling.RateSampler
|
||||
import com.datadoghq.trace.writer.DDAgentWriter
|
||||
import com.datadoghq.trace.writer.ListWriter
|
||||
import com.datadoghq.trace.writer.LoggingWriter
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
import static com.datadoghq.trace.DDTraceConfig.*
|
||||
|
||||
class DDTraceConfigTest extends Specification {
|
||||
static originalEnvMap
|
||||
static overrideEnvMap = new HashMap<String, String>()
|
||||
|
||||
def setupSpec() {
|
||||
def envMapField = ProcessEnvironment.getDeclaredField("theUnmodifiableEnvironment")
|
||||
envMapField.setAccessible(true)
|
||||
|
||||
Field modifiersField = Field.getDeclaredField("modifiers")
|
||||
modifiersField.setAccessible(true)
|
||||
modifiersField.setInt(envMapField, envMapField.getModifiers() & ~Modifier.FINAL)
|
||||
|
||||
originalEnvMap = envMapField.get(null)
|
||||
overrideEnvMap.putAll(originalEnvMap)
|
||||
envMapField.set(null, overrideEnvMap)
|
||||
}
|
||||
|
||||
def cleanupSpec() {
|
||||
def envMapField = ProcessEnvironment.getDeclaredField("theUnmodifiableEnvironment")
|
||||
envMapField.setAccessible(true)
|
||||
|
||||
Field modifiersField = Field.getDeclaredField("modifiers")
|
||||
modifiersField.setAccessible(true)
|
||||
modifiersField.setInt(envMapField, envMapField.getModifiers() & ~Modifier.FINAL)
|
||||
|
||||
originalEnvMap = envMapField.get(null)
|
||||
envMapField.set(null, originalEnvMap)
|
||||
}
|
||||
|
||||
def setup() {
|
||||
overrideEnvMap.clear()
|
||||
overrideEnvMap.putAll(originalEnvMap)
|
||||
|
||||
System.clearProperty(PREFIX + SERVICE_NAME)
|
||||
System.clearProperty(PREFIX + WRITER_TYPE)
|
||||
System.clearProperty(PREFIX + AGENT_HOST)
|
||||
System.clearProperty(PREFIX + AGENT_PORT)
|
||||
System.clearProperty(PREFIX + SAMPLER_TYPE)
|
||||
System.clearProperty(PREFIX + SAMPLER_RATE)
|
||||
}
|
||||
|
||||
def "verify env override"() {
|
||||
setup:
|
||||
overrideEnvMap.put("SOME_RANDOM_ENTRY", "asdf")
|
||||
|
||||
expect:
|
||||
System.getenv("SOME_RANDOM_ENTRY") == "asdf"
|
||||
}
|
||||
|
||||
def "verify defaults"() {
|
||||
when:
|
||||
def config = new DDTraceConfig()
|
||||
|
||||
then:
|
||||
config.getProperty(SERVICE_NAME) == "unnamed-java-app"
|
||||
config.getProperty(WRITER_TYPE) == null
|
||||
config.getProperty(AGENT_HOST) == null
|
||||
config.getProperty(AGENT_PORT) == null
|
||||
config.getProperty(SAMPLER_TYPE) == null
|
||||
config.getProperty(SAMPLER_RATE) == null
|
||||
|
||||
when:
|
||||
config = new DDTraceConfig("A different service name")
|
||||
|
||||
then:
|
||||
config.getProperty(SERVICE_NAME) == "A different service name"
|
||||
config.getProperty(WRITER_TYPE) == null
|
||||
config.getProperty(AGENT_HOST) == null
|
||||
config.getProperty(AGENT_PORT) == null
|
||||
config.getProperty(SAMPLER_TYPE) == null
|
||||
config.getProperty(SAMPLER_RATE) == null
|
||||
}
|
||||
|
||||
def "specify overrides via system properties"() {
|
||||
when:
|
||||
System.setProperty(PREFIX + SERVICE_NAME, "something else")
|
||||
System.setProperty(PREFIX + WRITER_TYPE, LoggingWriter.simpleName)
|
||||
System.setProperty(PREFIX + SAMPLER_TYPE, RateSampler.simpleName)
|
||||
System.setProperty(PREFIX + SAMPLER_RATE, ".5")
|
||||
def tracer = new DDTracer()
|
||||
|
||||
then:
|
||||
tracer.serviceName == "something else"
|
||||
tracer.writer instanceof LoggingWriter
|
||||
tracer.sampler.toString() == "RateSampler { sampleRate=0.5 }"
|
||||
}
|
||||
|
||||
def "specify overrides via env vars"() {
|
||||
when:
|
||||
overrideEnvMap.put(propToEnvName(PREFIX + SERVICE_NAME), "still something else")
|
||||
overrideEnvMap.put(propToEnvName(PREFIX + WRITER_TYPE), LoggingWriter.simpleName)
|
||||
overrideEnvMap.put(propToEnvName(PREFIX + SAMPLER_TYPE), AllSampler.simpleName)
|
||||
def tracer = new DDTracer()
|
||||
|
||||
then:
|
||||
tracer.serviceName == "still something else"
|
||||
tracer.writer instanceof LoggingWriter
|
||||
tracer.sampler instanceof AllSampler
|
||||
}
|
||||
|
||||
def "sys props override env vars"() {
|
||||
when:
|
||||
overrideEnvMap.put(propToEnvName(PREFIX + SERVICE_NAME), "still something else")
|
||||
overrideEnvMap.put(propToEnvName(PREFIX + WRITER_TYPE), ListWriter.simpleName)
|
||||
overrideEnvMap.put(propToEnvName(PREFIX + SAMPLER_TYPE), AllSampler.simpleName)
|
||||
|
||||
System.setProperty(PREFIX + SERVICE_NAME, "what we actually want")
|
||||
System.setProperty(PREFIX + WRITER_TYPE, DDAgentWriter.simpleName)
|
||||
System.setProperty(PREFIX + AGENT_HOST, "somewhere")
|
||||
System.setProperty(PREFIX + AGENT_PORT, "9999")
|
||||
System.setProperty(PREFIX + SAMPLER_TYPE, RateSampler.simpleName)
|
||||
System.setProperty(PREFIX + SAMPLER_RATE, ".9")
|
||||
|
||||
def tracer = new DDTracer()
|
||||
|
||||
then:
|
||||
tracer.serviceName == "what we actually want"
|
||||
tracer.writer.toString() == "DDAgentWriter { api=DDApi { tracesEndpoint=http://somewhere:9999/v0.3/traces } }"
|
||||
tracer.sampler.toString() == "RateSampler { sampleRate=0.9 }"
|
||||
}
|
||||
|
||||
def "verify defaults on tracer"() {
|
||||
when:
|
||||
def tracer = new DDTracer()
|
||||
|
||||
then:
|
||||
tracer.serviceName == "unnamed-java-app"
|
||||
tracer.sampler instanceof AllSampler
|
||||
tracer.writer.toString() == "DDAgentWriter { api=DDApi { tracesEndpoint=http://localhost:8126/v0.3/traces } }"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue