-
Notifications
You must be signed in to change notification settings - Fork 343
An RFC for SpanContext, References, as well as changes to carriers #32
Changes from all commits
0917928
5c32439
3630064
ba3be7b
592a452
a3b54a7
35337f1
392abc5
407854c
2f92013
623db61
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,35 @@ | ||
/** | ||
* Copyright 2016 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; | ||
|
||
/** | ||
* References is essentially a namespace for the official OpenTracing reference types. | ||
* | ||
* References are used by Tracer.buildSpan() to describe the relationships between Spans. | ||
* | ||
* @see io.opentracing.Tracer.SpanBuilder#addReference(Object, SpanContext) | ||
*/ | ||
public final class References { | ||
private References(){} | ||
|
||
/** | ||
* See http://opentracing.io/spec/#causal-span-references for more information about CHILD_OF references | ||
*/ | ||
public static final String CHILD_OF = "child_of"; | ||
|
||
/** | ||
* See http://opentracing.io/spec/#causal-span-references for more information about FOLLOWS_FROM references | ||
*/ | ||
public static final String FOLLOWS_FROM = "follows_from"; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,60 +19,53 @@ | |
* <p>Spans are created by the {@link Tracer#buildSpan} interface. | ||
*/ | ||
public interface Span extends AutoCloseable { | ||
/** | ||
* Retrieve the associated SpanContext. | ||
* | ||
* @return the SpanContext that encapsulates Span state that should propagate across process boundaries. | ||
*/ | ||
SpanContext context(); | ||
|
||
/** | ||
* Sets the end timestamp and records the span. | ||
* | ||
* <p>This should be the last call made to any span instance, and to do otherwise leads to | ||
* undefined behavior. | ||
*/ | ||
void finish(); | ||
/** | ||
* Sets the end timestamp and records the span. | ||
* | ||
* <p>This should be the last call made to any span instance, and to do otherwise leads to | ||
* undefined behavior. | ||
*/ | ||
void finish(); | ||
|
||
void close(); | ||
void close(); | ||
|
||
/** | ||
* Set a key:value tag on the Span. | ||
*/ | ||
// overloaded 3x to support the BasicType concern | ||
Span setTag(String key, String value); | ||
/** | ||
* Set a key:value tag on the Span. | ||
*/ | ||
// overloaded 3x to support the BasicType concern | ||
Span setTag(String key, String value); | ||
|
||
/** Same as {@link #setTag(String, String)}, but for boolean values. */ | ||
Span setTag(String key, boolean value); | ||
/** Same as {@link #setTag(String, String)}, but for boolean values. */ | ||
Span setTag(String key, boolean value); | ||
|
||
/** Same as {@link #setTag(String, String)}, but for numeric values. */ | ||
Span setTag(String key, Number value); | ||
/** Same as {@link #setTag(String, String)}, but for numeric values. */ | ||
Span setTag(String key, Number value); | ||
|
||
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. (removed / moved to SpanContext) |
||
/** | ||
* Set a Baggage item, represented as a simple string:string pair. | ||
* | ||
* Note that newly-set Baggage items are only guaranteed to propagate to future children of the given Span. | ||
*/ | ||
Span setBaggageItem(String key, String value); | ||
/** | ||
* Add a new log event to the Span, accepting an event name string and an optional structured payload argument. | ||
* If specified, the payload argument may be of any type and arbitrary size, | ||
* though implementations are not required to retain all payload arguments | ||
* (or even all parts of all payload arguments). | ||
* | ||
* The timestamp of this log event is the current time. | ||
**/ | ||
Span log(String eventName, /* @Nullable */ Object payload); | ||
|
||
/** Get a Baggage item by key. | ||
* | ||
* Returns null if no entry found, or baggage is not supported in the current implementation. | ||
*/ | ||
String getBaggageItem(String key); | ||
|
||
/** | ||
* Add a new log event to the Span, accepting an event name string and an optional structured payload argument. | ||
* If specified, the payload argument may be of any type and arbitrary size, | ||
* though implementations are not required to retain all payload arguments | ||
* (or even all parts of all payload arguments). | ||
* | ||
* The timestamp of this log event is the current time. | ||
**/ | ||
Span log(String eventName, /* @Nullable */ Object payload); | ||
|
||
/** | ||
* Add a new log event to the Span, accepting an event name string and an optional structured payload argument. | ||
* If specified, the payload argument may be of any type and arbitrary size, | ||
* though implementations are not required to retain all payload arguments | ||
* (or even all parts of all payload arguments). | ||
* | ||
* The timestamp is specified manually here to represent a past log event. | ||
* The timestamp in microseconds in UTC time. | ||
**/ | ||
Span log(long timestampMicroseconds, String eventName, /* @Nullable */ Object payload); | ||
/** | ||
* Add a new log event to the Span, accepting an event name string and an optional structured payload argument. | ||
* If specified, the payload argument may be of any type and arbitrary size, | ||
* though implementations are not required to retain all payload arguments | ||
* (or even all parts of all payload arguments). | ||
* | ||
* The timestamp is specified manually here to represent a past log event. | ||
* The timestamp in microseconds in UTC time. | ||
**/ | ||
Span log(long timestampMicroseconds, String eventName, /* @Nullable */ Object payload); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/** | ||
* Copyright 2016 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; | ||
|
||
/** | ||
* SpanContext represents Span state that must propagate to descendant Spans and across process boundaries. | ||
* | ||
* SpanContext is logically divided into two pieces: (1) the user-level "Baggage" (see set_baggage_item and get_baggage_item) that propagates across Span boundaries and (2) any Tracer-implementation-specific fields that are needed to identify or otherwise contextualize the associated Span instance (e.g., a <trace_id, span_id, sampled> tuple). | ||
*/ | ||
public interface SpanContext { | ||
/** | ||
* Sets a baggage item in the SpanContext as a key/value pair. | ||
* | ||
* Baggage enables powerful distributed context propagation functionality where arbitrary application data can be carried along the full path of request execution throughout the system. | ||
* | ||
* Note 1: Baggage is only propagated to the future (recursive) children of this SpanContext. | ||
* | ||
* Note 2: Baggage is sent in-band with every subsequent local and remote calls, so this feature must be used with care. | ||
* | ||
* @return this SpanContext instance, for chaining | ||
*/ | ||
SpanContext setBaggageItem(String key, String value); | ||
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 don't like the setBaggageItem method on the span context. It forces span context to be mutable and opened for side effects. I rather like to leave it on the aggregate root (the Span) which can forward the baggage item to the context internally. That would make it possible to have the spanContext immutable. 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. @dawallin if the SpanContext does not contain the baggage, then 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. @yurishkuro The SpanContext should definitely contain the baggage, it is only the set method that I don't like since it makes the SpanContext mutable. The Extract method creates the SpanContext and can pass the baggage as a list in the spanContext constructor, it does not need the set method. 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. @dawallin I see your point that the baggage could be set when the SpanContext impl is created. My best argument for keeping setBaggageItem on SpanContext is just symmetry: having the setter and getter on different objects will confuse people. There's also the question of whether baggage should be settable after Span All of this said, I would like to keep the setter and getter where they are for this PR. Your arguments apply across languages and we should be discussing them in the right place, not here on ot-java. I've created opentracing/opentracing.io#106 . |
||
|
||
/** | ||
* @return the value of the baggage item identified by the given key, or null if no such item could be found | ||
*/ | ||
String getBaggageItem(String key); | ||
} | ||
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. In go a method to iterate through all baggage items were introduced. Should you have it here as well ? 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 would like this to happen in a followup... there are some subtleties that you brought up re immutability that I think are relevant here (namely, if we know that SpanContexts are immutable, the interface can be zero-copy and return an iterator and be threadsafe... otherwise I'd rather do something like what we did in Go and have the SpanContext impl invoke a callback repeatedly). (ed: I misremembered and thought tinkerware had brought up the immutability issue -- it was @dawallin!) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,78 +15,84 @@ | |
|
||
|
||
/** | ||
* Tracer is a simple, thin interface for Span creation, and Span propagation into different transport formats. | ||
* Tracer is a simple, thin interface for Span creation and propagation across arbitrary transports. | ||
*/ | ||
public interface Tracer { | ||
|
||
/** | ||
* Create, start, and return a new Span with the given `operationName`. | ||
* An optional parent Span can be specified used to incorporate the newly-returned Span into an existing trace. | ||
* Return a new SpanBuilder for a Span with the given `operationName`. | ||
* | ||
* <p>Example: | ||
* <p>A contrived example: | ||
* <pre>{@code | ||
Tracer tracer = ... | ||
|
||
Span feed = tracer.buildSpan("GetFeed") | ||
.start(); | ||
Span parentSpan = tracer.buildSpan("DoWork") | ||
.start(); | ||
|
||
Span http = tracer.buildSpan("HandleHTTPRequest") | ||
.withParent(feed) | ||
.asChildOf(parentSpan.context()) | ||
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. @bensigelman similar to Node/Python, we can overload this to |
||
.withTag("user_agent", req.UserAgent) | ||
.withTag("lucky_number", 42) | ||
.start(); | ||
}</pre> | ||
*/ | ||
SpanBuilder buildSpan(String operationName); | ||
|
||
/** Takes two arguments: | ||
* a Span instance, and | ||
* a “carrier” object in which to inject that Span for cross-process propagation. | ||
/** | ||
* Inject a SpanContext into a `carrier` of a given type, presumably for propagation across process boundaries. | ||
* | ||
* A “carrier” object is some sort of http or rpc envelope, for example HeaderGroup (from Apache HttpComponents). | ||
* <p>Example: | ||
* <pre>{@code | ||
* Tracer tracer = ... | ||
* Span clientSpan = ... | ||
* HttpHeaderWriter httpHeaderWriter = new AnHttpHeaderCarrier(httpRequest); | ||
* tracer.inject(span.context(), httpHeaderWriter); | ||
* }</pre> | ||
* | ||
* Attempting to inject to a carrier that has been registered/configured to this Tracer will result in a | ||
* IllegalStateException. | ||
* @param spanContext the SpanContext instance to inject into the carrier | ||
* @param carrier the carrier for the SpanContext state; when inject() returns, the Tracer implementation will have represented the SpanContext within `carrier`. All Tracer.inject() implementations must support io.opentracing.propagation.TextMapWriter, io.opentracing.propagation.HttpHeaderWriter, and java.nio.ByteBuffer. | ||
* | ||
* All implementations support at minimum the required carriers BinaryWriter and TextMapWriter. | ||
* @see io.opentracing.propagation | ||
*/ | ||
<T> void inject(Span span, T carrier); | ||
void inject(SpanContext spanContext, Object carrier); | ||
|
||
/** Returns a SpanBuilder provided | ||
* a “carrier” object from which to extract identifying information needed by the new Span instance. | ||
* | ||
* If the carrier object has no such span stored within it, a new Span is created. | ||
* | ||
* Unless there’s an error, it returns a SpanBuilder. | ||
* The Span generated from the builder can be used in the host process like any other. | ||
/** | ||
* Extract a SpanContext from a `carrier` of a given type, presumably after propagation across a process boundary. | ||
* | ||
* (Note that some OpenTracing implementations consider the Spans on either side of an RPC to have the same identity, | ||
* and others consider the caller to be the parent and the receiver to be the child) | ||
* <p>Example: | ||
* <pre>{@code | ||
* Tracer tracer = ... | ||
* HttpHeaderReader httpHeaderReader = new AnHttpHeaderCarrier(httpRequest); | ||
* SpanContext spanCtx = tracer.extract(httpHeaderReader); | ||
* tracer.buildSpan('...').withChildOf(spanCtx).start(); | ||
* }</pre> | ||
* | ||
* Attempting to join from a carrier that has been registered/configured to this Tracer will result in a | ||
* IllegalStateException. | ||
* If the span serialized state is invalid (corrupt, wrong version, etc) inside the carrier this will result in an IllegalArgumentException. | ||
* | ||
* If the span serialized state is invalid (corrupt, wrong version, etc) inside the carrier this will result in a | ||
* IllegalArgumentException. | ||
* @param carrier the carrier for the SpanContext state. All Tracer.extract() implementations must support io.opentracing.propagation.TextMapReader, io.opentracing.propagation.HttpHeaderReader, and java.nio.ByteBuffer. | ||
* @returns the SpanContext instance extracted from the carrier | ||
* | ||
* All implementations support at minimum the required carriers BinaryReader and TextMapReader. | ||
* @see io.opentracing.propagation | ||
*/ | ||
<T> SpanBuilder join(T carrier); | ||
SpanContext extract(Object carrier); | ||
|
||
|
||
interface SpanBuilder { | ||
|
||
/** Specify the operationName. | ||
* | ||
* If the operationName has already been set (implicitly or explicitly) an IllegalStateException will be thrown. | ||
/** | ||
* A shorthand for withReference(Reference.childOf(parent)). | ||
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. bye operation name.. you will be missed :P 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. still there, of course, when creating the builder... but another nice thing about the move towards SpanContext (and extract instead of join) is that you basically never need to create a span when you don't already know the operation name. 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. cool thx |
||
*/ | ||
SpanBuilder withOperationName(String operationName); | ||
SpanBuilder asChildOf(SpanContext parent); | ||
|
||
/** Specify the parent span | ||
/** | ||
* Add a reference from the Span being built to a distinct (usually parent) Span. May be called multiple times to represent multiple such References. | ||
* | ||
* @param referenceType the reference type, typically one of the constants defined in References | ||
* @param referencedContext the SpanContext being referenced; e.g., for a References.CHILD_OF referenceType, the referencedContext is the parent | ||
* | ||
* If the parent has already been set an IllegalStateException will be thrown. | ||
* @see io.opentracing.References | ||
*/ | ||
SpanBuilder withParent(Span parent); | ||
SpanBuilder addReference(String referenceType, SpanContext referencedContext); | ||
|
||
/** Same as {@link Span#setTag(String, String)}, but for the span being built. */ | ||
SpanBuilder withTag(String key, String value); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/** | ||
* Copyright 2016 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.propagation; | ||
|
||
import java.util.Iterator; | ||
import java.util.Map; | ||
|
||
/** | ||
* HttpHeaderReader is a built-in carrier for Tracer.inject(). | ||
* | ||
* HttpHeaderReader implementations allows Tracers to write key:value String pairs into arbitrary HTTP header representations. In this way, HttpHeaderReader prevents a tight coupling between OpenTracing and any particular Java HTTP Header representation. | ||
* | ||
* @see io.opentracing.Tracer#extract(Object) | ||
*/ | ||
public interface HttpHeaderReader { | ||
/** | ||
* Gets HTTP headers from the implementations backing store. | ||
* | ||
* Note that these headers will often be a superset of whatever was injected via an HttpHeaderWriter in the peer. As such, Tracers should use a unique prefix or substring to identify their header map entries. | ||
* | ||
* @return all entries in the HTTP header map; note that keys may appear multiple times (just as they may with HTTP headers) | ||
* | ||
* @see io.opentracing.Tracer#extract(Object) | ||
*/ | ||
Iterator<Map.Entry<String,String>> getEntries(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/** | ||
* Copyright 2016 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.propagation; | ||
|
||
/** | ||
* HttpHeaderWriter is a built-in carrier for Tracer.inject(). | ||
* | ||
* HttpHeaderWriter implementations allows Tracers to write key:value String pairs into arbitrary HTTP header representations. In this way, HttpHeaderWriter prevents a tight coupling between OpenTracing and any particular Java HTTP Header representation. | ||
* | ||
* @see io.opentracing.Tracer#inject(io.opentracing.SpanContext, Object) | ||
*/ | ||
public interface HttpHeaderWriter { | ||
/** | ||
* Puts a key:value pair into the HTTP header map. | ||
* | ||
* Note that headers added via put() will often share the HTTP header map with other application data. As such, Tracers should use a unique prefix or substring to identify their header map entries. | ||
* | ||
* @param key a key suitable for use in an HTTP header (i.e., case-insensitive, no special characters, etc) | ||
* @param value a value suitable for use in an HTTP header (i.e., URL-escaped) | ||
* | ||
* @see io.opentracing.Tracer#inject(io.opentracing.SpanContext, Object) | ||
*/ | ||
void put(String key, String value); | ||
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. since http headers can repeat, should we define what should occur when the key exists? 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. this is just me, but I'd rather not. HTTP header support is so inconsistent across a large system, and IMO it's bad practice for a tracing system to rely on repeated headers anyway... 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 agree I don't think we should support duplicate values, just curious if
we should ask implementors to prevent this somehow. Not documenting is fine
as we could later if it became common enough concern.
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. 👍 In practice there will be O(1) headers for things like trace/span ids, and then zero or more headers for baggage. Since the baggage doesn't allow for duplicate keys, this is unlikely to become an issue at this layer (or so I hope). |
||
} |
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.
(indenting was off throughout this file)