OpenTelemetry
The concept of Distributed Tracing was first proposed by Google
. The technology has matured over time, and there are now some protocol standards available for reference. Currently, two open-source frameworks are quite influential in this area: Netflix's
OpenTracing
and Google's
OpenCensus
. Both frameworks have a sizable developer community. To establish a unified technical standard, the two frameworks merged to form the OpenTelemetry
project, abbreviated as otel
. For more details, you can refer to:
Thus, our tracing technology solution is implemented based on the OpenTelemetry
standard, with some open-source projects implementing the protocol standard in Golang
:
- https://github.com/open-telemetry/opentelemetry-go
- https://github.com/open-telemetry/opentelemetry-go-contrib
Other third-party frameworks and systems (such as Jaeger/Prometheus/Grafana
) also adhere to standardized protocols to integrate with OpenTelemetry
, significantly reducing development and maintenance costs.
Key Concepts
Let’s first look at the architecture diagram of OpenTelemetry
. We won’t introduce it in full, only the commonly used concepts. For an introduction to the internal technical architecture of OpenTelemetry
, you can refer to OpenTelemetry Architecture, and for semantic conventions, please refer to: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md
TracerProvider
Mainly responsible for creating Tracer
, usually requiring a concrete implementation from a third-party distributed tracing management platform. By default, it is an empty TracerProvider (NoopTracerProvider)
. While it can create Tracer
, it doesn’t actually execute specific data flow transmission logic internally.
Tracer
Tracer
represents a complete tracing, consisting of one or more span
. The example below illustrates a tracer
consisting of 8
spans:
[Span A] ←←←(the root span)
|
+------+------+
| |
[Span B] [Span C] ←←←(Span C is a `ChildOf` Span A)
| |
[Span D] +---+-------+
| |
[Span E] [Span F] >>> [Span G] >>> [Span H]
↑
↑
↑
(Span G `FollowsFrom` Span F)
The timeline representation makes it easier to understand:
––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time
[Span A···················································]
[Span B··············································]
[Span D··········································]
[Span C········································]
[Span E·······] [Span F··] [Span G··] [Span H··]
We usually create a Tracer
in the following way:
gtrace.NewTracer(tracerName)
Span
A Span
is a fundamental component of a tracing. It represents a single work unit, such as a function call or an HTTP
request. A span
records the following essential elements:
- Service name (
operation name
) - Service start and end times
K/V
formTags
K/V
formLogs
SpanContext
Span
is the most frequently used object among them, thus creating a Span
is very straightforward, for example:
gtrace.NewSpan(ctx, spanName, opts...)
Attributes
Attributes
are stored as K/V
key-value pairs for user-defined tags, mainly used for query filtering of tracing results. For example: http.method="GET",http.status_code=200
. The key
must be a string, and the value
must be a string, boolean, or numeric type. Attributes
in a span
are only visible to itself and are not passed to subsequent spans through SpanContext
. The way to set Attributes
is as follows:
span.SetAttributes(
label.String("http.remote", conn.RemoteAddr().String()),
label.String("http.local", conn.LocalAddr().String()),
)
Events
Events
are similar to Attributes
and are also in the form of K/V
key-value pairs. Unlike Attributes
, Events
also record the time when they are written, so Events
are mainly used to record the time certain events occur. The key
for Events
must also be a string, but there are no restrictions on the type of value
. For example:
span.AddEvent("http.request", trace.WithAttributes(
label.Any("http.request.header", headers),
label.Any("http.request.baggage", gtrace.GetBaggageMap(ctx)),
label.String("http.request.body", bodyContent),
))
SpanContext
SpanContext
carries some data used for cross-service (cross-process) communication, mainly including:
Information sufficient to identify this span within the system, e.g.,
span_id, trace_id
.Baggage
- maintains user-definedK/V
formatted data for cross-service (cross-process) tracing in the entire tracing link.Baggage
is similar toAttributes
, also inK/V
key-value pair format. The differences are:Both
key
andvalue
can only be in string format.Baggage
is visible not only to the current span but is also passed to all subsequent child spans throughSpanContext
. Be cautious usingBaggage
because transmitting theseK,V
in all spans incurs non-trivial network and CPU overhead.
Propagator
The Propagator
is used for encoding/decoding data end-to-end, such as data transmission from the Client
to the Server
. TraceId
, SpanId
, and Baggage
also need to be managed for data transmission through the propagator. Business-end developers are usually not aware of the Propagator
, only middleware/interceptor developers need to understand its role. The standard protocol implementation library of OpenTelemetry
provides a commonly used TextMapPropagator
for common text data end-to-end transmission. Furthermore, to ensure compatibility of the data transmitted in the TextMapPropagator
, special characters should not be included. For details, please refer to: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md
The GoFrame
framework uses the following propagator objects through the gtrace
module and sets them globally in OpenTelemetry
:
// defaultTextMapPropagator is the default propagator for context propagation between peers.
defaultTextMapPropagator = propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
)
Supported Components
tip
The core components of GoFrame
have fully supported the OpenTelemetry
standard and automatically enable the tracing feature, with no need for developers to explicitly call or use it. Without injecting an external TracerProvider
, the framework will use the default TracerProvider
, which will only automatically create a TraceID
and SpanID
to facilitate tracing the request log and will not execute complex logic.
Including but not limited to the following core components:
Components with Auto Tracing Support | Component Name | Description |
---|---|---|
HTTP Client | gclient | The HTTP client automatically enables the tracing feature. For specific usage examples, please refer to the subsequent example chapter. |
Http Server | ghttp | The HTTP server automatically enables the tracing feature. For specific usage examples, please refer to the subsequent example chapter. |
gRPC Client | contrib/rpc/grpcx | The gRPC client automatically enables the tracing feature. For specific usage examples, please refer to the subsequent example chapter. |
gRPC Server | contrib/rpc/grpcx | The gRPC server automatically enables the tracing feature. For specific usage examples, please refer to the subsequent example chapter. |
Logging | glog | The log content needs to inject the current request’s TraceId to quickly locate issues through logs. This feature is implemented by the glog component. Developers need to call the Ctx chain operation method to pass the context.Context context variable to the current logging operation chain when outputting logs. Failing to pass the context.Context will result in losing the TraceId in the log content. |
ORM | gdb | The execution of the database is an essential part of the link. The Orm component needs to deliver its execution information into the tracing as part of the execution trace. |
NoSQL Redis | gredis | The execution of Redis is also an essential part of the tracing. Redis needs to deliver its execution information into the tracing as part of the execution trace. |
Utils | gtrace | Managing the Tracing feature requires some encapsulation, mainly considering extensibility and usability. This encapsulation is implemented by the gtrace module. The documentation can be found at: https://pkg.go.dev/github.com/gogf/gf/v2/net/gtrace |