-
Notifications
You must be signed in to change notification settings - Fork 4
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
Provide a tracer wrapper that can be used as basis for adding ex… #9
Changes from all commits
8a17e75
d5f1093
af30b2d
f03c2ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!-- | ||
|
||
Copyright 2017 The OpenTracing Authors | ||
|
||
Licensed 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. | ||
|
||
--> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<artifactId>opentracing-api-extensions-parent</artifactId> | ||
<groupId>io.opentracing.contrib</groupId> | ||
<version>0.0.2-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>opentracing-api-extensions-tracer</artifactId> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>io.opentracing.contrib</groupId> | ||
<artifactId>opentracing-api-extensions</artifactId> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.mockito</groupId> | ||
<artifactId>mockito-all</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>junit</groupId> | ||
<artifactId>junit</artifactId> | ||
<version>${version.junit}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
/** | ||
* Copyright 2017 The OpenTracing Authors | ||
* | ||
* Licensed 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 io.opentracing.contrib.api.tracer; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.UUID; | ||
import java.util.concurrent.CopyOnWriteArrayList; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import io.opentracing.Span; | ||
import io.opentracing.SpanContext; | ||
import io.opentracing.contrib.api.SpanData; | ||
import io.opentracing.contrib.api.SpanObserver; | ||
|
||
public class APIExtensionsSpan implements Span, SpanData { | ||
|
||
private final Span wrappedSpan; | ||
|
||
private String operationName; | ||
private final long startTimestampMicro; | ||
private long finishTimestampMicro; | ||
private final long startTimeNano; | ||
private long finishTimeNano; | ||
private final Map<String,Object> tags; | ||
|
||
private final List<SpanObserver> observers = new CopyOnWriteArrayList<SpanObserver>(); | ||
|
||
private final UUID correlationId = UUID.randomUUID(); | ||
|
||
/** | ||
* This is the constructor for the extensions API span wrapper. | ||
* | ||
* @param span The span being wrapped | ||
* @param operationName The operation name | ||
* @param startTimestampMicro The start timestamp (microseconds) | ||
* @param startTimeNano The start nano time, or 0 if the start timestamp was explicitly provided by the app | ||
* @param tags The initial tags | ||
*/ | ||
APIExtensionsSpan(Span span, String operationName, | ||
long startTimestampMicro, long startTimeNano, Map<String,Object> tags) { | ||
this.wrappedSpan = span; | ||
this.operationName = operationName; | ||
this.startTimestampMicro = startTimestampMicro; | ||
this.startTimeNano = startTimeNano; | ||
this.tags = tags; | ||
} | ||
|
||
/** | ||
* This method adds a new {@link SpanObserver}. | ||
* | ||
* @param observer The observer | ||
*/ | ||
public void addSpanObserver(SpanObserver observer) { | ||
if (observer != null) { | ||
observers.add(observer); | ||
} | ||
} | ||
|
||
/** | ||
* This method removes a {@link SpanObserver}. | ||
* | ||
* @param observer The observer | ||
*/ | ||
public void removeSpanObserver(SpanObserver observer) { | ||
if (observer != null) { | ||
observers.remove(observer); | ||
} | ||
} | ||
|
||
@Override | ||
public SpanContext context() { | ||
return wrappedSpan.context(); | ||
} | ||
|
||
@Override | ||
public Object getCorrelationId() { | ||
return correlationId; | ||
} | ||
|
||
@Override | ||
public long getStartTime() { | ||
return startTimestampMicro; | ||
} | ||
|
||
@Override | ||
public long getFinishTime() { | ||
return finishTimestampMicro; | ||
} | ||
|
||
@Override | ||
public Span setOperationName(String operationName) { | ||
wrappedSpan.setOperationName(operationName); | ||
this.operationName = operationName; | ||
for (SpanObserver observer : observers) { | ||
observer.onSetOperationName(this, operationName); | ||
} | ||
return this; | ||
} | ||
|
||
@Override | ||
public String getOperationName() { | ||
return operationName; | ||
} | ||
|
||
@Override | ||
public String getBaggageItem(String name) { | ||
return wrappedSpan.getBaggageItem(name); | ||
} | ||
|
||
@Override | ||
public Span setBaggageItem(String name, String value) { | ||
wrappedSpan.setBaggageItem(name, value); | ||
for (SpanObserver observer : observers) { | ||
observer.onSetBaggageItem(this, name, value); | ||
} | ||
return this; | ||
} | ||
|
||
@Override | ||
public Span log(Map<String, ?> fields) { | ||
wrappedSpan.log(fields); | ||
return handleLog(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), fields); | ||
} | ||
|
||
@Override | ||
public Span log(long timestampMicroseconds, Map<String, ?> fields) { | ||
wrappedSpan.log(timestampMicroseconds, fields); | ||
return handleLog(timestampMicroseconds, fields); | ||
} | ||
|
||
private Span handleLog(long timestampMicroseconds, Map<String, ?> fields) { | ||
for (SpanObserver observer : observers) { | ||
observer.onLog(this, timestampMicroseconds, fields); | ||
} | ||
return this; | ||
} | ||
|
||
@Override | ||
public Span log(String event) { | ||
wrappedSpan.log(event); | ||
return handleLog(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), event); | ||
} | ||
|
||
@Override | ||
public Span log(long timestampMicroseconds, String event) { | ||
wrappedSpan.log(timestampMicroseconds, event); | ||
return handleLog(timestampMicroseconds, event); | ||
} | ||
|
||
private Span handleLog(long timestampMicroseconds, String event) { | ||
for (SpanObserver observer : observers) { | ||
observer.onLog(this, timestampMicroseconds, event); | ||
} | ||
return this; | ||
} | ||
|
||
@Override | ||
public Span setTag(String key, String value) { | ||
wrappedSpan.setTag(key, value); | ||
return handleSetTag(key, value); | ||
} | ||
|
||
@Override | ||
public Span setTag(String key, boolean value) { | ||
wrappedSpan.setTag(key, value); | ||
return handleSetTag(key, value); | ||
} | ||
|
||
@Override | ||
public Span setTag(String key, Number value) { | ||
wrappedSpan.setTag(key, value); | ||
return handleSetTag(key, value); | ||
} | ||
|
||
private Span handleSetTag(String key, Object value) { | ||
tags.put(key, value); | ||
for (SpanObserver observer : observers) { | ||
observer.onSetTag(this, key, value); | ||
} | ||
return this; | ||
} | ||
|
||
@Override | ||
public Map<String,Object> getTags() { | ||
return Collections.unmodifiableMap(tags); | ||
} | ||
|
||
@Override | ||
public String getStringTag(String key) { | ||
Object value = tags.get(key); | ||
if (value instanceof String) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rather to a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was discussed in the PR related to the API, and decided that non-string tags should not be returned as strings, as null is used as a way to understand it is not a string type. |
||
return (String) value; | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public Number getNumberTag(String key) { | ||
Object value = tags.get(key); | ||
if (value instanceof Number) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I try to get a "int" from a string value, I would expect an exception, not null (at least, that's what happen in other APIs out there). If returning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/opentracing-contrib/java-api-extensions/blob/master/opentracing-api-extensions/src/main/java/io/opentracing/contrib/api/SpanData.java#L87 |
||
return (Number) value; | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public Boolean getBooleanTag(String key) { | ||
Object value = tags.get(key); | ||
if (value instanceof Boolean) { | ||
return (Boolean) value; | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public void finish() { | ||
wrappedSpan.finish(); | ||
// Only set the finish nano time if not explicitly providing a timestamp | ||
finishTimeNano = System.nanoTime(); | ||
handleFinish(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())); | ||
} | ||
|
||
@Override | ||
public void finish(long finishMicros) { | ||
wrappedSpan.finish(finishMicros); | ||
handleFinish(finishMicros); | ||
} | ||
|
||
private void handleFinish(long finishMicros) { | ||
finishTimestampMicro = finishMicros; | ||
for (SpanObserver observer : observers) { | ||
observer.onFinish(this, finishMicros); | ||
} | ||
} | ||
|
||
@Override | ||
public long getDuration() { | ||
// If start or finish nano times are not available, then use timestamps | ||
if (startTimeNano == 0 || finishTimeNano == 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When this can be 0? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
return finishTimestampMicro == 0 ? 0 : finishTimestampMicro - startTimestampMicro; | ||
} | ||
return TimeUnit.NANOSECONDS.toMicros(finishTimeNano - startTimeNano); | ||
} | ||
|
||
@SuppressWarnings("deprecation") | ||
@Override | ||
public Span log(String eventName, Object payload) { | ||
return wrappedSpan.log(eventName, payload); | ||
} | ||
|
||
@SuppressWarnings("deprecation") | ||
@Override | ||
public Span log(long timestampMicroseconds, String eventName, Object payload) { | ||
return wrappedSpan.log(timestampMicroseconds, eventName, payload); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you replace the
System.currentTimeMillis
with aClock
? This way, you don't lose the micro precision (not to mention that it's far easier to test withClock
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you do change this to use a Clock, there are other places that would need to be changed as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately
Clock
is 1.8, and we are trying to remain 1.6 compatible for now. Hopefully that decision won't last forever.