All posts are in the personal independent blog http://blog.mozhu.org starting, welcome to visit.
In the last blog post, we learned about the basic use of the brave framework and analyzed some of the source code associated with tracer. This blog post we'll take a look at the initialization of tracing and the source code of the related class
public class Tracedemo {public
static void Main (string[] args) {
Sender Sender = okhttpsender.create ("Http://loc Alhost:9411/api/v2/spans ");
Asyncreporter asyncreporter = Asyncreporter.builder (sender)
. Closetimeout (timeunit.milliseconds)
. Build (SPANBYTESENCODER.JSON_V2);
Tracing tracing = Tracing.newbuilder ()
. Localservicename ("Tracer-demo")
. Spanreporter (asyncreporter)
. Propagationfactory (Extrafieldpropagation.newfactory (b3propagation.factory, "User-name"))
. Currenttracecontext (Threadcontextcurrenttracecontext.create ())
. Build ();
Tracer Tracer = Tracing.tracer ();
// ...
}
}
Brave in the various components to create a large number of Builder design patterns, tacing is no exception, first look at the Tracing.builder tracing.builder
public static final class Tracing.builder {String localservicename;
Endpoint Localendpoint; Reporter<zipkin2.
Span> reporter;
Clock Clock;
Sampler Sampler = sampler.always_sample;
Currenttracecontext Currenttracecontext = CurrentTraceContext.Default.inheritable ();
Boolean traceid128bit = false;
Boolean supportsjoin = true;
Propagation.factory propagationfactory = Propagation.Factory.B3;
Public tracing Build () {if (Clock = = null) Clock = Platform.get ();
if (Localendpoint = = null) {Localendpoint = Platform.get (). Localendpoint ();
if (localservicename!= null) {localendpoint = Localendpoint.tobuilder (). ServiceName (Localservicename). build ();
} if (reporter = NULL) reporter = Platform.get ();
Return to New Default (this); } Builder () {}}
Several important classes of tracing in the process of dependence
- Endpoint -IP, port and application service name information
- Sampler -Sampler, according to the Traceid to determine whether a trace needs to be sampled, that is, reported to Zipkin
- TraceContext -contains traceid,spanid, sampling and other data
- Currenttracecontext -is a helper class that can be used to get the current thread's TraceContext
- Propagation -is an interface that can inject (inject) and extract (extract) data into a data-carrying object carrier
-Factory class of propagation.factory -Propagation
In the previous Tracedemo example, when we initialize the tracing, we set the Localservicename,spanreporter,propagationfactory,currenttracecontext
Where Spanreporter for Asyncreporter we have analyzed its source code in the previous article, as you can see in the build method, the default implementation is platform, the default is to output the span information with logger instead of escalation to Zipkin
@Override public void Zipkin2. Span span) {
if (!logger.isloggable (Level.info)) return;
if (span = null) throw new NullPointerException ("span = null");
Logger.info (Span.tostring ());
}
Sampler
Sampler, according to Traceid to determine whether a trace needs to be sampled, that is, escalation to Zipkin
Public abstract class Sampler {public static final sampler always_sample = new Sampler () {@Override public Boolea
N issampled (Long Traceid) {return true;
@Override public String toString () {return "alwayssample";
}
}; public static Final Sampler never_sample = new Sampler () {@Override public boolean issampled (long Traceid) {RE
Turn false;
@Override public String toString () {return "neversample";
}
}; /** Returns True if the trace ID should be measured.
* * Public Abstract Boolean issampled (long Traceid);
/** * Returns A sampler, given a rate expressed as a percentage.
* <p>the Sampler returned is good to low volumes of traffic (<100k requests), as it is precise.
* If you are have high volumes of traffic, consider {@link Boundarysampler}. * * @param rate Minimum sample rate is 0.01, or 1% of traces/public static sampler Create (float rate) {RET Urn Countingsampler.creAte (rate); }
}
Sampler.always_sample always need to be sampled
Sampler.never_sample never sample
Sampler also has an implementation class
Countingsampler can specify the sampling rate, such as countingsampler.create (0.5f), to sample 50% of the requested data, which uses an algorithm that does not expand the analysis. TraceContext
Contains Traceid,spanid, whether to sample data, etc.
There is a section of code in the tracer Newrootcontext method that constructs TraceContext objects through Newbuilder
TraceContext Newrootcontext (samplingflags samplingflags, list<object> extra) {
long NextID = Platform.get (). Randomlong ();
Boolean sampled = Samplingflags.sampled ();
if (sampled = = null) sampled = Sampler.issampled (NextID);
Return Tracecontext.newbuilder ()
. Sampled (sampled)
. Traceidhigh (traceid128bit? Platform.get (). Nexttraceidhigh (): 0L). Traceid (NextID)
. Spanid (NextID)
. Debug (Samplingflags.debug ())
. Extra (extra). build ();
Some of the following properties are in TraceContext
- Traceidhigh -a 16-byte ID that uniquely identifies trace, that is, 128-bit
- Traceid -8-byte ID that uniquely identifies trace
- ParentID -Spanid of the parent span
- Spanid -8-byte ID that uniquely identifies a span in a trace
- Shared -If true, indicates that span information needs to be shared from other tracer
- Extra -additional data set associated with a trace
and two attributes inherited from Samplingflags.
- sampled -sampling
- Debug-Debug-if True, even if sampled is false, it also indicates that trace needs to be sampled (that is, you can overwrite sampled values)
Two interfaces are also defined in the TraceContext injector,extractor
Public interface Injector<c> {
void inject (TraceContext TraceContext, C carrier);
}
Public interface Extractor<c> {
tracecontextorsamplingflags extract (C carrier);
}
Injector-Used to inject various data from the TraceContext into the carrier, where carrier is typically an object that is similar to the HTTP headers that can carry additional information in the RPC middle.
Extractor-Used to extract TraceContext-related information or sample tag information in carrier Tracecontextorsamplingflags
Tracecontextorsamplingflags
Tracecontextorsamplingflags is a joint type of three types of data, Tracecontext,traceidcontext,samplingflags, the official document says
-When there are Traceid and Spanid, create (TraceContext) is required
-Create when only Spanid is required (traceidcontext)
-In other cases, create (samplingflags) is required
The code in Tracecontextorsamplingflags is simpler, and there is no analysis here Currenttracecontext
Currenttracecontext is a helper class that can be used to get the TraceContext of the current thread, and its default implementation class is Currenttracecontext.default
public static final class default extends Currenttracecontext {static final threadlocal<tracecontext> default =
New Threadlocal<> (); Inheritable as brave 3 ' s threadlocalserverclientandlocalspanstate was inheritable static final Inheritablethreadloc
Al<tracecontext> inheritable = new inheritablethreadlocal<> ();
Final Threadlocal<tracecontext> Local; /** @deprecated prefer {@link #create ()} as it isn ' t inheritable, so can ' t leak contexts.
* * @Deprecated public Default () {this (inheritable); }/** Uses a non-inheritable static thread local */public static Currenttracecontext create () {return new
Default (default); }/** * Uses an inheritable static thread \ which allows arbitrary calls to {@link * Thread#start ()} to automatically inherit this context. This is available as it's is feature 3, because some users couldn ' t control threads in their ApplicAtions. * <p>this can be a problem into scenarios such as thread pool expansion, leading to data being * recorded In the wrong span, or spans with the wrong parent.
If you are are impacted by this, * switch to {@link #create ()}.
* * public static Currenttracecontext inheritable () {return new Default (inheritable); } Default (Threadlocal<tracecontext> local = if (local = null) throw new NullPointerException ("local = =
Null ");
this.local = local;
@Override public TraceContext get () {return local.get (); }
}
Currenttracecontext.default provides two static methods, that is, create () and inheritable ()
When created using the Create method, the local object is the threadlocal type
When created using the inheritable method, the local object is inheritablethreadlocal type
Threadlocal can be understood as a shared memory space developed by the JVM for the same thread, which is called by different methods in the same thread and can be taken out of the placed object from that space
And when the thread-bound object is fetched using inheritablethreadlocal, the current thread does not, and the current thread's parent thread's shared memory is fetched
Official documentation points out that the inheritable method needs to be used with care in the context of the online pool and may take out the wrong tracecontext, which can cause information such as span to be logged and associated to the wrong Traceid Currenttracecontext.scope
Public abstract Scope Newscope (@Nullable tracecontext currentspan);
/** A span remains in the scope it being bound to until the close is called. * * Public
interface Scope extends Closeable {/** No exceptions are the thrown when
unbinding a span Scope. * *
@Ov erride void Close ();
}
A scope interface is also defined in Currenttracecontext, which inherits from the Closeable interface
Since JDK7, the object that implements the Closeable interface, as long as it is defined in the Try statement, and when finally executes, the JVM will actively call its Close method to reclaim the resource. So the Currenttracecontext provides a newscope method that we can use in the code
Try (Scope scope = Newscope (Invocationcontext)) {
//do somthing
}
Let's see how the Newscope is realized in Currenttracecontext.default
@Override public Scope newscope (@Nullable tracecontext currentspan) {
final TraceContext previous = Local.get ();
Local.set (Currentspan);
Class Defaultcurrenttracecontextscope implements Scope {
@Override public void Close () {
local.set (previous); c7/>}
return
new Defaultcurrenttracecontextscope ();
}
The TraceContext of the current thread is first assigned to the previous variable, and the new TraceContext is set to the current thread, and when the Close method call of scope restores the previous value to the current thread
Use two nested try blocks to demonstrate the meaning of the following procedure
TraceContext TraceContext1;
TraceContext traceContext2;
Try (Scope scope = Newscope (TraceContext1)) {
//1. Here Currenttracecontext.get () can obtain TRACECONTEXT1
try (scope Scope = Newscope (traceContext2)) {
//2. Here Currenttracecontext.get () can get traceContext2
}
//3. Here Currenttracecontext.get () can obtain TRACECONTEXT1
}
Before entering the inner-layer try code block, the TRACECONTEXT1 obtained via Currenttracecontext.get () is passed through the Currenttracecontext.get () after entering the inner-layer try code block. The traceContext2 obtained is obtained by Currenttracecontext.get () after running the inner try code block TRACECONTEXT1
This approach is indeed more flexible and elegant, but for the use of people, is also a bit too vague, do not know JDK7 new characteristics of the students just started to see this usage may be a blank face
Of course, this usage must allow the person to use the scope object new in the Try statement, everyone can follow this agreed rules to write, error prone, so currenttracecontext in the provision of several pairs of callable, Wrap method of runnable encapsulation method
/** wraps the input so this it executes with the same context as now. * Public
<C> callable<c> Wrap (callable<c> Task) {
final TraceContext Invocationcontext = Get ();
Class Currenttracecontextcallable implements callable<c> {
@Override public C call () throws Exception {
Try (Scope scope = Newscope (Invocationcontext)) {return
task.call ();
}
}} return new currenttracecontextcallable ();
}
/** wraps the input so this it executes with the same context as now. * * Public
Runnable Wrap (Runnable Task) {
final TraceContext invocationcontext = Get ();
Class Currenttracecontextrunnable implements Runnable {
@Override public void Run () {
try (scope scope = Newscop E (Invocationcontext)) {
task.run ();
}}} return new currenttracecontextrunnable ();
}
Currenttracecontext also provides packaging methods for executor and Executeservice.
/** * decorates the input such that the {@link #get () the "Current Trace"} at the "Time a" task is * scheduled is
Made the task is executed. */Public Executor Executor (Executor delegate) {class Currenttracecontextexecutor implements Executor {@Over
Ride public void Execute (Runnable task) {Delegate.execute (CurrentTraceContext.this.wrap (Task));
Return to New Currenttracecontextexecutor (); }/** * decorates the input such that the {@link #get () current trace context} at the time a task is * scheduled
is made the task is executed. */Public Executorservice Executorservice (Executorservice delegate) {class Currenttracecontextexecutorservice Exten DS Brave.internal.WrappingExecutorService {@Override protected Executorservice delegate () {return Delegat
E } @Override protected <C> callable<c> Wrap (callable<c> Task) {return Currenttracecontext . thIs.wrap (Task);
@Override protected Runnable Wrap (Runnable Task) {return CurrentTraceContext.this.wrap (Task);
Return to New Currenttracecontextexecutorservice (); }
These methods are used in the adorner design pattern, is a more commonly used design pattern, here is no longer to expand the analysis of the threadcontextcurrenttracecontext
You can see in the Tracedemo, we set the Currenttracecontext is Threadcontextcurrenttracecontext.create ()
The Threadcontextcurrenttracecontext is encapsulated for LOG4J2, a class in the Brave-context-log4j2 package, where the Traceid and Spanid two properties are placed in the Threadcontext. We can configure the log print pattern in the log4j2 configuration file, using placeholders%x{traceid} and%x{spanid}, so that each row of logs can print the current Traceid and Spanid
Zipkin-learning\chapter1\servlet25\src\main\resources\log4j2.properties
Appender.console.layout.pattern =%d{absolute} [%x{traceid}/%x{spanid}]%-5p [%t]%c{2}-%m%n
Need to add log-related jars in pom.xml
<brave.version>4.9.1</brave.version> <log4j.version>2.8.2</log4j.version> <depende Ncy> <groupId>io.zipkin.brave</groupId> <ARTIFACTID>BRAVE-CONTEXT-LOG4J2</ARTIFACTID&G
T <version>${brave.version}</version> </dependency> <dependency> <groupid>org.a Pache.logging.log4j</groupid> <artifactId>log4j-core</artifactId> <version>${log4j.ver sion}</version> </dependency> <dependency> <groupid>org.apache.logging.log4j</gro upid> <artifactId>log4j-jul</artifactId> <version>${log4j.version}</version> &L t;/dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifacti d>log4j-jcl</artifactid> <version>${log4j.version}</version> </dependency> <de
Pendency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>${log4j.version}</version> </dependency>
In the case of Chapter1, if you look at the console of Frontend and backend, the following output 0cabad9917e767ab is Traceid, 0cabad9917e767ab and E96A226CE75D30B4 for Spanid
10:11:05,731 [0cabad9917e767ab/0cabad9917e767ab] INFO [QTP1441410416-17] servlet. Frontendservlet-frontend Receive Request
10:11:05,820 [0CABAD9917E767AB/E96A226CE75D30B4] INFO [qtp1441410416-15] servlet. Backendservlet-backend Receive Request
Public final class Threadcontextcurrenttracecontext extends Currenttracecontext {public static Threadcontextcurrenttra
Cecontext Create () {return Create (CurrentTraceContext.Default.inheritable ()); public static Threadcontextcurrenttracecontext Create (Currenttracecontext delegate) {return new threadcontextcur
Renttracecontext (delegate);
Final Currenttracecontext delegate; Threadcontextcurrenttracecontext (Currenttracecontext delegate) {if (delegate = = null) throw new NullPointerException (
"Delegate = = null");
This.delegate = delegate;
@Override public TraceContext get () {return delegate.get (); @Override public Scope newscope (@Nullable TraceContext currentspan) {final String Previoustraceid = Threadcontex
T.get ("Traceid");
Final String Previousspanid = Threadcontext.get ("Spanid");
if (Currentspan!= null) {threadcontext.put ("Traceid", currentspan.traceidstring ()); Threadcontext.put ("Spanid", Hexcodec.tolowerhEx (Currentspan.spanid ()));
else {threadcontext.remove ("Traceid");
Threadcontext.remove ("Spanid");
Scope scope = Delegate.newscope (Currentspan); Class Threadcontextcurrenttracecontextscope implements Scope {@Override public void close () {scope.close ()
;
Threadcontext.put ("Traceid", Previoustraceid);
Threadcontext.put ("Spanid", Previousspanid);
Return to New Threadcontextcurrenttracecontextscope (); }
}
Threadcontextcurrenttracecontext inherits Currenttracecontext, overrides its Newscope method, Extracts the Traceid and Spanid in the Currentspan into the Log4j2 context object Threadcontext
Support for SLF4J and log4j can also be found in the Https://github.com/openzipkin/brave/tree/master/context.
The Brave.context.slf4j.MDCCurrentTraceContext in the BRAVE-CONTEXT-SLF4J
The Brave.context.log4j12.MDCCurrentTraceContext in the BRAVE-CONTEXT-LOG4J12
The code is similar, here is not the propagation
Propagation, an English translation propagator, is an interface that can inject (inject) and extract (extract) data into a data-carrying object carrier.
In the case of HTTP protocol, usually carrier means HTTP request object, its HTTP headers can carry trace information, generally HTTP client will inject (inject) trace information in headers, The server side will extract (extract) trace information in the headers
Propagation.setter and Propagation.getter can set and get values in carrier
In addition, injector and extractor methods are returned to Tracecontext.injector and Tracecontext.extractor respectively.
Interface Setter<c, k> {
void put (C carrier, K key, String value);
Interface Getter<c, k> {
@Nullable String get (C carrier, K key);
}
<C> tracecontext.injector<c> Injector (setter<c, k> Setter);
<C> tracecontext.extractor<c> Extractor (getter<c, k> Getter);
Propagation also has a factory class propagation.factory, there is a factory method created, through Keyfactory to create propagation objects
Abstract class Factory {public
static final Factory B3 = b3propagation.factory;