Files
opentf/internal/tracing/traceattrs/semconv.go
Martin Atkins 0503163e28 tracing: Centralize our OpenTelemetry package imports
OpenTelemetry has various Go packages split across several Go modules that
often need to be carefully upgraded together. And in particular, we are
using the "semconv" package in conjunction with the OpenTelemetry SDK's
"resource" package in a way that requires that they both agree on which
version of the OpenTelemetry Semantic Conventions are being followed.

To help avoid "dependency hell" situations when upgrading, this centralizes
all of our direct calls into the OpenTelemetry SDK and tracing API into
packages under internal/tracing, by exposing a few thin wrapper functions
that other packages can use to access the same functionality indirectly.

We only use a relatively small subset of the OpenTelemetry library surface
area, so we don't need too many of these reexports and they should not
represent a significant additional maintenance burden.

For the semconv and resource interaction in particular this also factors
that out into a separate helper function with a unit test, so we should
notice quickly whenever they become misaligned. This complements the
end-to-end test previously added in opentofu/opentofu#3447 to give us
faster feedback about this particular problem, while the end-to-end test
has the broader scope of making sure there aren't any errors at all when
initializing OpenTelemetry tracing.

Finally, this also replaces the constants we previously had in package
traceaddrs with functions that return attribute.KeyValue values directly.
This matches the API style used by the OpenTelemetry semconv packages, and
makes the calls to these helpers from elsewhere in the system a little
more concise.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
2025-10-30 13:27:10 -07:00

91 lines
3.3 KiB
Go

// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package traceattrs
// This file contains wrappers and reexports of some symbols from the
// OpenTelemetry "semconv" package and the "resource" package from the
// OpenTelemetry SDK, which we centralize here because their version numbers
// must be coordinated carefully to avoid runtime panics of mismatched versions.
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk"
"go.opentelemetry.io/otel/sdk/resource"
// The version number at the end of this package path MUST match the
// semconv version imported by the "go.opentelemetry.io/otel/sdk/resource",
// because we also use some semconv symbols indirectly through that
// package, and so we need to update this each time we upgrade the module
// "go.opentelemetry.io/otel/sdk".
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"github.com/opentofu/opentofu/version"
)
// NewResource constructs a *resource.Resource that should be used when
// constructing our global tracer provider.
//
// This is factored out here because its correct behavior depends on correctly
// matching our import of an "go.opentelemetry.io/otel/semconv/*" package
// for direct attribute definitions with the version used indirectly by
// "go.opentelemetry.io/otel/sdk/resource". If they don't match then this
// function will fail with an error.
//
// The unit test [TestNewResource] runs this function in isolation so we can
// make sure it succeeds without having to actually initialize the telemetry
// system.
func NewResource(ctx context.Context, serviceName string) (*resource.Resource, error) {
return resource.New(ctx,
// Use built-in detectors to simplify the collation of the tracing information
resource.WithOS(),
resource.WithHost(),
resource.WithProcess(),
resource.WithSchemaURL(semconv.SchemaURL),
resource.WithAttributes(),
// Add custom service attributes
resource.WithAttributes(
semconv.ServiceName(serviceName),
semconv.ServiceVersion(version.Version),
// We add in the telemetry SDK information so that we don't end up with
// duplicate schema urls that clash
semconv.TelemetrySDKName("opentelemetry"),
semconv.TelemetrySDKLanguageGo,
semconv.TelemetrySDKVersion(sdk.Version()),
),
)
}
// URLFull returns an attribute representing an absolute URL associated with
// a trace span, using the attribute name defined by our currently-selected
// version of the OpenTelemetry semantic conventions.
//
// This wraps [semconv.URLFull].
func URLFull(val string) attribute.KeyValue {
return semconv.URLFull(val)
}
// FilePath returns an attribute representing an absolute file path associated
// with a trace span, using the attribute name defined by our currently-selected
// version of the OpenTelemetry semantic conventions.
//
// This wraps [semconv.FilePath].
func FilePath(val string) attribute.KeyValue {
return semconv.FilePath(val)
}
// FileSize returns an attribute representing the size in bytes of a file
// associated with a trace span, using the attribute name defined by our
// currently-selected version of the OpenTelemetry semantic conventions.
//
// This wraps [semconv.FileSize].
func FileSize(val int) attribute.KeyValue {
return semconv.FileSize(val)
}