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>
This commit is contained in:
Martin Atkins
2025-10-29 17:06:02 -07:00
parent 6d0d9b8773
commit 0503163e28
34 changed files with 487 additions and 292 deletions

View File

@@ -15,8 +15,6 @@ import (
"log"
"sync"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
orasRemote "oras.land/oras-go/v2/registry/remote"
orasAuth "oras.land/oras-go/v2/registry/remote/auth"
orasCreds "oras.land/oras-go/v2/registry/remote/credentials"
@@ -27,6 +25,7 @@ import (
"github.com/opentofu/opentofu/internal/getproviders"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// ociCredsPolicyBuilder is the type of a callback function that the [providerSource]
@@ -77,9 +76,9 @@ func getOCIRepositoryStore(ctx context.Context, registryDomain, repositoryName s
ctx, span := tracing.Tracer().Start(
ctx, "Authenticate to OCI Registry",
otelTrace.WithAttributes(
otelAttr.String("opentofu.oci.registry.domain", registryDomain),
otelAttr.String("opentofu.oci.repository.name", repositoryName),
tracing.SpanAttributes(
traceattrs.String("opentofu.oci.registry.domain", registryDomain),
traceattrs.String("opentofu.oci.repository.name", repositoryName),
),
)
defer span.End()
@@ -189,9 +188,9 @@ func (o ociCredentialsLookupEnv) QueryDockerCredentialHelper(ctx context.Context
ctx, span := tracing.Tracer().Start(
ctx, "Query Docker-style credential helper",
otelTrace.WithAttributes(
otelAttr.String("opentofu.oci.docker_credential_helper.name", helperName),
otelAttr.String("opentofu.oci.registry.url", serverURL),
tracing.SpanAttributes(
traceattrs.String("opentofu.oci.docker_credential_helper.name", helperName),
traceattrs.String("opentofu.oci.registry.url", serverURL),
),
)
defer span.End()
@@ -203,14 +202,14 @@ func (o ociCredentialsLookupEnv) QueryDockerCredentialHelper(ctx context.Context
// than "Docker-style Credential Helper", but it's the
// same protocol nonetheless.
var executeSpan otelTrace.Span // ORAS tracing API can't directly propagate span from Start to Done
var executeSpan tracing.Span // ORAS tracing API can't directly propagate span from Start to Done
ctx = orasCredsTrace.WithExecutableTrace(ctx, &orasCredsTrace.ExecutableTrace{
ExecuteStart: func(executableName, action string) {
_, executeSpan = tracing.Tracer().Start(
ctx, "Execute helper program",
otelTrace.WithAttributes(
otelAttr.String("opentofu.oci.docker_credential_helper.executable", helperName),
otelAttr.String("opentofu.oci.registry.url", serverURL),
tracing.SpanAttributes(
traceattrs.String("opentofu.oci.docker_credential_helper.executable", helperName),
traceattrs.String("opentofu.oci.registry.url", serverURL),
),
)
log.Printf("[DEBUG] Executing docker-style credentials helper %q for %s", helperName, serverURL)

View File

@@ -2,13 +2,14 @@
This document describes how to use and implement tracing in OpenTofu Core using OpenTelemetry.
> [!NOTE]
> For background on the design decisions and motivation behind OpenTofu's tracing implementation, see the [OpenTelemetry Tracing RFC](https://github.com/opentofu/opentofu/blob/main/rfc/20250129-Tracing-For-Extra-Context.md).
There's background information on OpenTofu's tracing implementation in [the OpenTelemetry Tracing RFC](https://github.com/opentofu/opentofu/blob/main/rfc/20250129-Tracing-For-Extra-Context.md)
> [!NOTE]
> If you are upgrading any dependent libraries which pull in a new OTEL version, you *MUST* update the semconv version in tracing/init.go to the latest version.
> Failing to do this will result in an error "Could not initialize telemetry: failed to create resource: error detecting resource: conflicting Schema URL".
> This sets the *maximum* supported schema version in our OTEL context. Semconv is backwards compatible with older versions, but the newest must be specified.
> [!WARNING]
> If you change which version of the `go.opentelemetry.io/otel/sdk` we have selected in our `go.mod`, you **must** make sure that `internal/tracing/traceattrs/semconv.go` imports the same subpackage of `go.opentelemetry.io/otel/semconv/*` that is used by the selected version of `go.opentelemetry.io/otel/sdk`.
>
> This is important because our tracing setup uses a blend of directly-constructed `semconv` attributes and attributes chosen indirectly through the `resource` package, and they must all be using the same version of the semantic conventions schema or there will be a "conflicting Schema URL" error at runtime.
>
> (Problems of this sort should be detected both by a unit test in `internal/tracing/traceattrs` and an end-to-end test that executes OpenTofu with tracing enabled.)
## Overview
@@ -61,7 +62,7 @@ Then configure OpenTofu as shown above and access the Jaeger UI at http://localh
## Adding Tracing to OpenTofu Code
> [!NOTE]
> **For Contributors**: When adding tracing to OpenTofu, remember that the primary audience is **end users** who need to understand performance, not developers. Add spans sparingly to avoid polluting traces with too much detail.
> **For Contributors**: When adding tracing to OpenTofu, remember that the primary audience is **end users** who need to understand performance, not OpenTofu developers. Add spans sparingly to avoid polluting traces with too much detail.
### Basic Span Creation
@@ -69,19 +70,25 @@ Then configure OpenTofu as shown above and access the Jaeger UI at http://localh
import (
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
otelAttr "go.opentelemetry.io/otel/attribute" // Note the alias
)
func SomeFunction(ctx context.Context) error {
// Create a new span
ctx, span := tracing.Tracer().Start(ctx, "Human readable operation name")
ctx, span := tracing.Tracer().Start(ctx, "Human readable operation name",
tracing.SpanAttributes(
traceattrs.String("opentofu.some_attribute", "value")
),
)
defer span.End()
// Add attributes to provide context
span.SetAttributes(otelAttr.String("opentofu.some.attribute", "value"))
// Optionally add additional attributes after the span is created, if
// they only need to appear in certain cases.
span.SetAttributes(traceattrs.String("opentofu.some_other_attribute", "value"))
// Using predefined attributes from traceattrs package
span.SetAttributes(otelAttr.String(traceattrs.ProviderAddress, "hashicorp/aws"))
// Use the more specific attribute-construction helpers from package
// traceattrs where they are relevant, to ensure we follow consistent
// semantic conventions for cross-cutting concerns.
span.SetAttributes(traceattrs.OpenTofuProviderAddress("hashicorp/aws"))
// Your function logic here...
@@ -95,9 +102,14 @@ func SomeFunction(ctx context.Context) error {
}
```
> [!TIP]
> We should use the `otelAttr` alias for OpenTelemetry's attribute package to clearly distinguish it from OpenTofu's trace attribute constants in the `traceattrs` package.
> This convention makes the code more readable and prevents import conflicts.
OpenTelemetry has many different packages spread across a variety of different Go modules, and those different modules often need to be upgraded together to ensure consistent behavior and avoid errors at runtime.
Therefore we prefer to directly import `go.opentelemetry.io/otel/*` packages only from our packages under `internal/tracing`, and then reexport certain functions from our own packages so that we can manage all of the OpenTelemetry dependencies in a centralized place to minimize "dependency hell" problems when upgrading. Packages under `go.opentelemetry.io/contrib/instrumentation/*` are an exception because they tend to be more tightly-coupled to whatever they are instrumenting than to the other OpenTelemetry packages, and so it's better to import those from the same file that's importing whatever other package the instrumentation is being applied to.
> [!WARNING]
> Don't import `go.opentelemetry.io/otel/semconv/*` packages from anywhere except `internal/tracing/traceattrs/semconv.go`!
>
> If you want to use standard OpenTelemetry semantic conventions from other packages, use them indirectly through reexports in `package traceattrs` instead, so we can make sure there's only one file in OpenTofu deciding which version of semconv we are currently depending on.
### Tracing Conventions
@@ -110,17 +122,10 @@ func SomeFunction(ctx context.Context) error {
#### Attributes
- Prefer standard [OpenTelemetry semantic conventions](https://opentelemetry.io/docs/specs/semconv/) where applicable
- Follow the [OpenTelemetry attribute naming convention](https://opentelemetry.io/docs/specs/semconv/general/naming/)
- Cross-cutting attributes are defined in `internal/tracing/traceattrs`
```go
// Good attribute names
"opentofu.provider.address" // For provider addresses
"opentofu.module.source" // For module sources
"opentofu.operation.target_count" // For operation-specific counts
```
- Prefer standard [OpenTelemetry semantic conventions](https://opentelemetry.io/docs/specs/semconv/) where applicable, using helper functions from [`internal/tracing/traceattrs`](https://pkg.go.dev/github.com/opentofu/opentofu/internal/tracing/traceattrs).
- Use `OpenTofu`-prefixed functions in [`internal/tracing/traceattrs`](https://pkg.go.dev/github.com/opentofu/opentofu/internal/tracing/traceattrs) for OpenTofu-specific cross-cutting concerns.
- It's okay to use one-off inline strings for attribute names specific to a single span, but make sure to still follow the [OpenTelemetry attribute naming conventions](https://opentelemetry.io/docs/specs/semconv/general/naming/) and use the `opentofu.` prefix for anything that is not a standardized semantic convention.
- If a particular subsystem of OpenTofu has some repeated conventions for attribute names, consider creating unexported string constants or attribute construction helper functions in the same package to centralize those naming conventions.
#### Error Handling

View File

@@ -17,8 +17,6 @@ import (
"github.com/opentofu/svchost"
"github.com/posener/complete"
"github.com/zclconf/go-cty/cty"
otelAttr "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/backend"
@@ -36,6 +34,7 @@ import (
"github.com/opentofu/opentofu/internal/tofu"
"github.com/opentofu/opentofu/internal/tofumigrate"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
tfversion "github.com/opentofu/opentofu/version"
)
@@ -164,8 +163,8 @@ func (c *InitCommand) Run(args []string) int {
ShowLocalPaths: false, // since they are in a weird location for init
}
ctx, span := tracing.Tracer().Start(ctx, "From module", trace.WithAttributes(
otelAttr.String("opentofu.module_source", src),
ctx, span := tracing.Tracer().Start(ctx, "From module", tracing.SpanAttributes(
traceattrs.OpenTofuModuleSource(src),
))
defer span.End()
@@ -407,8 +406,8 @@ func (c *InitCommand) getModules(ctx context.Context, path, testsDir string, ear
return false, false, nil
}
ctx, span := tracing.Tracer().Start(ctx, "Get Modules", trace.WithAttributes(
otelAttr.Bool("opentofu.modules.upgrade", upgrade),
ctx, span := tracing.Tracer().Start(ctx, "Get Modules", tracing.SpanAttributes(
traceattrs.Bool("opentofu.modules.upgrade", upgrade),
))
defer span.End()

View File

@@ -10,14 +10,13 @@ import (
"net/url"
"os"
otelAttr "go.opentelemetry.io/otel/attribute"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/depsfile"
"github.com/opentofu/opentofu/internal/getproviders"
"github.com/opentofu/opentofu/internal/providercache"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
type providersLockChangeType string
@@ -61,12 +60,12 @@ func (c *ProvidersLockCommand) Run(args []string) int {
return 1
}
span.SetAttributes(otelAttr.StringSlice("opentofu.provider.lock.targetplatforms", optPlatforms))
span.SetAttributes(traceattrs.StringSlice("opentofu.provider.lock.targetplatforms", optPlatforms))
if fsMirrorDir != "" {
span.SetAttributes(otelAttr.String("opentofu.provider.lock.fsmirror", fsMirrorDir))
span.SetAttributes(traceattrs.String("opentofu.provider.lock.fsmirror", fsMirrorDir))
}
if netMirrorURL != "" {
span.SetAttributes(otelAttr.String("opentofu.provider.lock.netmirror", netMirrorURL))
span.SetAttributes(traceattrs.String("opentofu.provider.lock.netmirror", netMirrorURL))
}
var diags tfdiags.Diagnostics
@@ -88,7 +87,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
if len(optPlatforms) == 0 {
platforms = []getproviders.Platform{getproviders.CurrentPlatform}
span.SetAttributes(
otelAttr.StringSlice("opentofu.provider.lock.targetplatforms", []string{getproviders.CurrentPlatform.String()}),
traceattrs.StringSlice("opentofu.provider.lock.targetplatforms", []string{getproviders.CurrentPlatform.String()}),
)
} else {
platforms = make([]getproviders.Platform, 0, len(optPlatforms))

View File

@@ -12,9 +12,6 @@ import (
"os"
"strings"
otelAttr "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/cloud"
"github.com/opentofu/opentofu/internal/cloud/cloudplan"
@@ -29,6 +26,7 @@ import (
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tofu"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// Many of the methods we get data from can emit special error types if they're
@@ -76,11 +74,11 @@ func (c *ShowCommand) Run(rawArgs []string) int {
//nolint:ineffassign - As this is a high-level call, we want to ensure that we are correctly using the right ctx later on when
ctx, span := tracing.Tracer().Start(ctx, "Show",
trace.WithAttributes(
otelAttr.String("opentofu.show.view", args.ViewType.String()),
otelAttr.String("opentofu.show.target", args.TargetType.String()),
otelAttr.String("opentofu.show.target_arg", args.TargetArg),
otelAttr.Bool("opentofu.show.show_sensitive", args.ShowSensitive),
tracing.SpanAttributes(
traceattrs.String("opentofu.show.view", args.ViewType.String()),
traceattrs.String("opentofu.show.target", args.TargetType.String()),
traceattrs.String("opentofu.show.target_arg", args.TargetArg),
traceattrs.Bool("opentofu.show.show_sensitive", args.ShowSensitive),
),
)
defer span.End()

View File

@@ -16,14 +16,13 @@ import (
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/zclconf/go-cty/cty"
semconv "go.opentelemetry.io/otel/semconv/v1.30.0"
"go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/getproviders"
"github.com/opentofu/opentofu/internal/replacefile"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
"github.com/opentofu/opentofu/version"
)
@@ -95,7 +94,9 @@ func loadLocks(loadParse func(*hclparse.Parser) (*hcl.File, hcl.Diagnostics)) (*
// temporary files may be temporarily created in the same directory as the
// given filename during the operation.
func SaveLocksToFile(ctx context.Context, locks *Locks, filename string) tfdiags.Diagnostics {
_, span := tracing.Tracer().Start(ctx, "Save lockfile", trace.WithAttributes(semconv.FileName(filename)))
_, span := tracing.Tracer().Start(ctx, "Save lockfile", tracing.SpanAttributes(
traceattrs.FilePath(filename),
))
defer span.End()
src, diags := SaveLocksToBytes(locks)
@@ -105,7 +106,7 @@ func SaveLocksToFile(ctx context.Context, locks *Locks, filename string) tfdiags
}
span.AddEvent("Serialized lockfile")
span.SetAttributes(semconv.FileSize(len(src)))
span.SetAttributes(traceattrs.FileSize(len(src)))
err := replacefile.AtomicWriteFile(filename, src, 0644)
if err != nil {

View File

@@ -10,12 +10,11 @@ import (
"fmt"
"maps"
semconv "go.opentelemetry.io/otel/semconv/v1.30.0"
"go.opentelemetry.io/otel/trace"
getter "github.com/hashicorp/go-getter"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// PackageFetcher is a low-level utility for fetching remote module packages
@@ -90,7 +89,7 @@ func NewPackageFetcher(ctx context.Context, env PackageFetcherEnvironment) *Pack
// getmodules.SplitPackageSubdir and getmodules.ExpandSubdirGlobs functions.
func (f *PackageFetcher) FetchPackage(ctx context.Context, instDir string, packageAddr string) error {
ctx, span := tracing.Tracer().Start(ctx, "Fetch Package",
trace.WithAttributes(semconv.URLFull(packageAddr)),
tracing.SpanAttributes(traceattrs.URLFull(packageAddr)),
)
defer span.End()
err := f.getter.getWithGoGetter(ctx, instDir, packageAddr)

View File

@@ -17,11 +17,11 @@ import (
getter "github.com/hashicorp/go-getter"
ociDigest "github.com/opencontainers/go-digest"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opentofu/opentofu/internal/tracing"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
orasContent "oras.land/oras-go/v2/content"
orasRegistry "oras.land/oras-go/v2/registry"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// ociImageManifestArtifactType is the artifact type we expect for the image
@@ -71,9 +71,9 @@ func (g *ociDistributionGetter) Get(destDir string, url *url.URL) error {
ctx, span := tracing.Tracer().Start(
ctx, "Fetch 'oci' module package",
otelTrace.WithAttributes(
otelAttr.String("opentofu.module.source", url.String()),
otelAttr.String("opentofu.module.local_dir", destDir),
tracing.SpanAttributes(
traceattrs.String("opentofu.module.source", url.String()),
traceattrs.String("opentofu.module.local_dir", destDir),
),
)
defer span.End()
@@ -220,9 +220,9 @@ func (g *ociDistributionGetter) resolveRepositoryRef(url *url.URL) (*orasRegistr
func (g *ociDistributionGetter) resolveManifestDescriptor(ctx context.Context, ref *orasRegistry.Reference, query url.Values, store OCIRepositoryStore) (desc ociv1.Descriptor, err error) {
ctx, span := tracing.Tracer().Start(
ctx, "Resolve reference",
otelTrace.WithAttributes(
otelAttr.String("opentofu.oci.registry.domain", ref.Registry),
otelAttr.String("opentofu.oci.repository.name", ref.Repository),
tracing.SpanAttributes(
traceattrs.String("opentofu.oci.registry.domain", ref.Registry),
traceattrs.String("opentofu.oci.repository.name", ref.Repository),
),
)
defer span.End()
@@ -279,7 +279,7 @@ func (g *ociDistributionGetter) resolveManifestDescriptor(ctx context.Context, r
// If we're starting with a tag name then we need to query the
// repository to find out which digest is currently selected.
span.SetAttributes(
otelAttr.String("opentofu.oci.reference.tag", wantTag),
traceattrs.String("opentofu.oci.reference.tag", wantTag),
)
desc, err = store.Resolve(ctx, wantTag)
if err != nil {
@@ -294,7 +294,7 @@ func (g *ociDistributionGetter) resolveManifestDescriptor(ctx context.Context, r
// and so we can't exercise this specific case from unit tests
// using in-memory or on-disk fakes. :(
span.SetAttributes(
otelAttr.String("opentofu.oci.reference.digest", wantDigest.String()),
traceattrs.String("opentofu.oci.reference.digest", wantDigest.String()),
)
desc, err = store.Resolve(ctx, wantDigest.String())
if err != nil {
@@ -303,9 +303,9 @@ func (g *ociDistributionGetter) resolveManifestDescriptor(ctx context.Context, r
}
span.SetAttributes(
otelAttr.String("oci.manifest.digest", desc.Digest.String()),
otelAttr.String("opentofu.oci.manifest.media_type", desc.MediaType),
otelAttr.Int64("opentofu.oci.manifest.size", desc.Size),
traceattrs.String("oci.manifest.digest", desc.Digest.String()),
traceattrs.String("opentofu.oci.manifest.media_type", desc.MediaType),
traceattrs.Int64("opentofu.oci.manifest.size", desc.Size),
)
// The initial request is only required to return a "plain" descriptor,
@@ -326,9 +326,9 @@ func (g *ociDistributionGetter) resolveManifestDescriptor(ctx context.Context, r
func fetchOCIImageManifest(ctx context.Context, desc ociv1.Descriptor, store OCIRepositoryStore) (*ociv1.Manifest, error) {
ctx, span := tracing.Tracer().Start(
ctx, "Fetch manifest",
otelTrace.WithAttributes(
otelAttr.String("oci.manifest.digest", desc.Digest.String()),
otelAttr.Int64("opentofu.oci.manifest.size", desc.Size),
tracing.SpanAttributes(
traceattrs.String("oci.manifest.digest", desc.Digest.String()),
traceattrs.Int64("opentofu.oci.manifest.size", desc.Size),
),
)
defer span.End()
@@ -356,8 +356,8 @@ func fetchOCIImageManifest(ctx context.Context, desc ociv1.Descriptor, store OCI
}
span.SetAttributes(
otelAttr.String("opentofu.oci.manifest.media_type", desc.MediaType),
otelAttr.String("opentofu.oci.manifest.artifact_type", desc.ArtifactType),
traceattrs.String("opentofu.oci.manifest.media_type", desc.MediaType),
traceattrs.String("opentofu.oci.manifest.artifact_type", desc.ArtifactType),
)
// Now we'll make sure that what we decoded seems vaguely sensible before we
@@ -454,10 +454,10 @@ func selectOCILayerBlob(descs []ociv1.Descriptor) (ociv1.Descriptor, error) {
func fetchOCIBlobToTemporaryFile(ctx context.Context, desc ociv1.Descriptor, store orasContent.Fetcher) (tempFile string, err error) {
ctx, span := tracing.Tracer().Start(
ctx, "Fetch module package",
otelTrace.WithAttributes(
otelAttr.String("opentofu.oci.blob.digest", desc.Digest.String()),
otelAttr.String("opentofu.oci.blob.media_type", desc.MediaType),
otelAttr.Int64("opentofu.oci.blob.size", desc.Size),
tracing.SpanAttributes(
traceattrs.String("opentofu.oci.blob.digest", desc.Digest.String()),
traceattrs.String("opentofu.oci.blob.media_type", desc.MediaType),
traceattrs.Int64("opentofu.oci.blob.size", desc.Size),
),
)
defer span.End()

View File

@@ -16,8 +16,6 @@ import (
"github.com/apparentlymart/go-versions/versions"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
orasErrors "oras.land/oras-go/v2/errdef"
orasRegistryErrors "oras.land/oras-go/v2/registry/remote/errcode"
@@ -163,8 +161,8 @@ func NewOCIRegistryMirrorSource(
func (o *OCIRegistryMirrorSource) AvailableVersions(ctx context.Context, provider addrs.Provider) (VersionList, Warnings, error) {
ctx, span := tracing.Tracer().Start(
ctx, "Get available versions from oci_mirror",
otelTrace.WithAttributes(
otelAttr.String(traceattrs.ProviderAddress, provider.String()),
tracing.SpanAttributes(
traceattrs.OpenTofuProviderAddress(provider.String()),
),
)
defer span.End()
@@ -278,10 +276,10 @@ func errRepresentsOCIProviderNotFound(err error) bool {
func (o *OCIRegistryMirrorSource) PackageMeta(ctx context.Context, provider addrs.Provider, version Version, target Platform) (PackageMeta, error) {
ctx, span := tracing.Tracer().Start(
ctx, "Get metadata from oci_mirror",
otelTrace.WithAttributes(
otelAttr.String(traceattrs.ProviderAddress, provider.String()),
otelAttr.String(traceattrs.ProviderVersion, version.String()),
otelAttr.String(traceattrs.TargetPlatform, target.String()),
tracing.SpanAttributes(
traceattrs.OpenTofuProviderAddress(provider.String()),
traceattrs.OpenTofuProviderVersion(version.String()),
traceattrs.OpenTofuTargetPlatform(target.String()),
),
)
defer span.End()
@@ -476,8 +474,8 @@ type OCIRepositoryStore interface {
func fetchOCIDescriptorForVersion(ctx context.Context, version versions.Version, store OCIRepositoryStore) (ociv1.Descriptor, error) {
ctx, span := tracing.Tracer().Start(
ctx, "Resolve reference",
otelTrace.WithAttributes(
otelAttr.String(traceattrs.ProviderVersion, version.String()),
tracing.SpanAttributes(
traceattrs.OpenTofuProviderVersion(version.String()),
),
)
defer span.End()
@@ -495,9 +493,9 @@ func fetchOCIDescriptorForVersion(ctx context.Context, version versions.Version,
return ociv1.Descriptor{}, prepErr(fmt.Errorf("resolving tag %q: %w", tagName, err))
}
span.SetAttributes(
otelAttr.String("oci.manifest.digest", desc.Digest.String()),
otelAttr.String("opentofu.oci.reference.tag", tagName),
otelAttr.String("opentofu.oci.manifest.media_type", desc.MediaType),
traceattrs.String("oci.manifest.digest", desc.Digest.String()),
traceattrs.String("opentofu.oci.reference.tag", tagName),
traceattrs.String("opentofu.oci.manifest.media_type", desc.MediaType),
)
// Not all store implementations can return the manifest's artifact type as part
// of the tag-resolution response, so we'll check this early if we can, but
@@ -507,7 +505,7 @@ func fetchOCIDescriptorForVersion(ctx context.Context, version versions.Version,
// one way or another.)
if desc.ArtifactType != "" && desc.ArtifactType != ociIndexManifestArtifactType {
span.SetAttributes(
otelAttr.String("opentofu.oci.manifest.artifact_type", desc.ArtifactType),
traceattrs.String("opentofu.oci.manifest.artifact_type", desc.ArtifactType),
)
switch desc.ArtifactType {
case "application/vnd.opentofu.provider-target":
@@ -546,9 +544,9 @@ func fetchOCIDescriptorForVersion(ctx context.Context, version versions.Version,
func fetchOCIIndexManifest(ctx context.Context, desc ociv1.Descriptor, store OCIRepositoryStore) (*ociv1.Index, error) {
ctx, span := tracing.Tracer().Start(
ctx, "Fetch index manifest",
otelTrace.WithAttributes(
otelAttr.String("oci.manifest.digest", desc.Digest.String()),
otelAttr.Int64("opentofu.oci.manifest.size", desc.Size),
tracing.SpanAttributes(
traceattrs.String("oci.manifest.digest", desc.Digest.String()),
traceattrs.Int64("opentofu.oci.manifest.size", desc.Size),
),
)
defer span.End()
@@ -575,8 +573,8 @@ func fetchOCIIndexManifest(ctx context.Context, desc ociv1.Descriptor, store OCI
return nil, prepErr(fmt.Errorf("invalid manifest content: %w", err))
}
span.SetAttributes(
otelAttr.String("opentofu.oci.manifest.media_type", manifest.MediaType),
otelAttr.String("opentofu.oci.manifest.artifact_type", manifest.ArtifactType),
traceattrs.String("opentofu.oci.manifest.media_type", manifest.MediaType),
traceattrs.String("opentofu.oci.manifest.artifact_type", manifest.ArtifactType),
)
// Now we'll make sure that what we decoded seems vaguely sensible before we
@@ -598,9 +596,9 @@ func fetchOCIIndexManifest(ctx context.Context, desc ociv1.Descriptor, store OCI
func fetchOCIImageManifest(ctx context.Context, desc ociv1.Descriptor, store OCIRepositoryStore) (*ociv1.Manifest, error) {
ctx, span := tracing.Tracer().Start(
ctx, "Fetch platform-specific manifest",
otelTrace.WithAttributes(
otelAttr.String("oci.manifest.digest", desc.Digest.String()),
otelAttr.Int64("opentofu.oci.manifest.size", desc.Size),
tracing.SpanAttributes(
traceattrs.String("oci.manifest.digest", desc.Digest.String()),
traceattrs.Int64("opentofu.oci.manifest.size", desc.Size),
),
)
defer span.End()
@@ -627,8 +625,8 @@ func fetchOCIImageManifest(ctx context.Context, desc ociv1.Descriptor, store OCI
return nil, prepErr(fmt.Errorf("invalid manifest content: %w", err))
}
span.SetAttributes(
otelAttr.String("opentofu.oci.manifest.media_type", manifest.MediaType),
otelAttr.String("opentofu.oci.manifest.artifact_type", manifest.ArtifactType),
traceattrs.String("opentofu.oci.manifest.media_type", manifest.MediaType),
traceattrs.String("opentofu.oci.manifest.artifact_type", manifest.ArtifactType),
)
// Now we'll make sure that what we decoded seems vaguely sensible before we

View File

@@ -14,12 +14,11 @@ import (
"github.com/hashicorp/go-getter"
"github.com/hashicorp/go-retryablehttp"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/logging"
semconv "go.opentelemetry.io/otel/semconv/v1.30.0"
"go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// PackageHTTPURL is a provider package location accessible via HTTP.
@@ -53,8 +52,8 @@ func (p PackageHTTPURL) String() string { return p.URL }
func (p PackageHTTPURL) InstallProviderPackage(ctx context.Context, meta PackageMeta, targetDir string, allowedHashes []Hash) (*PackageAuthenticationResult, error) {
url := meta.Location.String()
ctx, span := tracing.Tracer().Start(ctx, "Install (http)", trace.WithAttributes(
semconv.URLFull(url),
ctx, span := tracing.Tracer().Start(ctx, "Install (http)", tracing.SpanAttributes(
traceattrs.URLFull(url),
))
defer span.End()

View File

@@ -12,9 +12,9 @@ import (
"os"
"github.com/hashicorp/go-getter"
semconv "go.opentelemetry.io/otel/semconv/v1.30.0"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// We borrow the "unpack a zip file into a target directory" logic from
@@ -67,7 +67,7 @@ func (p PackageLocalArchive) InstallProviderPackage(ctx context.Context, meta Pa
}
filename := meta.Location.String()
span.SetAttributes(semconv.FilePath(filename))
span.SetAttributes(traceattrs.FilePath(filename))
// NOTE: Packages are immutable, but we may want to skip overwriting the existing
// files in due to specific scenarios defined below.

View File

@@ -16,11 +16,10 @@ import (
"github.com/hashicorp/go-getter"
ociDigest "github.com/opencontainers/go-digest"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
orasContent "oras.land/oras-go/v2/content"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
orasContent "oras.land/oras-go/v2/content"
)
// ociPackageMediaType is the specific media type we're expecting for the blob
@@ -88,16 +87,16 @@ func (p PackageOCIBlobArchive) InstallProviderPackage(ctx context.Context, meta
pkgDesc := p.blobDescriptor
ctx, span := tracing.Tracer().Start(
ctx, "Fetch provider package",
otelTrace.WithAttributes(
otelAttr.String("opentofu.oci.registry.domain", p.registryDomain),
otelAttr.String("opentofu.oci.repository.name", p.repositoryName),
otelAttr.String("opentofu.oci.blob.digest", pkgDesc.Digest.String()),
otelAttr.String("opentofu.oci.blob.media_type", pkgDesc.MediaType),
otelAttr.Int64("opentofu.oci.blob.size", pkgDesc.Size),
otelAttr.String("opentofu.provider.local_dir", targetDir),
otelAttr.String(traceattrs.ProviderAddress, meta.Provider.String()),
otelAttr.String(traceattrs.ProviderVersion, meta.Version.String()),
otelAttr.String(traceattrs.TargetPlatform, meta.TargetPlatform.String()),
tracing.SpanAttributes(
traceattrs.String("opentofu.oci.registry.domain", p.registryDomain),
traceattrs.String("opentofu.oci.repository.name", p.repositoryName),
traceattrs.String("opentofu.oci.blob.digest", pkgDesc.Digest.String()),
traceattrs.String("opentofu.oci.blob.media_type", pkgDesc.MediaType),
traceattrs.Int64("opentofu.oci.blob.size", pkgDesc.Size),
traceattrs.String("opentofu.provider.local_dir", targetDir),
traceattrs.OpenTofuProviderAddress(meta.Provider.String()),
traceattrs.OpenTofuProviderVersion(meta.Version.String()),
traceattrs.OpenTofuTargetPlatform(meta.TargetPlatform.String()),
),
)
defer span.End()

View File

@@ -20,9 +20,6 @@ import (
"github.com/hashicorp/go-retryablehttp"
"github.com/opentofu/svchost"
"github.com/opentofu/svchost/svcauth"
otelAttr "go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
"go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/tracing"
@@ -67,8 +64,8 @@ func newRegistryClient(ctx context.Context, baseURL *url.URL, creds svcauth.Host
func (c *registryClient) ProviderVersions(ctx context.Context, addr addrs.Provider) (map[string][]string, []string, error) {
ctx, span := tracing.Tracer().Start(ctx,
"List Versions",
trace.WithAttributes(
otelAttr.String(traceattrs.ProviderAddress, addr.String()),
tracing.SpanAttributes(
traceattrs.OpenTofuProviderAddress(addr.String()),
),
)
defer span.End()
@@ -79,7 +76,7 @@ func (c *registryClient) ProviderVersions(ctx context.Context, addr addrs.Provid
return nil, nil, err
}
endpointURL := c.baseURL.ResolveReference(endpointPath)
span.SetAttributes(semconv.URLFull(endpointURL.String()))
span.SetAttributes(traceattrs.URLFull(endpointURL.String()))
req, err := retryablehttp.NewRequest("GET", endpointURL.String(), nil)
if err != nil {
return nil, nil, err
@@ -167,9 +164,9 @@ func (c *registryClient) PackageMeta(ctx context.Context, provider addrs.Provide
))
ctx, span := tracing.Tracer().Start(ctx,
"Fetch metadata",
trace.WithAttributes(
otelAttr.String(traceattrs.ProviderAddress, provider.String()),
otelAttr.String(traceattrs.ProviderVersion, version.String()),
tracing.SpanAttributes(
traceattrs.OpenTofuProviderAddress(provider.String()),
traceattrs.OpenTofuProviderVersion(version.String()),
))
defer span.End()
@@ -180,7 +177,7 @@ func (c *registryClient) PackageMeta(ctx context.Context, provider addrs.Provide
}
endpointURL := c.baseURL.ResolveReference(endpointPath)
span.SetAttributes(
semconv.URLFull(endpointURL.String()),
traceattrs.URLFull(endpointURL.String()),
)
req, err := retryablehttp.NewRequest("GET", endpointURL.String(), nil)

View File

@@ -11,8 +11,8 @@ import (
cleanhttp "github.com/hashicorp/go-cleanhttp"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
otelTrace "go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/version"
)
@@ -34,7 +34,7 @@ func New(ctx context.Context) *http.Client {
inner: cli.Transport,
}
if span := otelTrace.SpanFromContext(ctx); span != nil && span.IsRecording() {
if span := tracing.SpanFromContext(ctx); span != nil && span.IsRecording() {
// We consider the presence of an active span -- that is, one whose
// presence is going to be reported to a trace collector outside of
// the OpenTofu process -- as sufficient signal that generating

View File

@@ -20,8 +20,6 @@ import (
version "github.com/hashicorp/go-version"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
otelAttr "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs"
@@ -242,10 +240,11 @@ func (i *ModuleInstaller) moduleInstallWalker(_ context.Context, manifest modsdi
ctx, span := tracing.Tracer().Start(ctx,
fmt.Sprintf("Install Module %q", req.Name),
trace.WithAttributes(
otelAttr.String(traceattrs.ModuleCallName, req.Name),
otelAttr.String(traceattrs.ModuleSource, req.SourceAddr.String()),
))
tracing.SpanAttributes(
traceattrs.OpenTofuModuleCallName(req.Name),
traceattrs.OpenTofuModuleSource(req.SourceAddr.String()),
),
)
defer span.End()
log.Printf("[DEBUG] Module installer: begin %s", key)
@@ -343,7 +342,7 @@ func (i *ModuleInstaller) moduleInstallWalker(_ context.Context, manifest modsdi
case addrs.ModuleSourceLocal:
log.Printf("[TRACE] ModuleInstaller: %s has local path %q", key, addr.String())
span.SetAttributes(otelAttr.String("opentofu.module.source_type", "local"))
span.SetAttributes(traceattrs.String("opentofu.module.source_type", "local"))
mod, mDiags := i.installLocalModule(ctx, req, key, manifest, hooks)
mDiags = maybeImproveLocalInstallError(req, mDiags)
diags = append(diags, mDiags...)
@@ -351,7 +350,7 @@ func (i *ModuleInstaller) moduleInstallWalker(_ context.Context, manifest modsdi
case addrs.ModuleSourceRegistry:
log.Printf("[TRACE] ModuleInstaller: %s is a registry module at %s", key, addr.String())
span.SetAttributes(otelAttr.String("opentofu.module.source_type", "registry"))
span.SetAttributes(traceattrs.String("opentofu.module.source_type", "registry"))
mod, v, mDiags := i.installRegistryModule(ctx, req, key, instPath, addr, manifest, hooks, fetcher)
diags = append(diags, mDiags...)
return mod, v, diags
@@ -426,9 +425,9 @@ func (i *ModuleInstaller) installDescendentModules(ctx context.Context, rootMod
func (i *ModuleInstaller) installLocalModule(ctx context.Context, req *configs.ModuleRequest, key string, manifest modsdir.Manifest, hooks ModuleInstallHooks) (*configs.Module, hcl.Diagnostics) {
var diags hcl.Diagnostics
_, span := tracing.Tracer().Start(ctx, "Install Local Module",
trace.WithAttributes(otelAttr.String(traceattrs.ModuleCallName, req.Name)),
trace.WithAttributes(otelAttr.String(traceattrs.ModuleSource, req.SourceAddr.String())),
_, span := tracing.Tracer().Start(ctx, "Install Local Module", tracing.SpanAttributes(
traceattrs.OpenTofuModuleCallName(req.Name),
traceattrs.OpenTofuModuleSource(req.SourceAddr.String())),
)
defer span.End()
@@ -508,11 +507,11 @@ var versionRegexp = regexp.MustCompile(version.VersionRegexpRaw)
func (i *ModuleInstaller) installRegistryModule(ctx context.Context, req *configs.ModuleRequest, key string, instPath string, addr addrs.ModuleSourceRegistry, manifest modsdir.Manifest, hooks ModuleInstallHooks, fetcher *getmodules.PackageFetcher) (*configs.Module, *version.Version, hcl.Diagnostics) {
var diags hcl.Diagnostics
ctx, span := tracing.Tracer().Start(ctx, "Install Registry Module",
trace.WithAttributes(otelAttr.String(traceattrs.ModuleCallName, req.Name)),
trace.WithAttributes(otelAttr.String(traceattrs.ModuleSource, req.SourceAddr.String())),
trace.WithAttributes(otelAttr.String(traceattrs.ModuleVersion, req.VersionConstraint.Required.String())),
)
ctx, span := tracing.Tracer().Start(ctx, "Install Registry Module", tracing.SpanAttributes(
traceattrs.OpenTofuModuleCallName(req.Name),
traceattrs.OpenTofuModuleSource(req.SourceAddr.String()),
traceattrs.OpenTofuModuleVersion(req.VersionConstraint.Required.String()),
))
defer span.End()
if i.reg == nil || fetcher == nil {
@@ -774,7 +773,7 @@ func (i *ModuleInstaller) installRegistryModule(ctx context.Context, req *config
return nil, nil, diags
}
span.SetAttributes(otelAttr.String(traceattrs.ModuleSource, realAddr.String()))
span.SetAttributes(traceattrs.OpenTofuModuleSource(realAddr.String()))
switch realAddr := realAddr.(type) {
// Only a remote source address is allowed here: a registry isn't

View File

@@ -14,8 +14,6 @@ import (
"strings"
"github.com/apparentlymart/go-versions/versions"
otelAttr "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
copydir "github.com/opentofu/opentofu/internal/copy"
@@ -235,7 +233,7 @@ func (i *Installer) EnsureProviderVersions(ctx context.Context, locks *depsfile.
// Step 3: For each provider version we've decided we need to install,
// install its package into our target cache (possibly via the global cache).
targetPlatform := i.targetDir.targetPlatform // we inherit this to behave correctly in unit tests
span.SetAttributes(otelAttr.String(traceattrs.TargetPlatform, targetPlatform.String()))
span.SetAttributes(traceattrs.OpenTofuTargetPlatform(targetPlatform.String()))
span.SetName("Install Providers - " + targetPlatform.String())
authResults, err := i.ensureProviderVersionsInstall(ctx, locks, reqs, mode, need, targetPlatform, errs)
if err != nil {
@@ -449,10 +447,10 @@ func (i *Installer) ensureProviderVersionsInstall(
for provider, version := range need {
traceCtx, span := tracing.Tracer().Start(ctx,
fmt.Sprintf("Install Provider %q", provider.String()),
trace.WithAttributes(
otelAttr.String(traceattrs.ProviderAddress, provider.String()),
otelAttr.String(traceattrs.ProviderVersion, version.String()),
otelAttr.String(traceattrs.TargetPlatform, targetPlatform.String()),
tracing.SpanAttributes(
traceattrs.OpenTofuProviderAddress(provider.String()),
traceattrs.OpenTofuProviderVersion(version.String()),
traceattrs.OpenTofuTargetPlatform(targetPlatform.String()),
),
)

View File

@@ -20,8 +20,6 @@ import (
"github.com/hashicorp/go-retryablehttp"
"github.com/opentofu/svchost"
"github.com/opentofu/svchost/disco"
otelAttr "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/registry/regsrc"
@@ -84,11 +82,9 @@ func (c *Client) Discover(ctx context.Context, host svchost.Hostname, serviceID
// ModuleVersions queries the registry for a module, and returns the available versions.
func (c *Client) ModuleVersions(ctx context.Context, module *regsrc.Module) (*response.ModuleVersions, error) {
ctx, span := tracing.Tracer().Start(ctx, "List Versions",
trace.WithAttributes(
otelAttr.String("opentofu.module.name", module.RawName),
),
)
ctx, span := tracing.Tracer().Start(ctx, "List Versions", tracing.SpanAttributes(
traceattrs.OpenTofuModuleCallName(module.RawName),
))
defer span.End()
host, err := module.SvcHost()
@@ -165,13 +161,11 @@ func (c *Client) addRequestCreds(ctx context.Context, host svchost.Hostname, req
// ModuleLocation find the download location for a specific version module.
// This returns a string, because the final location may contain special go-getter syntax.
func (c *Client) ModuleLocation(ctx context.Context, module *regsrc.Module, version string) (string, error) {
ctx, span := tracing.Tracer().Start(ctx, "Find Module Location",
trace.WithAttributes(
otelAttr.String(traceattrs.ModuleCallName, module.RawName),
otelAttr.String(traceattrs.ModuleSource, module.Module()),
otelAttr.String(traceattrs.ModuleVersion, version),
),
)
ctx, span := tracing.Tracer().Start(ctx, "Find Module Location", tracing.SpanAttributes(
traceattrs.OpenTofuModuleCallName(module.RawName),
traceattrs.OpenTofuModuleSource(module.Module()),
traceattrs.OpenTofuModuleVersion(version),
))
defer span.End()
host, err := module.SvcHost()

View File

@@ -12,8 +12,6 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs"
@@ -21,6 +19,7 @@ import (
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// ApplyOpts are the various options that affect the details of how OpenTofu
@@ -57,8 +56,8 @@ func (c *Context) Apply(ctx context.Context, plan *plans.Plan, config *configs.C
ctx, span := tracing.Tracer().Start(
ctx, "Apply phase",
otelTrace.WithAttributes(
otelAttr.String("opentofu.plan.mode", plan.UIMode.UIName()),
tracing.SpanAttributes(
traceattrs.String("opentofu.plan.mode", plan.UIMode.UIName()),
),
)
defer span.End()

View File

@@ -16,8 +16,6 @@ import (
"time"
"github.com/hashicorp/hcl/v2"
otelAttr "go.opentelemetry.io/otel/attribute"
"github.com/zclconf/go-cty/cty"
"github.com/opentofu/opentofu/internal/addrs"
@@ -29,6 +27,7 @@ import (
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// PlanOpts are the various options that affect the details of how OpenTofu
@@ -150,10 +149,10 @@ func (c *Context) Plan(ctx context.Context, config *configs.Config, prevRunState
ctx, "Plan phase",
)
span.SetAttributes(
otelAttr.String("opentofu.plan.mode", opts.Mode.UIName()),
otelAttr.StringSlice("opentofu.plan.target_addrs", tracing.StringSlice(span, slices.Values(opts.Targets))),
otelAttr.StringSlice("opentofu.plan.exclude_addrs", tracing.StringSlice(span, slices.Values(opts.Excludes))),
otelAttr.StringSlice("opentofu.plan.force_replace_addrs", tracing.StringSlice(span, slices.Values(opts.ForceReplace))),
traceattrs.String("opentofu.plan.mode", opts.Mode.UIName()),
traceattrs.StringSlice("opentofu.plan.target_addrs", tracing.StringSlice(span, slices.Values(opts.Targets))),
traceattrs.StringSlice("opentofu.plan.exclude_addrs", tracing.StringSlice(span, slices.Values(opts.Excludes))),
traceattrs.StringSlice("opentofu.plan.force_replace_addrs", tracing.StringSlice(span, slices.Values(opts.ForceReplace))),
// Additions here should typically be limited only to options that
// significantly change what provider-driven operations we'd perform
// during the planning phase, since that's the main influence on how

View File

@@ -12,14 +12,13 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/providers"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// traceAttrProviderAddress is a standardized trace span attribute name that we
@@ -127,10 +126,10 @@ func (n *NodeApplyableProvider) executeInstance(ctx context.Context, evalCtx Eva
func (n *NodeApplyableProvider) ValidateProvider(ctx context.Context, evalCtx EvalContext, providerKey addrs.InstanceKey, provider providers.Interface) tfdiags.Diagnostics {
_, span := tracing.Tracer().Start(
ctx, "Validate provider configuration",
otelTrace.WithAttributes(
otelAttr.String(traceAttrProviderAddr, n.Addr.Provider.String()),
otelAttr.String(traceAttrProviderConfigAddr, n.Addr.String()),
otelAttr.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.Addr, providerKey)),
tracing.SpanAttributes(
traceattrs.String(traceAttrProviderAddr, n.Addr.Provider.String()),
traceattrs.String(traceAttrProviderConfigAddr, n.Addr.String()),
traceattrs.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.Addr, providerKey)),
),
)
defer span.End()
@@ -193,10 +192,10 @@ func (n *NodeApplyableProvider) ValidateProvider(ctx context.Context, evalCtx Ev
func (n *NodeApplyableProvider) ConfigureProvider(ctx context.Context, evalCtx EvalContext, providerKey addrs.InstanceKey, provider providers.Interface, verifyConfigIsKnown bool) tfdiags.Diagnostics {
_, span := tracing.Tracer().Start(
ctx, "Configure provider",
otelTrace.WithAttributes(
otelAttr.String(traceAttrProviderAddr, n.Addr.Provider.String()),
otelAttr.String(traceAttrProviderConfigAddr, n.Addr.String()),
otelAttr.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.Addr, providerKey)),
tracing.SpanAttributes(
traceattrs.String(traceAttrProviderAddr, n.Addr.Provider.String()),
traceattrs.String(traceAttrProviderConfigAddr, n.Addr.String()),
traceattrs.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.Addr, providerKey)),
),
)
defer span.End()

View File

@@ -10,9 +10,6 @@ import (
"fmt"
"log"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/instances"
@@ -22,6 +19,7 @@ import (
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// NodeApplyableResourceInstance represents a resource instance that is
@@ -126,8 +124,8 @@ func (n *NodeApplyableResourceInstance) Execute(ctx context.Context, evalCtx Eva
ctx, span := tracing.Tracer().Start(
ctx, traceNameApplyResourceInstance,
otelTrace.WithAttributes(
otelAttr.String(traceAttrResourceInstanceAddr, addr.String()),
tracing.SpanAttributes(
traceattrs.String(traceAttrResourceInstanceAddr, addr.String()),
),
)
defer span.End()
@@ -161,7 +159,7 @@ func (n *NodeApplyableResourceInstance) Execute(ctx context.Context, evalCtx Eva
return diags
}
span.SetAttributes(
otelAttr.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
traceattrs.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
)
// Eval info is different depending on what kind of resource this is

View File

@@ -11,8 +11,6 @@ import (
"log"
"github.com/hashicorp/hcl/v2"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/dag"
@@ -22,6 +20,7 @@ import (
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// ConcreteResourceInstanceDeposedNodeFunc is a callback type used to convert
@@ -96,9 +95,9 @@ func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx context.Context, eva
_, span := tracing.Tracer().Start(
ctx, traceNamePlanResourceInstance,
otelTrace.WithAttributes(
otelAttr.String(traceAttrResourceInstanceAddr, n.Addr.String()),
otelAttr.Bool(traceAttrPlanRefresh, !n.skipRefresh),
tracing.SpanAttributes(
traceattrs.String(traceAttrResourceInstanceAddr, n.Addr.String()),
traceattrs.Bool(traceAttrPlanRefresh, !n.skipRefresh),
),
)
defer span.End()
@@ -108,7 +107,7 @@ func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx context.Context, eva
return diags
}
span.SetAttributes(
otelAttr.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
traceattrs.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
)
// Read the state for the deposed resource instance

View File

@@ -10,9 +10,6 @@ import (
"fmt"
"log"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/communicator/shared"
"github.com/opentofu/opentofu/internal/configs"
@@ -21,6 +18,7 @@ import (
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// NodeDestroyResourceInstance represents a resource instance that is to be
@@ -157,8 +155,8 @@ func (n *NodeDestroyResourceInstance) Execute(ctx context.Context, evalCtx EvalC
ctx, span := tracing.Tracer().Start(
ctx, traceNameApplyResourceInstance,
otelTrace.WithAttributes(
otelAttr.String(traceAttrResourceInstanceAddr, addr.String()),
tracing.SpanAttributes(
traceattrs.String(traceAttrResourceInstanceAddr, addr.String()),
),
)
defer span.End()
@@ -172,7 +170,7 @@ func (n *NodeDestroyResourceInstance) Execute(ctx context.Context, evalCtx EvalC
return diags
}
span.SetAttributes(
otelAttr.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
traceattrs.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
)
diags = diags.Append(
n.managedResourceExecute(ctx, evalCtx),

View File

@@ -11,8 +11,7 @@ import (
"log"
"github.com/opentofu/opentofu/internal/dag"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/plans"
@@ -62,9 +61,9 @@ func (n *NodePlanDestroyableResourceInstance) Execute(ctx context.Context, evalC
ctx, span := tracing.Tracer().Start(
ctx, traceNamePlanResourceInstance,
otelTrace.WithAttributes(
otelAttr.String(traceAttrResourceInstanceAddr, addr.String()),
otelAttr.Bool(traceAttrPlanRefresh, !n.skipRefresh),
tracing.SpanAttributes(
traceattrs.String(traceAttrResourceInstanceAddr, addr.String()),
traceattrs.Bool(traceAttrPlanRefresh, !n.skipRefresh),
),
)
defer span.End()
@@ -75,7 +74,7 @@ func (n *NodePlanDestroyableResourceInstance) Execute(ctx context.Context, evalC
return diags
}
span.SetAttributes(
otelAttr.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
traceattrs.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
)
switch addr.Resource.Resource.Mode {

View File

@@ -15,8 +15,6 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/zclconf/go-cty/cty"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs"
@@ -28,6 +26,7 @@ import (
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// NodePlannableResourceInstance represents a _single_ resource
@@ -93,10 +92,10 @@ func (n *NodePlannableResourceInstance) Execute(ctx context.Context, evalCtx Eva
ctx, span := tracing.Tracer().Start(
ctx, traceNamePlanResourceInstance,
otelTrace.WithAttributes(
otelAttr.String(traceAttrResourceInstanceAddr, addr.String()),
otelAttr.Bool(traceAttrPlanRefresh, !n.skipRefresh),
otelAttr.Bool(traceAttrPlanPlanChanges, !n.skipPlanChanges),
tracing.SpanAttributes(
traceattrs.String(traceAttrResourceInstanceAddr, addr.String()),
traceattrs.Bool(traceAttrPlanRefresh, !n.skipRefresh),
traceattrs.Bool(traceAttrPlanPlanChanges, !n.skipPlanChanges),
),
)
defer span.End()
@@ -107,7 +106,7 @@ func (n *NodePlannableResourceInstance) Execute(ctx context.Context, evalCtx Eva
return diags
}
span.SetAttributes(
otelAttr.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
traceattrs.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
)
// Eval info is different depending on what kind of resource this is

View File

@@ -11,8 +11,6 @@ import (
"log"
"github.com/hashicorp/hcl/v2"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/plans"
@@ -20,6 +18,7 @@ import (
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// NodePlannableResourceInstanceOrphan represents a resource that is "applyable":
@@ -63,10 +62,10 @@ func (n *NodePlannableResourceInstanceOrphan) Execute(ctx context.Context, evalC
ctx, span := tracing.Tracer().Start(
ctx, traceNamePlanResourceInstance,
otelTrace.WithAttributes(
otelAttr.String(traceAttrResourceInstanceAddr, addr.String()),
otelAttr.Bool(traceAttrPlanRefresh, !n.skipRefresh),
otelAttr.Bool(traceAttrPlanPlanChanges, !n.skipPlanChanges),
tracing.SpanAttributes(
traceattrs.String(traceAttrResourceInstanceAddr, addr.String()),
traceattrs.Bool(traceAttrPlanRefresh, !n.skipRefresh),
traceattrs.Bool(traceAttrPlanPlanChanges, !n.skipPlanChanges),
),
)
defer span.End()
@@ -82,7 +81,7 @@ func (n *NodePlannableResourceInstanceOrphan) Execute(ctx context.Context, evalC
return diags
}
span.SetAttributes(
otelAttr.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
traceattrs.String(traceAttrProviderInstanceAddr, traceProviderInstanceAddr(n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)),
)
diags = diags.Append(
n.managedResourceExecute(ctx, evalCtx),

View File

@@ -12,8 +12,6 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
otelAttr "go.opentelemetry.io/otel/attribute"
otelTrace "go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/communicator/shared"
@@ -26,6 +24,7 @@ import (
"github.com/opentofu/opentofu/internal/provisioners"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tracing"
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
// NodeValidatableResource represents a resource that is used for validation
@@ -54,8 +53,8 @@ func (n *NodeValidatableResource) Path() addrs.ModuleInstance {
func (n *NodeValidatableResource) Execute(ctx context.Context, evalCtx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
_, span := tracing.Tracer().Start(
ctx, traceNameValidateResource,
otelTrace.WithAttributes(
otelAttr.String(traceAttrConfigResourceAddr, n.Addr.String()),
tracing.SpanAttributes(
traceattrs.String(traceAttrConfigResourceAddr, n.Addr.String()),
),
)
defer span.End()

View File

@@ -15,15 +15,21 @@ import (
"go.opentelemetry.io/contrib/exporters/autoexport"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
// This *MUST* always be updated to the latest version when OTEL dependencies are updated in OpenTofu
// Failing to do so will prevent OpenTofu from initializing tracing.
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
// ---------------------------------------------------------------------
// DO NOT IMPORT ANY "go.opentelemetry.io/otel/semconv/*" PACKAGES HERE!
// ---------------------------------------------------------------------
// Instead, use semconv indirectly through wrappers and reexports in
// ./traceattrs/semconv.go, because we need to coordinate our chosen
// semconv version with the OTel SDK's "resource" package.
"github.com/opentofu/opentofu/version"
// The version number at the end of this package math MUST match the
// semconv version imported by the "go.opentelemetry.io/otel/sdk/resource",
// so we will typically need to update this each time we upgrade
// the module "go.opentelemetry.io/otel/sdk".
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
)
/*
@@ -107,26 +113,7 @@ func OpenTelemetryInit(ctx context.Context) (context.Context, error) {
serviceName = envServiceName
}
otelResource, err := resource.New(context.Background(),
// Use built-in detectors to simplify the collation of the racing 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()),
),
)
otelResource, err := traceattrs.NewResource(ctx, serviceName)
if err != nil {
return ctx, fmt.Errorf("failed to create resource: %w", err)
}

View File

@@ -0,0 +1,42 @@
// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package traceattrs
import (
"go.opentelemetry.io/otel/attribute"
)
// String wraps [attribute.String] just so that we can keep most of our direct
// OpenTelemetry package imports centralized in this package where it's
// easier to keep our version selections consistent.
func String(name string, val string) attribute.KeyValue {
return attribute.String(name, val)
}
// StringSlice wraps [attribute.StringSlice] just so that we can keep most of
// our direct OpenTelemetry package imports centralized in this package where
// it's easier to keep our version selections consistent.
//
// If the items you want to report are not yet assembled into a string slice,
// consider using [tracing.StringSlice] with an [iter.Seq[string]] argument
// to skip constructing the slice when tracing isn't enabled.
func StringSlice(name string, val []string) attribute.KeyValue {
return attribute.StringSlice(name, val)
}
// Bool wraps [attribute.Bool] just so that we can keep most of our direct
// OpenTelemetry package imports centralized in this package where it's
// easier to keep our version selections consistent.
func Bool(name string, val bool) attribute.KeyValue {
return attribute.Bool(name, val)
}
// Int64 wraps [attribute.Int64] just so that we can keep most of our direct
// OpenTelemetry package imports centralized in this package where it's
// easier to keep our version selections consistent.
func Int64(name string, val int64) attribute.KeyValue {
return attribute.Int64(name, val)
}

View File

@@ -0,0 +1,82 @@
// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package traceattrs
import (
"go.opentelemetry.io/otel/attribute"
)
// This file contains some functions representing OpenTofu-specific semantic
// conventions, which we use alongside the general OpenTelemetry-specified
// semantic conventions.
//
// These functions tend to take strings that are expected to be the canonical
// string representation of some more specific type from elsewhere in OpenTofu,
// but we make the caller produce the string representation rather than doing it
// inline because this package needs to avoid importing any other packages
// from this codebase so that the rest of OpenTofu can use this package without
// creating import cycles.
//
// We only create functions in here for attribute names that we want to use
// consistently across many different callers. For one-off attribute names that
// are only used in a single kind of span, use the generic functions like
// [String], [StringSlice], etc, instead.
// OpenTofuProviderAddress returns an attribute definition for indicating
// which provider is relevant to a particular trace span.
//
// The given address should be the result of calling [addrs.Provider.String].
func OpenTofuProviderAddress(addr string) attribute.KeyValue {
return attribute.String("opentofu.provider.address", addr)
}
// OpenTofuProviderVersion returns an attribute definition for indicating
// which version of a provider is relevant to a particular trace span.
//
// The given address should be the result of calling
// [getproviders.Version.String]. This should typically be used alongside
// [OpenTofuProviderAddress] to indicate which provider the version number is
// for.
func OpenTofuProviderVersion(v string) attribute.KeyValue {
return attribute.String("opentofu.provider.version", v)
}
// OpenTofuTargetPlatform returns an attribute definition for indicating
// which target platform is relevant to a particular trace span.
//
// The given address should be the result of calling
// [getproviders.Platform.String].
func OpenTofuTargetPlatform(platform string) attribute.KeyValue {
return attribute.String("opentofu.target_platform", platform)
}
// OpenTofuModuleCallName returns an attribute definition for indicating
// the name of a module call that's relevant to a particular trace span.
//
// The given address should be something that would be valid in the
// [addrs.ModuleCall.Name] field.
func OpenTofuModuleCallName(name string) attribute.KeyValue {
return attribute.String("opentofu.module.name", name)
}
// OpenTofuModuleSource returns an attribute definition for indicating
// which module source address is relevant to a particular trace span.
//
// The given address should be the result of calling
// [addrs.ModuleSource.String], or any other syntax-compatible representation.
func OpenTofuModuleSource(addr string) attribute.KeyValue {
return attribute.String("opentofu.module.source", addr)
}
// OpenTofuModuleVersion returns an attribute definition for indicating
// which version of a module is relevant to a particular trace span.
//
// The given address should be either the result of calling
// [getproviders.Version.String], or the String method from the "Version" type
// from HashiCorp's "go-version" library.
func OpenTofuModuleVersion(v string) attribute.KeyValue {
return attribute.String("opentofu.module.version", v)
}

View File

@@ -0,0 +1,90 @@
// 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)
}

View File

@@ -0,0 +1,18 @@
// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package traceattrs
import (
"testing"
)
func TestNewResource(t *testing.T) {
_, err := NewResource(t.Context(), "test-service")
if err != nil {
t.Errorf("failed to create OpenTelemetry SDK resource: %s", err)
t.Errorf("If the above error message is about conflicting schema versions, then make sure that the semconv package imported in semconv.go matches the semconv package imported by \"go.opentelemetry.io/otel/sdk/resource\".")
}
}

View File

@@ -1,19 +0,0 @@
// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package traceattrs
const (
// Common attributes names used across the codebase
ProviderAddress = "opentofu.provider.address"
ProviderVersion = "opentofu.provider.version"
TargetPlatform = "opentofu.target_platform"
ModuleCallName = "opentofu.module.name"
ModuleSource = "opentofu.module.source"
ModuleVersion = "opentofu.module.version"
)

View File

@@ -14,9 +14,9 @@ import (
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
"github.com/opentofu/opentofu/internal/tfdiags"
@@ -119,3 +119,26 @@ func extractImportPath(fullName string) string {
return fullName[:lastSlash+dotAfterSlash]
}
// Span is an alias for [trace.Span] just to centralize all of our direct
// imports of OpenTelemetry packages into our tracing packages, to help
// avoid dependency hell.
type Span = trace.Span
// SpanFromContext returns the trace span asssociated with the given context,
// or nil if there is no associated span.
//
// This is a wrapper around [trace.SpanFromContext] just to centralize all of
// our imports of OpenTelemetry packages into our tracing packages, to help
// avoid dependency hell.
func SpanFromContext(ctx context.Context) trace.Span {
return trace.SpanFromContext(ctx)
}
// SpanAttributes wraps [trace.WithAttributes] just so that we can minimize
// how many different OpenTofu packages directly import the OpenTelemetry
// packages, because we tend to need to control which versions we're using
// quite closely to avoid dependency hell.
func SpanAttributes(attrs ...attribute.KeyValue) trace.SpanStartEventOption {
return trace.WithAttributes(attrs...)
}