Skip to content

Commit c9c0526

Browse files
committed
Update OpenTelemetry and use an internal package
The "autoexport" and "autoprop" packages import and configure standard exporters according to the current Specification, 1.39.0. The correct correlation of logs to trace context uses underscores now. The "google.golang.org/grpc" module raised its required Go version to 1.22.7 this release but has since reverted that change. The "spancheck" linter reminds us to call "Span.End" after calling "tracing.Start". Issue: PGO-1954 See: https://opentelemetry.io/docs/specs/otel See: https://opentelemetry.io/docs/specs/otel/compatibility/logging_trace_context See: https://www.github.com/open-telemetry/opentelemetry-go/issues/5969
1 parent a9dae18 commit c9c0526

File tree

23 files changed

+468
-207
lines changed

23 files changed

+468
-207
lines changed

.golangci.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ linters-settings:
2222
depguard:
2323
rules:
2424
everything:
25+
list-mode: lax
26+
allow:
27+
- go.opentelemetry.io/otel/semconv/v1.27.0
2528
deny:
29+
- pkg: go.opentelemetry.io/otel/semconv
30+
desc: Use "go.opentelemetry.io/otel/semconv/v1.27.0" instead.
31+
2632
- pkg: io/ioutil
2733
desc: >
2834
Use the "io" and "os" packages instead.
@@ -93,6 +99,10 @@ linters-settings:
9399
alias: apierrors
94100
no-unaliased: true
95101

102+
spancheck:
103+
extra-start-span-signatures:
104+
- 'github.com/crunchydata/postgres-operator/internal/tracing.Start:opentelemetry'
105+
96106
issues:
97107
exclude-generated: strict
98108
exclude-rules:

cmd/postgres-operator/main.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package main
66

77
import (
88
"context"
9+
"errors"
910
"fmt"
1011
"net/http"
1112
"os"
@@ -16,7 +17,6 @@ import (
1617
"time"
1718
"unicode"
1819

19-
"go.opentelemetry.io/otel"
2020
"k8s.io/apimachinery/pkg/util/validation"
2121
"k8s.io/client-go/rest"
2222
"sigs.k8s.io/controller-runtime/pkg/healthz"
@@ -33,12 +33,11 @@ import (
3333
"github.com/crunchydata/postgres-operator/internal/logging"
3434
"github.com/crunchydata/postgres-operator/internal/naming"
3535
"github.com/crunchydata/postgres-operator/internal/registration"
36+
"github.com/crunchydata/postgres-operator/internal/tracing"
3637
"github.com/crunchydata/postgres-operator/internal/upgradecheck"
3738
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
3839
)
3940

40-
var versionString string
41-
4241
// assertNoError panics when err is not nil.
4342
func assertNoError(err error) {
4443
if err != nil {
@@ -128,6 +127,7 @@ func main() {
128127
running, stopRunning := context.WithCancel(context.Background())
129128
defer stopRunning()
130129

130+
initVersion()
131131
initLogging()
132132
log := logging.FromContext(starting)
133133
log.V(1).Info("debug flag set to true")
@@ -159,11 +159,14 @@ func main() {
159159
// Initialize OpenTelemetry and flush data when there is a panic.
160160
otelFinish, err := initOpenTelemetry(starting)
161161
assertNoError(err)
162-
defer otelFinish(starting)
162+
defer func(ctx context.Context) { _ = otelFinish(ctx) }(starting)
163+
164+
tracing.SetDefaultTracer(tracing.New("github.com/CrunchyData/postgres-operator"))
163165

164166
cfg, err := runtime.GetConfig()
165167
assertNoError(err)
166168

169+
cfg.UserAgent = userAgent
167170
cfg.Wrap(otelTransportWrapper())
168171

169172
// TODO(controller-runtime): Set config.WarningHandler instead after v0.19.0.
@@ -250,8 +253,7 @@ func main() {
250253
}
251254

252255
// Flush any telemetry with the remaining time we have.
253-
otelFinish(stopping)
254-
if err != nil {
256+
if err = errors.Join(err, otelFinish(stopping)); err != nil {
255257
log.Error(err, "shutdown failed")
256258
} else {
257259
log.Info("shutdown complete")
@@ -266,7 +268,6 @@ func addControllersToManager(mgr runtime.Manager, log logging.Logger, reg regist
266268
Owner: postgrescluster.ControllerName,
267269
Recorder: mgr.GetEventRecorderFor(postgrescluster.ControllerName),
268270
Registration: reg,
269-
Tracer: otel.Tracer(postgrescluster.ControllerName),
270271
}
271272

272273
if err := pgReconciler.SetupWithManager(mgr); err != nil {

cmd/postgres-operator/open_telemetry.go

Lines changed: 65 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,79 +6,91 @@ package main
66

77
import (
88
"context"
9-
"fmt"
10-
"io"
9+
"errors"
1110
"net/http"
1211
"os"
1312

13+
"go.opentelemetry.io/contrib/exporters/autoexport"
1414
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
15+
"go.opentelemetry.io/contrib/propagators/autoprop"
1516
"go.opentelemetry.io/otel"
16-
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
17-
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
18-
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
17+
"go.opentelemetry.io/otel/sdk/resource"
1918
"go.opentelemetry.io/otel/sdk/trace"
20-
)
19+
semconv "go.opentelemetry.io/otel/semconv/v1.27.0"
2120

22-
func initOpenTelemetry(ctx context.Context) (func(context.Context), error) {
23-
// At the time of this writing, the SDK (go.opentelemetry.io/otel@v1.2.0)
24-
// does not automatically initialize any exporter. We import the OTLP and
25-
// stdout exporters and configure them below. Much of the OTLP exporter can
26-
// be configured through environment variables.
27-
//
28-
// - https://github.com/open-telemetry/opentelemetry-go/issues/2310
29-
// - https://github.com/open-telemetry/opentelemetry-specification/blob/v1.8.0/specification/sdk-environment-variables.md
21+
"github.com/crunchydata/postgres-operator/internal/logging"
22+
)
3023

31-
switch os.Getenv("OTEL_TRACES_EXPORTER") {
32-
case "json":
33-
var closer io.Closer
34-
filename := os.Getenv("OTEL_JSON_FILE")
35-
options := []stdouttrace.Option{}
24+
func initOpenTelemetry(ctx context.Context) (func(context.Context) error, error) {
25+
var started []interface{ Shutdown(context.Context) error }
3626

37-
if filename != "" {
38-
file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
39-
if err != nil {
40-
return nil, fmt.Errorf("unable to open exporter file: %w", err)
41-
}
42-
closer = file
43-
options = append(options, stdouttrace.WithWriter(file))
27+
// shutdown returns the results of calling all the Shutdown methods in started.
28+
var shutdown = func(ctx context.Context) error {
29+
var err error
30+
for _, s := range started {
31+
err = errors.Join(err, s.Shutdown(ctx))
4432
}
33+
started = nil
34+
return err
35+
}
4536

46-
exporter, err := stdouttrace.New(options...)
47-
if err != nil {
48-
return nil, fmt.Errorf("unable to initialize stdout exporter: %w", err)
49-
}
37+
// The default for OTEL_PROPAGATORS is "tracecontext,baggage".
38+
otel.SetTextMapPropagator(autoprop.NewTextMapPropagator())
5039

51-
provider := trace.NewTracerProvider(trace.WithBatcher(exporter))
52-
flush := func(ctx context.Context) {
53-
_ = provider.Shutdown(ctx)
54-
if closer != nil {
55-
_ = closer.Close()
56-
}
57-
}
40+
// Skip any remaining setup when OTEL_SDK_DISABLED is exactly "true".
41+
// - https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables
42+
if os.Getenv("OTEL_SDK_DISABLED") == "true" {
43+
return shutdown, nil
44+
}
5845

59-
otel.SetTracerProvider(provider)
60-
return flush, nil
46+
log := logging.FromContext(ctx).WithName("open-telemetry")
47+
otel.SetLogger(log)
48+
otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {
49+
// TODO(events): Emit this as an event instead.
50+
log.V(1).Info(semconv.ExceptionEventName,
51+
string(semconv.ExceptionMessageKey), err)
52+
}))
6153

62-
case "otlp":
63-
client := otlptracehttp.NewClient()
64-
exporter, err := otlptrace.New(context.TODO(), client)
65-
if err != nil {
66-
return nil, fmt.Errorf("unable to initialize OTLP exporter: %w", err)
67-
}
54+
// Build a resource from the OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME environment variables.
55+
// - https://opentelemetry.io/docs/languages/go/resources
56+
self, _ := resource.Merge(resource.NewSchemaless(
57+
semconv.ServiceVersion(versionString),
58+
), resource.Default())
6859

69-
provider := trace.NewTracerProvider(trace.WithBatcher(exporter))
70-
flush := func(ctx context.Context) {
71-
_ = provider.Shutdown(ctx)
60+
// Provide defaults for some other detectable attributes.
61+
if r, err := resource.New(ctx,
62+
resource.WithProcessRuntimeName(),
63+
resource.WithProcessRuntimeVersion(),
64+
resource.WithProcessRuntimeDescription(),
65+
); err == nil {
66+
self, _ = resource.Merge(r, self)
67+
}
68+
if r, err := resource.New(ctx,
69+
resource.WithHost(),
70+
resource.WithOS(),
71+
); err == nil {
72+
self, _ = resource.Merge(r, self)
73+
}
74+
75+
// The default for OTEL_TRACES_EXPORTER is "otlp" but we prefer "none".
76+
// Only assign an exporter when the environment variable is set.
77+
if os.Getenv("OTEL_TRACES_EXPORTER") != "" {
78+
exporter, err := autoexport.NewSpanExporter(ctx)
79+
if err != nil {
80+
return nil, errors.Join(err, shutdown(ctx))
7281
}
7382

83+
// The defaults for this batch processor come from the OTEL_BSP_* environment variables.
84+
// - https://pkg.go.dev/go.opentelemetry.io/otel/sdk/internal/env
85+
provider := trace.NewTracerProvider(
86+
trace.WithBatcher(exporter),
87+
trace.WithResource(self),
88+
)
89+
started = append(started, provider)
7490
otel.SetTracerProvider(provider)
75-
return flush, nil
7691
}
7792

78-
// $OTEL_TRACES_EXPORTER is unset or unknown, so no TracerProvider has been assigned.
79-
// The default at this time is a single "no-op" tracer.
80-
81-
return func(context.Context) {}, nil
93+
return shutdown, nil
8294
}
8395

8496
// otelTransportWrapper creates a function that wraps the provided net/http.RoundTripper

cmd/postgres-operator/version.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2017 - 2024 Crunchy Data Solutions, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package main
6+
7+
import (
8+
"fmt"
9+
"os"
10+
"path/filepath"
11+
"runtime"
12+
)
13+
14+
var userAgent string
15+
var versionString string
16+
17+
func initVersion() {
18+
command := "unknown"
19+
if len(os.Args) > 0 && len(os.Args[0]) > 0 {
20+
command = filepath.Base(os.Args[0])
21+
}
22+
if len(versionString) > 0 {
23+
command += "/" + versionString
24+
}
25+
userAgent = fmt.Sprintf("%s (%s/%s)", command, runtime.GOOS, runtime.GOARCH)
26+
}

go.mod

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/crunchydata/postgres-operator
22

3-
go 1.22.0
3+
go 1.22.7
44

55
require (
66
github.com/go-logr/logr v1.4.2
@@ -14,14 +14,13 @@ require (
1414
github.com/pkg/errors v0.9.1
1515
github.com/sirupsen/logrus v1.9.3
1616
github.com/xdg-go/stringprep v1.0.2
17-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0
18-
go.opentelemetry.io/otel v1.27.0
19-
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0
20-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0
21-
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0
22-
go.opentelemetry.io/otel/sdk v1.27.0
23-
go.opentelemetry.io/otel/trace v1.27.0
24-
golang.org/x/crypto v0.27.0
17+
go.opentelemetry.io/contrib/exporters/autoexport v0.57.0
18+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0
19+
go.opentelemetry.io/contrib/propagators/autoprop v0.57.0
20+
go.opentelemetry.io/otel v1.32.0
21+
go.opentelemetry.io/otel/sdk v1.32.0
22+
go.opentelemetry.io/otel/trace v1.32.0
23+
golang.org/x/crypto v0.28.0
2524
golang.org/x/tools v0.22.0
2625
gotest.tools/v3 v3.1.0
2726
k8s.io/api v0.30.2
@@ -55,37 +54,58 @@ require (
5554
github.com/google/gofuzz v1.2.0 // indirect
5655
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
5756
github.com/gorilla/websocket v1.5.0 // indirect
58-
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
57+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
5958
github.com/imdario/mergo v0.3.16 // indirect
6059
github.com/josharian/intern v1.0.0 // indirect
6160
github.com/json-iterator/go v1.1.12 // indirect
61+
github.com/klauspost/compress v1.17.11 // indirect
6262
github.com/mailru/easyjson v0.7.7 // indirect
6363
github.com/moby/spdystream v0.2.0 // indirect
6464
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
6565
github.com/modern-go/reflect2 v1.0.2 // indirect
6666
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
6767
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
68-
github.com/prometheus/client_golang v1.19.1 // indirect
68+
github.com/prometheus/client_golang v1.20.5 // indirect
6969
github.com/prometheus/client_model v0.6.1 // indirect
70-
github.com/prometheus/common v0.54.0 // indirect
70+
github.com/prometheus/common v0.60.1 // indirect
7171
github.com/prometheus/procfs v0.15.1 // indirect
7272
github.com/spf13/pflag v1.0.5 // indirect
73-
go.opentelemetry.io/otel/metric v1.27.0 // indirect
73+
go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 // indirect
74+
go.opentelemetry.io/contrib/propagators/aws v1.32.0 // indirect
75+
go.opentelemetry.io/contrib/propagators/b3 v1.32.0 // indirect
76+
go.opentelemetry.io/contrib/propagators/jaeger v1.32.0 // indirect
77+
go.opentelemetry.io/contrib/propagators/ot v1.32.0 // indirect
78+
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 // indirect
79+
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 // indirect
80+
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 // indirect
81+
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 // indirect
82+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect
83+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 // indirect
84+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 // indirect
85+
go.opentelemetry.io/otel/exporters/prometheus v0.54.0 // indirect
86+
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 // indirect
87+
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 // indirect
88+
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 // indirect
89+
go.opentelemetry.io/otel/log v0.8.0 // indirect
90+
go.opentelemetry.io/otel/metric v1.32.0 // indirect
91+
go.opentelemetry.io/otel/sdk/log v0.8.0 // indirect
92+
go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect
7493
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
94+
go.uber.org/multierr v1.11.0 // indirect
7595
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect
7696
golang.org/x/mod v0.18.0 // indirect
77-
golang.org/x/net v0.29.0 // indirect
78-
golang.org/x/oauth2 v0.21.0 // indirect
79-
golang.org/x/sync v0.8.0 // indirect
80-
golang.org/x/sys v0.25.0 // indirect
81-
golang.org/x/term v0.24.0 // indirect
82-
golang.org/x/text v0.18.0 // indirect
97+
golang.org/x/net v0.30.0 // indirect
98+
golang.org/x/oauth2 v0.23.0 // indirect
99+
golang.org/x/sync v0.9.0 // indirect
100+
golang.org/x/sys v0.27.0 // indirect
101+
golang.org/x/term v0.25.0 // indirect
102+
golang.org/x/text v0.20.0 // indirect
83103
golang.org/x/time v0.5.0 // indirect
84104
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
85-
google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 // indirect
86-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
87-
google.golang.org/grpc v1.66.2 // indirect
88-
google.golang.org/protobuf v1.34.2 // indirect
105+
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
106+
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
107+
google.golang.org/grpc v1.68.0 // indirect
108+
google.golang.org/protobuf v1.35.1 // indirect
89109
gopkg.in/inf.v0 v0.9.1 // indirect
90110
gopkg.in/yaml.v2 v2.4.0 // indirect
91111
gopkg.in/yaml.v3 v3.0.1 // indirect

0 commit comments

Comments
 (0)