gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

resource.go (7810B)


      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 	"errors"
     20 	"sync"
     21 
     22 	"go.opentelemetry.io/otel"
     23 	"go.opentelemetry.io/otel/attribute"
     24 )
     25 
     26 // Resource describes an entity about which identifying information
     27 // and metadata is exposed.  Resource is an immutable object,
     28 // equivalent to a map from key to unique value.
     29 //
     30 // Resources should be passed and stored as pointers
     31 // (`*resource.Resource`).  The `nil` value is equivalent to an empty
     32 // Resource.
     33 type Resource struct {
     34 	attrs     attribute.Set
     35 	schemaURL string
     36 }
     37 
     38 var (
     39 	emptyResource       Resource
     40 	defaultResource     *Resource
     41 	defaultResourceOnce sync.Once
     42 )
     43 
     44 var errMergeConflictSchemaURL = errors.New("cannot merge resource due to conflicting Schema URL")
     45 
     46 // New returns a Resource combined from the user-provided detectors.
     47 func New(ctx context.Context, opts ...Option) (*Resource, error) {
     48 	cfg := config{}
     49 	for _, opt := range opts {
     50 		cfg = opt.apply(cfg)
     51 	}
     52 
     53 	r := &Resource{schemaURL: cfg.schemaURL}
     54 	return r, detect(ctx, r, cfg.detectors)
     55 }
     56 
     57 // NewWithAttributes creates a resource from attrs and associates the resource with a
     58 // schema URL. If attrs contains duplicate keys, the last value will be used. If attrs
     59 // contains any invalid items those items will be dropped. The attrs are assumed to be
     60 // in a schema identified by schemaURL.
     61 func NewWithAttributes(schemaURL string, attrs ...attribute.KeyValue) *Resource {
     62 	resource := NewSchemaless(attrs...)
     63 	resource.schemaURL = schemaURL
     64 	return resource
     65 }
     66 
     67 // NewSchemaless creates a resource from attrs. If attrs contains duplicate keys,
     68 // the last value will be used. If attrs contains any invalid items those items will
     69 // be dropped. The resource will not be associated with a schema URL. If the schema
     70 // of the attrs is known use NewWithAttributes instead.
     71 func NewSchemaless(attrs ...attribute.KeyValue) *Resource {
     72 	if len(attrs) == 0 {
     73 		return &emptyResource
     74 	}
     75 
     76 	// Ensure attributes comply with the specification:
     77 	// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/common/README.md#attribute
     78 	s, _ := attribute.NewSetWithFiltered(attrs, func(kv attribute.KeyValue) bool {
     79 		return kv.Valid()
     80 	})
     81 
     82 	// If attrs only contains invalid entries do not allocate a new resource.
     83 	if s.Len() == 0 {
     84 		return &emptyResource
     85 	}
     86 
     87 	return &Resource{attrs: s} //nolint
     88 }
     89 
     90 // String implements the Stringer interface and provides a
     91 // human-readable form of the resource.
     92 //
     93 // Avoid using this representation as the key in a map of resources,
     94 // use Equivalent() as the key instead.
     95 func (r *Resource) String() string {
     96 	if r == nil {
     97 		return ""
     98 	}
     99 	return r.attrs.Encoded(attribute.DefaultEncoder())
    100 }
    101 
    102 // MarshalLog is the marshaling function used by the logging system to represent this exporter.
    103 func (r *Resource) MarshalLog() interface{} {
    104 	return struct {
    105 		Attributes attribute.Set
    106 		SchemaURL  string
    107 	}{
    108 		Attributes: r.attrs,
    109 		SchemaURL:  r.schemaURL,
    110 	}
    111 }
    112 
    113 // Attributes returns a copy of attributes from the resource in a sorted order.
    114 // To avoid allocating a new slice, use an iterator.
    115 func (r *Resource) Attributes() []attribute.KeyValue {
    116 	if r == nil {
    117 		r = Empty()
    118 	}
    119 	return r.attrs.ToSlice()
    120 }
    121 
    122 // SchemaURL returns the schema URL associated with Resource r.
    123 func (r *Resource) SchemaURL() string {
    124 	if r == nil {
    125 		return ""
    126 	}
    127 	return r.schemaURL
    128 }
    129 
    130 // Iter returns an iterator of the Resource attributes.
    131 // This is ideal to use if you do not want a copy of the attributes.
    132 func (r *Resource) Iter() attribute.Iterator {
    133 	if r == nil {
    134 		r = Empty()
    135 	}
    136 	return r.attrs.Iter()
    137 }
    138 
    139 // Equal returns true when a Resource is equivalent to this Resource.
    140 func (r *Resource) Equal(eq *Resource) bool {
    141 	if r == nil {
    142 		r = Empty()
    143 	}
    144 	if eq == nil {
    145 		eq = Empty()
    146 	}
    147 	return r.Equivalent() == eq.Equivalent()
    148 }
    149 
    150 // Merge creates a new resource by combining resource a and b.
    151 //
    152 // If there are common keys between resource a and b, then the value
    153 // from resource b will overwrite the value from resource a, even
    154 // if resource b's value is empty.
    155 //
    156 // The SchemaURL of the resources will be merged according to the spec rules:
    157 // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/resource/sdk.md#merge
    158 // If the resources have different non-empty schemaURL an empty resource and an error
    159 // will be returned.
    160 func Merge(a, b *Resource) (*Resource, error) {
    161 	if a == nil && b == nil {
    162 		return Empty(), nil
    163 	}
    164 	if a == nil {
    165 		return b, nil
    166 	}
    167 	if b == nil {
    168 		return a, nil
    169 	}
    170 
    171 	// Merge the schema URL.
    172 	var schemaURL string
    173 	switch true {
    174 	case a.schemaURL == "":
    175 		schemaURL = b.schemaURL
    176 	case b.schemaURL == "":
    177 		schemaURL = a.schemaURL
    178 	case a.schemaURL == b.schemaURL:
    179 		schemaURL = a.schemaURL
    180 	default:
    181 		return Empty(), errMergeConflictSchemaURL
    182 	}
    183 
    184 	// Note: 'b' attributes will overwrite 'a' with last-value-wins in attribute.Key()
    185 	// Meaning this is equivalent to: append(a.Attributes(), b.Attributes()...)
    186 	mi := attribute.NewMergeIterator(b.Set(), a.Set())
    187 	combine := make([]attribute.KeyValue, 0, a.Len()+b.Len())
    188 	for mi.Next() {
    189 		combine = append(combine, mi.Attribute())
    190 	}
    191 	merged := NewWithAttributes(schemaURL, combine...)
    192 	return merged, nil
    193 }
    194 
    195 // Empty returns an instance of Resource with no attributes. It is
    196 // equivalent to a `nil` Resource.
    197 func Empty() *Resource {
    198 	return &emptyResource
    199 }
    200 
    201 // Default returns an instance of Resource with a default
    202 // "service.name" and OpenTelemetrySDK attributes.
    203 func Default() *Resource {
    204 	defaultResourceOnce.Do(func() {
    205 		var err error
    206 		defaultResource, err = Detect(
    207 			context.Background(),
    208 			defaultServiceNameDetector{},
    209 			fromEnv{},
    210 			telemetrySDK{},
    211 		)
    212 		if err != nil {
    213 			otel.Handle(err)
    214 		}
    215 		// If Detect did not return a valid resource, fall back to emptyResource.
    216 		if defaultResource == nil {
    217 			defaultResource = &emptyResource
    218 		}
    219 	})
    220 	return defaultResource
    221 }
    222 
    223 // Environment returns an instance of Resource with attributes
    224 // extracted from the OTEL_RESOURCE_ATTRIBUTES environment variable.
    225 func Environment() *Resource {
    226 	detector := &fromEnv{}
    227 	resource, err := detector.Detect(context.Background())
    228 	if err != nil {
    229 		otel.Handle(err)
    230 	}
    231 	return resource
    232 }
    233 
    234 // Equivalent returns an object that can be compared for equality
    235 // between two resources. This value is suitable for use as a key in
    236 // a map.
    237 func (r *Resource) Equivalent() attribute.Distinct {
    238 	return r.Set().Equivalent()
    239 }
    240 
    241 // Set returns the equivalent *attribute.Set of this resource's attributes.
    242 func (r *Resource) Set() *attribute.Set {
    243 	if r == nil {
    244 		r = Empty()
    245 	}
    246 	return &r.attrs
    247 }
    248 
    249 // MarshalJSON encodes the resource attributes as a JSON list of { "Key":
    250 // "...", "Value": ... } pairs in order sorted by key.
    251 func (r *Resource) MarshalJSON() ([]byte, error) {
    252 	if r == nil {
    253 		r = Empty()
    254 	}
    255 	return r.attrs.MarshalJSON()
    256 }
    257 
    258 // Len returns the number of unique key-values in this Resource.
    259 func (r *Resource) Len() int {
    260 	if r == nil {
    261 		return 0
    262 	}
    263 	return r.attrs.Len()
    264 }
    265 
    266 // Encoded returns an encoded representation of the resource.
    267 func (r *Resource) Encoded(enc attribute.Encoder) string {
    268 	if r == nil {
    269 		return ""
    270 	}
    271 	return r.attrs.Encoded(enc)
    272 }