Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	src/main/java/com/datadoghq/trace/impl/DDSpan.java
#	src/main/java/com/datadoghq/trace/writer/impl/DDApi.java
#	src/test/java/com/datadoghq/trace/impl/DDSpanTest.java
This commit is contained in:
Guillaume Polaert 2017-04-27 09:45:37 +02:00
commit d364d10008
6 changed files with 234 additions and 186 deletions

View File

@ -1,10 +1,17 @@
package com.datadoghq.trace;
import java.util.List;
import io.opentracing.Span;
public interface Writer {
public void write(Span span);
/**
* Write a trace represented by the entire list of all the finished spans
*
* @param trace
*/
public void write(List<Span> trace);
public void close();
}

View File

@ -1,190 +1,193 @@
package com.datadoghq.trace.impl;
import java.time.Clock;
import java.util.*;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.opentracing.Span;
import io.opentracing.SpanContext;
public class DDSpan implements io.opentracing.Span {
protected final Tracer tracer;
protected String operationName;
protected Map<String, Object> tags;
protected long startTimeNano;
protected long durationNano;
protected final DDSpanContext context;
protected final ArrayList<Span> trace;
protected final Tracer tracer;
protected String operationName;
protected Map<String, Object> tags;
protected long startTime;
protected long startTimeNano; // Only used to measure nano time durations
protected long durationNano;
protected final DDSpanContext context;
protected final LinkedHashSet<Span> traces;
DDSpan(
Tracer tracer,
String operationName,
ArrayList<Span> trace,
Map<String, Object> tags,
Long timestampMilliseconds,
DDSpanContext context) {
DDSpan(
Tracer tracer,
String operationName,
LinkedHashSet<Span> traces,
Map<String, Object> tags,
Long timestamp,
DDSpanContext context) {
this.tracer = tracer;
this.operationName = operationName;
this.trace = Optional.ofNullable(trace).orElse(new ArrayList<>());
this.tags = tags;
this.startTimeNano = Optional.ofNullable(timestampMilliseconds).orElse(Clock.systemUTC().millis()) * 1000000L;
this.context = context;
this.tracer = tracer;
this.operationName = operationName;
this.traces = Optional.ofNullable(traces).orElse(new LinkedHashSet<>());
this.tags = tags;
this.startTime = System.currentTimeMillis() * 1000000;
this.startTimeNano = System.nanoTime();
this.context = context;
// track each span of the trace
this.trace.add(this);
// track each span of the trace
this.traces.add(this);
}
}
public SpanContext context() {
return this.context;
}
public SpanContext context() {
return this.context;
}
public void finish() {
this.durationNano = System.nanoTime() - startTimeNano;
afterFinish();
}
public void finish() {
finish(Clock.systemUTC().millis());
}
public void finish(long stopTimeMicro) {
this.durationNano = stopTimeMicro * 1000L - startTime;
afterFinish();
}
public void finish(long stopTimeMillis) {
this.durationNano = (stopTimeMillis * 1000000L - startTimeNano);
if (this.isRootSpan()) {
this.trace.stream()
.filter(s -> {
boolean isSelf = ((DDSpanContext) s.context()).getSpanId() == ((DDSpanContext) this.context()).getSpanId();
boolean isFinished = ((DDSpan) s).getDurationNano() != 0L;
return !isSelf && !isFinished;
})
.forEach(Span::finish);
}
}
protected void afterFinish(){
if (this.isRootSpan()) {
this.traces.stream()
.filter(s -> ((DDSpanContext) s.context()).getSpanId() != ((DDSpanContext) this.context()).getSpanId())
.forEach(s -> s.finish());
}
}
public void close() {
this.finish();
}
public void close() {
this.finish();
}
private boolean isRootSpan() {
return context.getTraceId() == context.getSpanId();
}
private boolean isRootSpan() {
return context.getTraceId() == context.getSpanId();
}
public io.opentracing.Span setTag(String tag, String value) {
return this.setTag(tag, value);
}
public io.opentracing.Span setTag(String tag, String value) {
return this.setTag(tag, value);
}
public Span setTag(String tag, boolean value) {
return this.setTag(tag, value);
}
public Span setTag(String tag, boolean value) {
return this.setTag(tag, value);
}
public Span setTag(String tag, Number value) {
return this.setTag(tag, (Object) value);
}
public Span setTag(String tag, Number value) {
return this.setTag(tag, (Object) value);
}
private Span setTag(String tag, Object value) {
this.tags.put(tag, value);
return this;
}
private Span setTag(String tag, Object value) {
this.tags.put(tag, value);
return this;
}
public Span log(Map<String, ?> map) {
return null;
}
public Span log(Map<String, ?> map) {
return null;
}
public Span log(long l, Map<String, ?> map) {
return null;
}
public Span log(long l, Map<String, ?> map) {
return null;
}
public Span log(String s) {
return null;
}
public Span log(String s) {
return null;
}
public Span log(long l, String s) {
return null;
}
public Span log(long l, String s) {
return null;
}
public Span setBaggageItem(String key, String value) {
this.context.setBaggageItem(key, value);
return this;
}
public Span setBaggageItem(String key, String value) {
this.context.setBaggageItem(key, value);
return this;
}
public String getBaggageItem(String key) {
return this.context.getBaggageItem(key);
}
public String getBaggageItem(String key) {
return this.context.getBaggageItem(key);
}
public Span setOperationName(String operationName) {
// FIXME: @renaud, the operationName (mandatory) is always set by the constructor
// FIXME: should be an UnsupportedOperation if we don't want to update the operationName + final
if (this.operationName != null) {
throw new IllegalArgumentException("The operationName is already assigned.");
}
this.operationName = operationName;
return this;
}
public Span setOperationName(String operationName) {
// FIXME: @renaud, the operationName (mandatory) is always set by the constructor
// FIXME: should be an UnsupportedOperation if we don't want to update the operationName + final
if (this.operationName != null) {
throw new IllegalArgumentException("The operationName is already assigned.");
}
this.operationName = operationName;
return this;
}
public Span log(String s, Object o) {
return null;
}
public Span log(String s, Object o) {
return null;
}
public Span log(long l, String s, Object o) {
return null;
}
public Span log(long l, String s, Object o) {
return null;
}
//Getters and JSON serialisation instructions
//Getters and JSON serialisation instructions
@JsonGetter(value = "name")
public String getOperationName() {
return operationName;
}
@JsonGetter(value = "name")
public String getOperationName() {
return operationName;
}
@JsonGetter(value = "meta")
public Map<String, Object> getTags() {
return this.tags;
}
@JsonGetter(value = "meta")
public Map<String, Object> getTags() {
return this.tags;
}
@JsonGetter(value = "start")
public long getStartTime() {
return startTimeNano;
}
@JsonGetter(value = "start")
public long getStartTime() {
return startTime;
}
@JsonGetter(value = "duration")
public long getDurationNano() {
return durationNano;
}
@JsonGetter(value = "duration")
public long getDurationNano() {
return durationNano;
}
public String getService() {
return context.getServiceName();
}
public String getService() {
return context.getServiceName();
}
@JsonGetter(value = "trace_id")
public long getTraceId() {
return context.getTraceId();
}
@JsonGetter(value = "trace_id")
public long getTraceId() {
return context.getTraceId();
}
@JsonGetter(value = "span_id")
public long getSpanId() {
return context.getSpanId();
}
@JsonGetter(value = "span_id")
public long getSpanId() {
return context.getSpanId();
}
@JsonGetter(value = "parent_id")
public long getParentId() {
return context.getParentId();
}
@JsonGetter(value = "parent_id")
public long getParentId() {
return context.getParentId();
}
@JsonGetter(value = "resource")
public String getResourceName() {
return context.getResourceName() == null ? getOperationName() : context.getResourceName();
}
@JsonGetter(value = "resource")
public String getResourceName() {
return context.getResourceName() == null ? getOperationName() : context.getResourceName();
}
public String getType() {
return context.getSpanType();
}
public String getType() {
return context.getSpanType();
}
public int getError() {
return context.getErrorFlag() ? 1 : 0;
}
public int getError() {
return context.getErrorFlag() ? 1 : 0;
}
@JsonIgnore
public ArrayList<Span> getTrace() {
return trace;
}
@JsonIgnore
public LinkedHashSet<Span> getTraces() {
return traces;
}
}

View File

@ -130,12 +130,12 @@ public class Tracer implements io.opentracing.Tracer {
p.getTraceId(),
generatedId,
p.getSpanId(),
Optional.ofNullable(p.getServiceName()).orElse(this.serviceName),
Optional.ofNullable(this.serviceName).orElse(p.getServiceName()),
Optional.ofNullable(this.resourceName).orElse(this.operationName),
p.getBaggageItems(),
errorFlag,
null,
this.spanType,
Optional.ofNullable(this.spanType).orElse(p.getSpanType()),
true
);
} else {

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Semaphore;
import com.datadoghq.trace.Writer;
@ -13,58 +14,90 @@ public class DDAgentWriter implements Writer {
protected static final String DEFAULT_HOSTNAME = "localhost";
protected static final int DEFAULT_PORT = 8126;
protected static final int DEFAULT_MAX_TRACES = 1000;
protected static final int DEFAULT_MAX_SPANS = 1000;
protected static final int DEFAULT_BATCH_SIZE = 10;
protected static final int DEFAULT_MAX_SERVICES = 1000;
protected static final long DEFAULT_TIMEOUT = 5000;
protected final BlockingQueue<Span> commandQueue;
private final Semaphore tokens;
protected final BlockingQueue<List<Span>> traces;
protected final Thread asyncWriterThread;
protected final DDApi api;
public DDAgentWriter() {
super();
commandQueue = new ArrayBlockingQueue<Span>(DEFAULT_MAX_TRACES);
tokens = new Semaphore(DEFAULT_MAX_SPANS);
traces = new ArrayBlockingQueue<List<Span>>(DEFAULT_MAX_SPANS);
api = new DDApi(DEFAULT_HOSTNAME, DEFAULT_PORT);
asyncWriterThread = new Thread(new SpansSendingTask(), "dd.DDAgentWriter-SpansSendingTask");
asyncWriterThread.setDaemon(true);
asyncWriterThread.start();
}
public void write(Span span) {
try{
//Try to add a new span in the queue
commandQueue.add(span);
}catch(IllegalStateException e){
public void write(List<Span> trace) {
//Try to add a new span in the queue
boolean proceed = tokens.tryAcquire(trace.size());
if(proceed){
traces.add(trace);
}else{
//It was not possible to add the span the queue is full!
//FIXME proper logging
System.out.println("Cannot add the following span as the async queue is full: "+span);
System.out.println("Cannot add the following trace as the async queue is full: "+trace);
}
}
public void close() {
asyncWriterThread.interrupt();
try {
asyncWriterThread.join();
} catch (InterruptedException e) {
//Nothing to log as we expect and interrupted exception
}
}
protected class SpansSendingTask implements Runnable {
protected final List<List<Span>> payload = new ArrayList<List<Span>>();
public void run() {
while (true) {
try {
//Wait until a new span comes
Span span = commandQueue.take();
//WAIT until a new span comes
payload.add(traces.take());
//FIXME proper logging
System.out.println("Start writing traces");
//Drain all spans up to a certain batch suze
List<Span> spans = new ArrayList<Span>();
spans.add(span);
commandQueue.drainTo(spans, DEFAULT_BATCH_SIZE);
traces.drainTo(payload, DEFAULT_BATCH_SIZE);
//SEND the payload to the agent
api.sendTraces(payload);
//Compute the number of spans sent
int spansCount = 0;
for(List<Span> trace:payload){
spansCount+=trace.size();
}
//Then write to the agent
System.out.println(spans);
//FIXME proper logging
System.out.println("Sent "+spansCount+" spans through "+payload.size()+" traces");
//Force garbage collect of the payload
payload.clear();
//Release the tokens
tokens.release(spansCount);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
// FIXME proper logging
e.printStackTrace();
//The thread was interrupted, we break the LOOP
break;
}

View File

@ -16,7 +16,7 @@ import io.opentracing.Span;
public class DDApi {
protected static final String TRACES_ENDPOINT = "/v0.3/trace";
protected static final String TRACES_ENDPOINT = "/v0.3/traces";
protected static final String SERVICES_ENDPOINT = "/v0.3/services";
protected final String host;
@ -114,14 +114,12 @@ public class DDApi {
parent.finish();
List<List<Span>> traces = new ArrayList<List<Span>>();
traces.add(array);
DDAgentWriter writer = new DDAgentWriter();
writer.write(array);
DDApi api = new DDApi(DDAgentWriter.DEFAULT_HOSTNAME, DDAgentWriter.DEFAULT_PORT);
Thread.sleep(1000);
// String service = "{\"service_name\": {\"app\": \"service-name\",\"app_type\": \"web\"}}";
// System.out.println("Pushed service: "+api.callPUT(api.servicesEndpoint, service));
System.out.println("Pushed trace: "+api.sendTraces(traces));
writer.close();
}
}

View File

@ -1,35 +1,42 @@
import java.util.ArrayList;
import java.util.List;
import com.datadoghq.trace.Writer;
import com.datadoghq.trace.impl.Tracer;
import com.datadoghq.trace.writer.impl.DDAgentWriter;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.opentracing.Span;
public class Example {
public static void main(String[] args) {
Tracer tracer = new Tracer();
public static void main(String[] args) throws Exception{
List<Span> trace = new ArrayList<Span>();
Tracer tracer = new Tracer();
Writer writer = new DDAgentWriter();
Span parent = tracer
.buildSpan("hello-world")
.withServiceName("service-name")
.withSpanType("web")
.start();
parent.setBaggageItem("a-baggage", "value");
trace.add(parent);
parent.finish();
Tracer.SpanBuilder builder = (Tracer.SpanBuilder) tracer
Span child = tracer
.buildSpan("hello-world")
.asChildOf(parent);
Span child = builder
.withServiceName("service-name")
.start();
.asChildOf(parent)
.withResourceName("resource-name")
.start();
child.finish();
trace.add(child);
parent.finish();
writer.write(trace);
writer.close();
}