OpenTelemetry可用于测量采集遥测数据的代码。有关更多详细信息,请访问OpenTelemetry Website
希望使用OpenTelemetry导出遥测数据的库必须依赖opentelemetry-api
包,并且绝不可以配置或依赖 OpenTelemetry SDK。
SDK配置必须由“应用程序”提供,而该应用程序取决于opentelemetry-sdk
软件包,或任何其他OpenTelemetry API的实现。这样,
库只有用户应用程序做相应配置的情况下才会获得真正的实现。有关更多详细信息,请查阅Library Guidelines。
我们介绍如何使用OpenTelemetry API追踪代码。注意:永远不要调用OpenTelemetry SDK的方法。
首先必须获取一个Tracer,该Tracer负责创建spans和并与上下文交互。Tracer是通过使用OpenTelemetry API来获取的,该API指定了检测要监控的检查库或应用程序的库的名称和版本。可在规范章节 Obtaining a Tracer中获得更多信息。
Tracer tracer =
OpenTelemetry.getTracer("instrumentation-library-name","semver:1.0.0");
要创建基础Span,只需指定Span的名称。 Span的开始和结束时间由OpenTelemetry SDK自动设置。
Span span = tracer.spanBuilder("my span").startSpan();
try (Scope scope = tracer.withSpan(span)) {
// your use case
...
} catch (Throwable t) {
Status status = Status.UNKNOWN.withDescription("Change it to your error message");
span.setStatus(status);
} finally {
span.end(); // closing the scope does not end the span, this has to be done manually
}
很多时候我们希望为嵌套操作关联span。OpenTelemetry支持在进程内和跨远程进程进行追踪。更多关于如何在远程进程间共享上下文的详细信息,请查看上下文传播。
对于方法a调用方法b,可以通过以下方式手动链接span:
void a() {
Span parentSpan = tracer.spanBuilder("a")
.startSpan();
b(parentSpan);
parentSpan.end();
}
void b(Span parentSpan) {
Span childSpan = tracer.spanBuilder("b")
.setParent(parentSpan)
.startSpan();
// do stuff
childSpan.end();
}
OpenTelemetry API还提供了一种自动方法来传播parentSpan:
void a() {
Span parentSpan = tracer.spanBuilder("a").startSpan();
try(Scope scope = tracer.withSpan(parentSpan)){
b();
} finally {
parentSpan.end();
}
}
void b() {
Span childSpan = tracer.spanBuilder("b")
// NOTE: setParent(parentSpan) is not required anymore,
// `tracer.getCurrentSpan()` is automatically added as parent
.startSpan();
// do stuff
childSpan.end();
}
链接来自远程进程的span,只需将Remote Context设置为父级即可。
Span childRemoteParent = tracer.spanBuilder("Child").setParent(remoteContext).startSpan();
在OpenTelemetry中,可以自由创建Span,由实现者使用特定于所表示操作的属性对其进行注释。属性在Span上提供有关它追踪的特定操作的附加上下文,比如结果或操作属性。
Span span = tracer.spanBuilder("/resource/path").setSpanKind(Span.Kind.CLIENT).startSpan();
span.setAttribute("http.method", "GET");
span.setAttribute("http.url", url.toString());
其中一些操作表示使用众所周知的协议(如HTTP或数据库调用)的调用。 对于这些,OpenTelemetry需要设置特定的属性。完整的属性列表在跨语言规范的Semantic Conventions中提供。
Span 可以携带零个或多个Span属性的命名事件进行注释,每一个事件都是一个key:value键值对并自动携带相应的时间戳。
span.addEvent("Init");
...
span.addEvent("End");
Attributes eventAttributes = Attributes.of(
"key", AttributeValue.stringAttributeValue("value"),
"result", AttributeValue.longAttributeValue(0L));
span.addEvent("End Computation", eventAttributes);
一个Span可以连接一个或多个因果相关的其他Span。链接可用于表示批处理操作,其中一个Span的初始化由多个Span初始化构成,其中每个Span表示批处理中处理的单个输入项。
Link link1 = SpanData.Link.create(parentSpan1.getContext());
Link link2 = SpanData.Link.create(parentSpan2.getContext());
Span child = tracer.spanBuilder("childWithLink")
.addLink(link1)
.addLink(link2)
.addLink(parentSpan3.getContext())
.addLink(remoteContext)
.startSpan();
有关如何从远程进程中读取上下文的更多详细信息,请参见上下文传播。
进程内传播依靠 gRPC Context, 一个完善的上下文传播库,包含在一个小构件中,它不依赖于整个gRPC引擎。
OpenTelemetry提供了一种基于文本的方法,可以使用 W3C Trace Context HTTP标头。
以下是使用HttpURLConnection
发出的HTTP请求的示例。
// Tell OpenTelemetry to inject the context in the HTTP headers
HttpTextFormat.Setter<HttpURLConnection> setter =
new HttpTextFormat.Setter<HttpURLConnection>() {
@Override
public void put(HttpURLConnection carrier, String key, String value) {
// Insert the context as Header
carrier.setRequestProperty(key, value);
}
};
URL url = new URL("http://127.0.0.1:8080/resource");
Span outGoing = tracer.spanBuilder("/resource").setSpanKind(Span.Kind.CLIENT).startSpan();
try (Scope scope = tracer.withSpan(outGoing)) {
// Semantic Convention.
// (Observe that to set these, Span does not *need* to be the current instance.)
outGoing.setAttribute("http.method", "GET");
outGoing.setAttribute("http.url", url.toString());
HttpURLConnection transportLayer = (HttpURLConnection) url.openConnection();
// Inject the request with the *current* Context, which contains our current Span.
OpenTelemetry.getPropagators().getHttpTextFormat().inject(Context.current(), transportLayer, setter);
// Make outgoing call
} finally {
outGoing.end();
}
...
类似的基于文本的方法可用于从传入请求中读取W3C追踪上下文。 下面提供了使用以下命令处理传入HTTP请求的示例 HttpExchange。
HttpTextFormat.Getter<HttpExchange> getter =
new HttpTextFormat.Getter<HttpExchange>() {
@Override
public String get(HttpExchange carrier, String key) {
if (carrier.getRequestHeaders().containsKey(key)) {
return carrier.getRequestHeaders().get(key).get(0);
}
return null;
}
};
...
public void handle(HttpExchange httpExchange) {
// Extract the SpanContext and other elements from the request.
Context extractedContext = OpenTelemetry.getPropagators().getHttpTextFormat()
.extract(Context.current(), httpExchange, getter);
Span serverSpan = null;
try (Scope scope = ContextUtils.withScopedContext(extractedContext)) {
// Automatically use the extracted SpanContext as parent.
serverSpan = tracer.spanBuilder("/resource").setSpanKind(Span.Kind.SERVER)
.startSpan();
// Add the attributes defined in the Semantic Conventions
serverSpan.setAttribute("http.method", "GET");
serverSpan.setAttribute("http.scheme", "http");
serverSpan.setAttribute("http.host", "localhost:8080");
serverSpan.setAttribute("http.target", "/resource");
// Serve the request
...
} finally {
if (serverSpan != null) {
serverSpan.end();
}
}
}
Span是获取应用程序正在执行的操作的详细信息的好方法,但是,对于更多聚合的场景哪?OpenTelemetry为指标提供支持, 一个时序数据可能表示诸如CPU利用率,HTTP服务器的请求计数或业务指标,例如交易。
所有指标都可以用标签进行注释:附加的限定有助于描述度量指标的细分。
以下是计数器用法的示例:
// Gets or creates a named meter instance
Meter meter = OpenTelemetry.getMeter("instrumentation-library-name","semver:1.0.0");
// Build counter e.g. LongCounter
LongCounter counter = meter
.longCounterBuilder("processed_jobs")
.setDescription("Processed jobs")
.setUnit("1")
.build();
// It is recommended that the API user keep a reference to a Bound Counter for the entire time or
// call unbind when no-longer needed.
BoundLongCounter someWorkCounter = counter.bind("Key", "SomeWork");
// Record data
someWorkCounter.add(123);
// Alternatively, the user can use the unbounded counter and explicitly
// specify the labels set at call-time:
counter.add(123, "Key", "SomeWork");
Observer
是支持异步API并按需收集度量数据的附加工具,按间隔收集数据。
以下是观察者用法的示例:
// Build observer e.g. LongObserver
LongObserver observer = meter
.observerLongBuilder("cpu_usage")
.setDescription("CPU Usage")
.setUnit("ms")
.build();
observer.setCallback(
new LongObserver.Callback<LongObserver.ResultLongObserver>() {
@Override
public void update(ResultLongObserver result) {
// long getCpuUsage()
result.observe(getCpuUsage(), "Key", "SomeWork");
}
});
本文档中的配置示例仅适用于opentelemetry-sdk
提供的SDK。API的其他实现可能提供不同的配置机制。
该应用程序必须安装带有输出器的span处理器,并且可以自定义OpenTelemetry SDK的行为。
比如一个基本配置实例化了SDK追踪器注册表,并设置为将追踪数据导出到日志记录流。
// Get the tracer
TracerSdkProvider tracerProvider = OpenTelemetrySdk.getTracerProvider();
// Set to export the traces to a logging stream
tracerProvider.addSpanProcessor(
SimpleSpanProcessor.newBuilder(
new LoggingSpanExporter()
).build());
追踪和导出应用程序中的每个用户请求并不总是可行的。 为了在可观察性和占用资源之间取得平衡,可以对追踪数据进行采样。 OpenTelemetry SDK提供了三个开箱即用的采样器:
- AlwaysOnSampler不管上游采样决定如何,都对每个追踪进行采样。
- AlwaysOffSampler不会对任何追踪进行采样,无论上游采样决定如何。
- Probability 对上游的任何采样的追踪数据,根据配置的百分比进行采样。
可以通过实现io.opentelemetry.sdk.trace.Sampler接口来提供其他采样器。
TraceConfig alwaysOn = TraceConfig.getDefault().toBuilder().setSampler(
Samplers.alwaysOn()
).build();
TraceConfig alwaysOff = TraceConfig.getDefault().toBuilder().setSampler(
Samplers.alwaysOff()
).build();
TraceConfig half = TraceConfig.getDefault().toBuilder().setSampler(
Samplers.probability(0.5)
).build();
// Configure the sampler to use
tracerProvider.updateActiveTraceConfig(
half
);
OpenTelemetry提供了不同的Span处理器。SimpleSpanProcessor
立即将结束的Span转发到导出器,而BatchSpanProcessor
对它们进行批处理并批量发送它们。MultiSpanProcessor
将多个Span处理器配置为同时处于活动状态。
tracerProvider.addSpanProcessor(
SimpleSpanProcessor.newBuilder(new LoggingSpanExporter()).build()
);
tracerProvider.addSpanProcessor(
BatchSpanProcessor.newBuilder(new LoggingSpanExporter()).build()
);
tracerProvider.addSpanProcessor(MultiSpanProcessor.create(Arrays.asList(
SimpleSpanProcessor.newBuilder(new LoggingSpanExporter()).build(),
BatchSpanProcessor.newBuilder(new LoggingSpanExporter()).build()
)));
Span处理器由导出器初始化,该导出器负责将遥测数据发送到特定的后端。OpenTelemetry提供了四种开箱即用的导出器:
- In-Memory: 数据保存在内存中,在debug时使用。
- Jaeger Exporter: 准备收集的遥测数据并将其通过gRPC发送到Jaeger后端。
- Zipkin Exporter: 准备收集的遥测数据,并通过Zipkin API将其发送到Zipkin后端。
- Logging Exporter: 将遥测数据保存到日志流中。
- OpenTelemetry Exporter: 将数据发送到OpenTelemetry Collector (还未实现)。
其他导出器可以在OpenTelemetry Registry中找到。
tracerProvider.addSpanProcessor(
SimpleSpanProcessor.newBuilder(InMemorySpanExporter.create()).build());
tracerProvider.addSpanProcessor(
SimpleSpanProcessor.newBuilder(new LoggingSpanExporter()).build());
ManagedChannel jaegerChannel =
ManagedChannelBuilder.forAddress([ip:String], [port:int]).usePlaintext().build();
JaegerGrpcSpanExporter jaegerExporter = JaegerGrpcSpanExporter.newBuilder()
.setServiceName("example").setChannel(jaegerChannel).setDeadline(30000)
.build();
tracerProvider.addSpanProcessor(BatchSpanProcessor.newBuilder(
jaegerExporter
).build());
英文原文:https://github.com/open-telemetry/opentelemetry-java/blob/master/QUICKSTART.md