From 69326eaef8a571a6159a1747242e643f592d56bc Mon Sep 17 00:00:00 2001 From: Phillip Carter Date: Mon, 20 Dec 2021 14:00:14 -0800 Subject: [PATCH] Add sections to docs for current span and nested spans (#2452) * Add sections to docs for current span and nested spans * Apply suggestions from code review Co-authored-by: Anthony Mirabella * Update based on review * satisfy linter * Apply suggestions from code review Co-authored-by: Tyler Yahn Co-authored-by: Anthony Mirabella Co-authored-by: Tyler Yahn --- website_docs/manual_instrumentation.md | 74 ++++++++++++++++++-------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/website_docs/manual_instrumentation.md b/website_docs/manual_instrumentation.md index 92d7d2f3a..8438d36e1 100644 --- a/website_docs/manual_instrumentation.md +++ b/website_docs/manual_instrumentation.md @@ -5,42 +5,68 @@ weight: 3 Instrumentation is the process of adding observability code to your application. There are two general types of instrumentation - automatic, and manual - and you should be familiar with both in order to effectively instrument your software. -# Creating Spans +## Creating Spans -Spans are created by tracers, which can be acquired from a Tracer Provider. +Spans are created by tracers, which can be acquired from a Tracer Provider. Typically a tracer is instantiated at the module level. + +To create a span with a tracer, you'll also need a handle on a `context.Context` instance. These will typically come from things like a request object and may already contain a parent span from an [instrumentation library][]. ```go -ctx := context.Background() -tracer := otel.Tracer("example/main") -var span trace.Span -ctx, span = tracer.Start(ctx, "helloWorld") -defer span.End() +func httpHandler(w http.ResponseWriter, r *http.Request) { + ctx, span := tracer.Start(r.Context(), "hello-span") + defer span.End() + + // do some work to track with hello-span +} ``` -In Go, the `context` package is used to store the active span. When you start a span, you'll get a handle on not only the span that's created, but the modified context that contains it. When starting a new span using this context, a parent-child relationship will automatically be created between the two spans, as seen here: +In Go, the `context` package is used to store the active span. When you start a span, you'll get a handle on not only the span that's created, but the modified context that contains it. + +Once a span has completed, it is immutable and can no longer be modified. + +### Get the current span + +To get the current span, you'll need to pull it out of a `context.Context` you have a handle on: ```go -func parentFunction() { - ctx := context.Background() - var parentSpan trace.Span - ctx, parentSpan = tracer.Start(ctx, "parent") +// This context needs contain the active span you plan to extract. +ctx := context.TODO() +span := trace.SpanFromContext(ctx) + +// Do something with the current span, optionally calling `span.End()` if you want it to end +``` + +This can helpful if you'd like to add information to the current span at a point in time. + +### Create nested spans + +You can create a nested span to track work in a nested operation. + +If the current `context.Context` you have a handle on already contains a span inside of it, creating a new span makes it a nested span. For example: + +```go +func parentFunction(ctx context.Context) { + ctx, parentSpan := tracer.Start(ctx, "parent") defer parentSpan.End() - // call our child function + + // call the child function and start a nested span in there childFunction(ctx) - // do more work, when this function ends, parentSpan will complete. + + // do more work - when this function ends, parentSpan will complete. } func childFunction(ctx context.Context) { - var childSpan trace.Span - ctx, childSpan = tracer.Start(ctx, "child") + // Create a span to track `childFunction()` - this is a nested span whose parent is `parentSpan` + ctx, childSpan := tracer.Start(ctx, "child") defer childSpan.End() + // do work here, when this function returns, childSpan will complete. } ``` Once a span has completed, it is immutable and can no longer be modified. -## Attributes +### Span Attributes Attributes are keys and values that are applied as metadata to your spans and are useful for aggregating, filtering, and grouping traces. Attributes can be added at span creation, or at any other time during the lifecycle of a span before it has completed. @@ -51,20 +77,20 @@ ctx, span = tracer.Start(ctx, "attributesAtCreation", trace.WithAttributes(attri span.SetAttributes(attribute.Bool("isTrue", true), attribute.String("stringAttr", "hi!")) ``` -Attribute keys can be precomputed, as well - +Attribute keys can be precomputed, as well: ```go var myKey = attribute.Key("myCoolAttribute") span.SetAttributes(myKey.String("a value")) ``` -### Semantic Attributes +#### Semantic Attributes Semantic Attributes are attributes that are defined by the [OpenTelemetry Specification][] in order to provide a shared set of attribute keys across multiple languages, frameworks, and runtimes for common concepts like HTTP methods, status codes, user agents, and more. These attributes are available in the `go.opentelemetry.io/otel/semconv/v1.7.0` package. For details, see [Trace semantic conventions][]. -## Events +### Events An event is a human-readable message on a span that represents "something happening" during it's lifetime. For example, imagine a function that requires exclusive access to a resource that is under a mutex. An event could be created at two points - once, when we try to gain access to the resource, and another when we acquire the mutex. @@ -85,11 +111,11 @@ Events can also have attributes of their own - span.AddEvent("Cancelled wait due to external signal", trace.WithAttributes(attribute.Int("pid", 4328), attribute.String("signal", "SIGHUP"))) ``` -# Creating Metrics +## Creating Metrics The metrics API is currently unstable, documentation TBA. -# Propagators and Context +## Propagators and Context Traces can extend beyond a single process. This requires _context propagation_, a mechanism where identifiers for a trace are sent to remote processes. @@ -107,3 +133,7 @@ otel.SetTextMapPropagator(propagation.TraceContext{}) > OpenTelemetry also supports the B3 header format, for compatibility with existing tracing systems (`go.opentelemetry.io/contrib/propagators/b3`) that do not support the W3C TraceContext standard. After configuring context propagation, you'll most likely want to use automatic instrumentation to handle the behind-the-scenes work of actually managing serializing the context. + +[OpenTelemetry Specification]: {{< relref "/docs/reference/specification" >}} +[Trace semantic conventions]: {{< relref "/docs/reference/specification/trace/semantic_conventions" >}} +[instrumentation library]: using_instrumentation_libraries