env.go (2996B)
1 // Copyright The OpenTelemetry Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package resource // import "go.opentelemetry.io/otel/sdk/resource" 16 17 import ( 18 "context" 19 "fmt" 20 "net/url" 21 "os" 22 "strings" 23 24 "go.opentelemetry.io/otel" 25 "go.opentelemetry.io/otel/attribute" 26 semconv "go.opentelemetry.io/otel/semconv/v1.17.0" 27 ) 28 29 const ( 30 // resourceAttrKey is the environment variable name OpenTelemetry Resource information will be read from. 31 resourceAttrKey = "OTEL_RESOURCE_ATTRIBUTES" 32 33 // svcNameKey is the environment variable name that Service Name information will be read from. 34 svcNameKey = "OTEL_SERVICE_NAME" 35 ) 36 37 var ( 38 // errMissingValue is returned when a resource value is missing. 39 errMissingValue = fmt.Errorf("%w: missing value", ErrPartialResource) 40 ) 41 42 // fromEnv is a Detector that implements the Detector and collects 43 // resources from environment. This Detector is included as a 44 // builtin. 45 type fromEnv struct{} 46 47 // compile time assertion that FromEnv implements Detector interface. 48 var _ Detector = fromEnv{} 49 50 // Detect collects resources from environment. 51 func (fromEnv) Detect(context.Context) (*Resource, error) { 52 attrs := strings.TrimSpace(os.Getenv(resourceAttrKey)) 53 svcName := strings.TrimSpace(os.Getenv(svcNameKey)) 54 55 if attrs == "" && svcName == "" { 56 return Empty(), nil 57 } 58 59 var res *Resource 60 61 if svcName != "" { 62 res = NewSchemaless(semconv.ServiceName(svcName)) 63 } 64 65 r2, err := constructOTResources(attrs) 66 67 // Ensure that the resource with the service name from OTEL_SERVICE_NAME 68 // takes precedence, if it was defined. 69 res, err2 := Merge(r2, res) 70 71 if err == nil { 72 err = err2 73 } else if err2 != nil { 74 err = fmt.Errorf("detecting resources: %s", []string{err.Error(), err2.Error()}) 75 } 76 77 return res, err 78 } 79 80 func constructOTResources(s string) (*Resource, error) { 81 if s == "" { 82 return Empty(), nil 83 } 84 pairs := strings.Split(s, ",") 85 var attrs []attribute.KeyValue 86 var invalid []string 87 for _, p := range pairs { 88 k, v, found := strings.Cut(p, "=") 89 if !found { 90 invalid = append(invalid, p) 91 continue 92 } 93 key := strings.TrimSpace(k) 94 val, err := url.QueryUnescape(strings.TrimSpace(v)) 95 if err != nil { 96 // Retain original value if decoding fails, otherwise it will be 97 // an empty string. 98 val = v 99 otel.Handle(err) 100 } 101 attrs = append(attrs, attribute.String(key, val)) 102 } 103 var err error 104 if len(invalid) > 0 { 105 err = fmt.Errorf("%w: %v", errMissingValue, invalid) 106 } 107 return NewSchemaless(attrs...), err 108 }