|
| 1 | +// Copyright The OpenTelemetry Authors |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +package otelsdk |
| 5 | + |
| 6 | +import ( |
| 7 | + "context" |
| 8 | + "debug/buildinfo" |
| 9 | + "strconv" |
| 10 | + |
| 11 | + "go.opentelemetry.io/otel/attribute" |
| 12 | + semconv "go.opentelemetry.io/otel/semconv/v1.30.0" |
| 13 | + |
| 14 | + "go.opentelemetry.io/auto/pipeline" |
| 15 | +) |
| 16 | + |
| 17 | +// Multiplexer supports sending telemetry from multiple resources through the |
| 18 | +// same processing and exporting pipeline. |
| 19 | +type Multiplexer struct { |
| 20 | + cfg config |
| 21 | +} |
| 22 | + |
| 23 | +// NewMultiplexer returns a new *Multiplexer that reuses the provided options as |
| 24 | +// a base configuration for all [pipeline.Handler] it creates. |
| 25 | +// |
| 26 | +// Any exporter provided in the options must support exporting telemetry for |
| 27 | +// multiple resources. |
| 28 | +func NewMultiplexer(ctx context.Context, options ...Option) (*Multiplexer, error) { |
| 29 | + cfg, err := newConfig(ctx, options) |
| 30 | + if err != nil { |
| 31 | + return nil, err |
| 32 | + } |
| 33 | + return &Multiplexer{cfg: cfg}, nil |
| 34 | +} |
| 35 | + |
| 36 | +// Handler returns a new [pipeline.Handler] configured with additional |
| 37 | +// resource attributes that follow OpenTelemetry semantic conventions for the |
| 38 | +// process associated with the given pid. |
| 39 | +// |
| 40 | +// If an error occurs while determining process-specific resource attributes, |
| 41 | +// the error is logged, and a handler without those attributes is returned. |
| 42 | +// |
| 43 | +// If Shutdown has already been called on the Multiplexer, the returned handler |
| 44 | +// will also be in a shut down state and will not export any telemetry. |
| 45 | +func (m Multiplexer) Handler(pid int) *pipeline.Handler { |
| 46 | + c := m.withProcResAttrs(pid) |
| 47 | + return &pipeline.Handler{TraceHandler: newTraceHandler(c)} |
| 48 | +} |
| 49 | + |
| 50 | +// Shutdown gracefully shuts down the Multiplexer's span processor. |
| 51 | +// |
| 52 | +// After Shutdown is called, any subsequent calls to Handler will return a |
| 53 | +// handler that is in a shut down state. These handlers will silently drop |
| 54 | +// telemetry and will not perform any processing or exporting. |
| 55 | +func (m Multiplexer) Shutdown(ctx context.Context) error { |
| 56 | + return m.cfg.spanProcessor.Shutdown(ctx) |
| 57 | +} |
| 58 | + |
| 59 | +// withProcResAttrs returns a copy of the Multiplexer's config with additional |
| 60 | +// resource attributes based on Go runtime build information for the process |
| 61 | +// identified by pid. |
| 62 | +// |
| 63 | +// It attempts to read the build info of the Go executable at /proc/<pid>/exe |
| 64 | +// and extracts semantic convention attributes such as the runtime version and |
| 65 | +// compiler name. If this fails, an error is logged and no extra attributes |
| 66 | +// are added. |
| 67 | +func (m Multiplexer) withProcResAttrs(pid int) (c config) { |
| 68 | + c = m.cfg // Make a shallow copy to modify attributes. |
| 69 | + |
| 70 | + var attrs []attribute.KeyValue |
| 71 | + |
| 72 | + path := "/proc/" + strconv.Itoa(pid) + "/exe" |
| 73 | + bi, err := buildinfo.ReadFile(path) |
| 74 | + if err != nil { |
| 75 | + c.logger.Error("failed to get Go proc build info", "error", err) |
| 76 | + return c |
| 77 | + } |
| 78 | + |
| 79 | + // Add Go runtime version as a semantic attribute. |
| 80 | + attrs = append(attrs, semconv.ProcessRuntimeVersion(bi.GoVersion)) |
| 81 | + |
| 82 | + // Try to determine which Go compiler was used. |
| 83 | + var compiler string |
| 84 | + for _, setting := range bi.Settings { |
| 85 | + if setting.Key == "-compiler" { |
| 86 | + compiler = setting.Value |
| 87 | + break |
| 88 | + } |
| 89 | + } |
| 90 | + switch compiler { |
| 91 | + case "": |
| 92 | + c.logger.Debug("failed to identify Go compiler") |
| 93 | + case "gc": |
| 94 | + attrs = append(attrs, semconv.ProcessRuntimeName("go")) |
| 95 | + default: |
| 96 | + attrs = append(attrs, semconv.ProcessRuntimeName(compiler)) |
| 97 | + } |
| 98 | + |
| 99 | + // Prepend process-specific attributes so user-provided ones have priority. |
| 100 | + c.resAttrs = append(attrs, c.resAttrs...) |
| 101 | + |
| 102 | + return c |
| 103 | +} |
0 commit comments