blob: 16a062ad8cb3b957c5dbfbeec0200b3cc9e2ee00 [file] [log] [blame]
Abhay Kumara2ae5992025-11-10 14:02:24 +00001// Copyright The OpenTelemetry Authors
2// SPDX-License-Identifier: Apache-2.0
3
4package resource // import "go.opentelemetry.io/otel/sdk/resource"
5
6import (
7 "context"
8 "fmt"
9 "net/url"
10 "os"
11 "strings"
12
13 "go.opentelemetry.io/otel"
14 "go.opentelemetry.io/otel/attribute"
15 semconv "go.opentelemetry.io/otel/semconv/v1.34.0"
16)
17
18const (
19 // resourceAttrKey is the environment variable name OpenTelemetry Resource information will be read from.
20 resourceAttrKey = "OTEL_RESOURCE_ATTRIBUTES" //nolint:gosec // False positive G101: Potential hardcoded credentials
21
22 // svcNameKey is the environment variable name that Service Name information will be read from.
23 svcNameKey = "OTEL_SERVICE_NAME"
24)
25
26// errMissingValue is returned when a resource value is missing.
27var errMissingValue = fmt.Errorf("%w: missing value", ErrPartialResource)
28
29// fromEnv is a Detector that implements the Detector and collects
30// resources from environment. This Detector is included as a
31// builtin.
32type fromEnv struct{}
33
34// compile time assertion that FromEnv implements Detector interface.
35var _ Detector = fromEnv{}
36
37// Detect collects resources from environment.
38func (fromEnv) Detect(context.Context) (*Resource, error) {
39 attrs := strings.TrimSpace(os.Getenv(resourceAttrKey))
40 svcName := strings.TrimSpace(os.Getenv(svcNameKey))
41
42 if attrs == "" && svcName == "" {
43 return Empty(), nil
44 }
45
46 var res *Resource
47
48 if svcName != "" {
49 res = NewSchemaless(semconv.ServiceName(svcName))
50 }
51
52 r2, err := constructOTResources(attrs)
53
54 // Ensure that the resource with the service name from OTEL_SERVICE_NAME
55 // takes precedence, if it was defined.
56 res, err2 := Merge(r2, res)
57
58 if err == nil {
59 err = err2
60 } else if err2 != nil {
61 err = fmt.Errorf("detecting resources: %s", []string{err.Error(), err2.Error()})
62 }
63
64 return res, err
65}
66
67func constructOTResources(s string) (*Resource, error) {
68 if s == "" {
69 return Empty(), nil
70 }
71 pairs := strings.Split(s, ",")
72 var attrs []attribute.KeyValue
73 var invalid []string
74 for _, p := range pairs {
75 k, v, found := strings.Cut(p, "=")
76 if !found {
77 invalid = append(invalid, p)
78 continue
79 }
80 key := strings.TrimSpace(k)
81 val, err := url.PathUnescape(strings.TrimSpace(v))
82 if err != nil {
83 // Retain original value if decoding fails, otherwise it will be
84 // an empty string.
85 val = v
86 otel.Handle(err)
87 }
88 attrs = append(attrs, attribute.String(key, val))
89 }
90 var err error
91 if len(invalid) > 0 {
92 err = fmt.Errorf("%w: %v", errMissingValue, invalid)
93 }
94 return NewSchemaless(attrs...), err
95}