Phase 2: Implement Execution Environment Abstraction (v0.3.0)
This commit implements Phase 2 of the CHORUS Task Execution Engine development plan, providing a comprehensive execution environment abstraction layer with Docker container sandboxing support. ## New Features ### Core Sandbox Interface - Comprehensive ExecutionSandbox interface with isolated task execution - Support for command execution, file I/O, environment management - Resource usage monitoring and sandbox lifecycle management - Standardized error handling with SandboxError types and categories ### Docker Container Sandbox Implementation - Full Docker API integration with secure container creation - Transparent repository mounting with configurable read/write access - Advanced security policies with capability dropping and privilege controls - Comprehensive resource limits (CPU, memory, disk, processes, file handles) - Support for tmpfs mounts, masked paths, and read-only bind mounts - Container lifecycle management with proper cleanup and health monitoring ### Security & Resource Management - Configurable security policies with SELinux, AppArmor, and Seccomp support - Fine-grained capability management with secure defaults - Network isolation options with configurable DNS and proxy settings - Resource monitoring with real-time CPU, memory, and network usage tracking - Comprehensive ulimits configuration for process and file handle limits ### Repository Integration - Seamless repository mounting from local paths to container workspaces - Git configuration support with user credentials and global settings - File inclusion/exclusion patterns for selective repository access - Configurable permissions and ownership for mounted repositories ### Testing Infrastructure - Comprehensive test suite with 60+ test cases covering all functionality - Docker integration tests with Alpine Linux containers (skipped in short mode) - Mock sandbox implementation for unit testing without Docker dependencies - Security policy validation tests with read-only filesystem enforcement - Resource usage monitoring and cleanup verification tests ## Technical Details ### Dependencies Added - github.com/docker/docker v28.4.0+incompatible - Docker API client - github.com/docker/go-connections v0.6.0 - Docker connection utilities - github.com/docker/go-units v0.5.0 - Docker units and formatting - Associated Docker API dependencies for complete container management ### Architecture - Interface-driven design enabling multiple sandbox implementations - Comprehensive configuration structures for all sandbox aspects - Resource usage tracking with detailed metrics collection - Error handling with retryable error classification - Proper cleanup and resource management throughout sandbox lifecycle ### Compatibility - Maintains backward compatibility with existing CHORUS architecture - Designed for future integration with Phase 3 Core Task Execution Engine - Extensible design supporting additional sandbox implementations (VM, process) This Phase 2 implementation provides the foundation for secure, isolated task execution that will be integrated with the AI model providers from Phase 1 in the upcoming Phase 3 development. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										58
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/attr.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/attr.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
|  | ||||
| package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" | ||||
|  | ||||
| // Attr is a key-value pair. | ||||
| type Attr struct { | ||||
| 	Key   string `json:"key,omitempty"` | ||||
| 	Value Value  `json:"value,omitempty"` | ||||
| } | ||||
|  | ||||
| // String returns an Attr for a string value. | ||||
| func String(key, value string) Attr { | ||||
| 	return Attr{key, StringValue(value)} | ||||
| } | ||||
|  | ||||
| // Int64 returns an Attr for an int64 value. | ||||
| func Int64(key string, value int64) Attr { | ||||
| 	return Attr{key, Int64Value(value)} | ||||
| } | ||||
|  | ||||
| // Int returns an Attr for an int value. | ||||
| func Int(key string, value int) Attr { | ||||
| 	return Int64(key, int64(value)) | ||||
| } | ||||
|  | ||||
| // Float64 returns an Attr for a float64 value. | ||||
| func Float64(key string, value float64) Attr { | ||||
| 	return Attr{key, Float64Value(value)} | ||||
| } | ||||
|  | ||||
| // Bool returns an Attr for a bool value. | ||||
| func Bool(key string, value bool) Attr { | ||||
| 	return Attr{key, BoolValue(value)} | ||||
| } | ||||
|  | ||||
| // Bytes returns an Attr for a []byte value. | ||||
| // The passed slice must not be changed after it is passed. | ||||
| func Bytes(key string, value []byte) Attr { | ||||
| 	return Attr{key, BytesValue(value)} | ||||
| } | ||||
|  | ||||
| // Slice returns an Attr for a []Value value. | ||||
| // The passed slice must not be changed after it is passed. | ||||
| func Slice(key string, value ...Value) Attr { | ||||
| 	return Attr{key, SliceValue(value...)} | ||||
| } | ||||
|  | ||||
| // Map returns an Attr for a map value. | ||||
| // The passed slice must not be changed after it is passed. | ||||
| func Map(key string, value ...Attr) Attr { | ||||
| 	return Attr{key, MapValue(value...)} | ||||
| } | ||||
|  | ||||
| // Equal reports whether a is equal to b. | ||||
| func (a Attr) Equal(b Attr) bool { | ||||
| 	return a.Key == b.Key && a.Value.Equal(b.Value) | ||||
| } | ||||
							
								
								
									
										8
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
|  | ||||
| /* | ||||
| Package telemetry provides a lightweight representations of OpenTelemetry | ||||
| telemetry that is compatible with the OTLP JSON protobuf encoding. | ||||
| */ | ||||
| package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" | ||||
							
								
								
									
										103
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/id.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/id.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
|  | ||||
| package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" | ||||
|  | ||||
| import ( | ||||
| 	"encoding/hex" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	traceIDSize = 16 | ||||
| 	spanIDSize  = 8 | ||||
| ) | ||||
|  | ||||
| // TraceID is a custom data type that is used for all trace IDs. | ||||
| type TraceID [traceIDSize]byte | ||||
|  | ||||
| // String returns the hex string representation form of a TraceID. | ||||
| func (tid TraceID) String() string { | ||||
| 	return hex.EncodeToString(tid[:]) | ||||
| } | ||||
|  | ||||
| // IsEmpty reports whether the TraceID contains only zero bytes. | ||||
| func (tid TraceID) IsEmpty() bool { | ||||
| 	return tid == [traceIDSize]byte{} | ||||
| } | ||||
|  | ||||
| // MarshalJSON converts the trace ID into a hex string enclosed in quotes. | ||||
| func (tid TraceID) MarshalJSON() ([]byte, error) { | ||||
| 	if tid.IsEmpty() { | ||||
| 		return []byte(`""`), nil | ||||
| 	} | ||||
| 	return marshalJSON(tid[:]) | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON inflates the trace ID from hex string, possibly enclosed in | ||||
| // quotes. | ||||
| func (tid *TraceID) UnmarshalJSON(data []byte) error { | ||||
| 	*tid = [traceIDSize]byte{} | ||||
| 	return unmarshalJSON(tid[:], data) | ||||
| } | ||||
|  | ||||
| // SpanID is a custom data type that is used for all span IDs. | ||||
| type SpanID [spanIDSize]byte | ||||
|  | ||||
| // String returns the hex string representation form of a SpanID. | ||||
| func (sid SpanID) String() string { | ||||
| 	return hex.EncodeToString(sid[:]) | ||||
| } | ||||
|  | ||||
| // IsEmpty reports whether the SpanID contains only zero bytes. | ||||
| func (sid SpanID) IsEmpty() bool { | ||||
| 	return sid == [spanIDSize]byte{} | ||||
| } | ||||
|  | ||||
| // MarshalJSON converts span ID into a hex string enclosed in quotes. | ||||
| func (sid SpanID) MarshalJSON() ([]byte, error) { | ||||
| 	if sid.IsEmpty() { | ||||
| 		return []byte(`""`), nil | ||||
| 	} | ||||
| 	return marshalJSON(sid[:]) | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes span ID from hex string, possibly enclosed in quotes. | ||||
| func (sid *SpanID) UnmarshalJSON(data []byte) error { | ||||
| 	*sid = [spanIDSize]byte{} | ||||
| 	return unmarshalJSON(sid[:], data) | ||||
| } | ||||
|  | ||||
| // marshalJSON converts id into a hex string enclosed in quotes. | ||||
| func marshalJSON(id []byte) ([]byte, error) { | ||||
| 	// Plus 2 quote chars at the start and end. | ||||
| 	hexLen := hex.EncodedLen(len(id)) + 2 | ||||
|  | ||||
| 	b := make([]byte, hexLen) | ||||
| 	hex.Encode(b[1:hexLen-1], id) | ||||
| 	b[0], b[hexLen-1] = '"', '"' | ||||
|  | ||||
| 	return b, nil | ||||
| } | ||||
|  | ||||
| // unmarshalJSON inflates trace id from hex string, possibly enclosed in quotes. | ||||
| func unmarshalJSON(dst, src []byte) error { | ||||
| 	if l := len(src); l >= 2 && src[0] == '"' && src[l-1] == '"' { | ||||
| 		src = src[1 : l-1] | ||||
| 	} | ||||
| 	nLen := len(src) | ||||
| 	if nLen == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if len(dst) != hex.DecodedLen(nLen) { | ||||
| 		return errors.New("invalid length for ID") | ||||
| 	} | ||||
|  | ||||
| 	_, err := hex.Decode(dst, src) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("cannot unmarshal ID from string '%s': %w", string(src), err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										67
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/number.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/number.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
|  | ||||
| package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"strconv" | ||||
| ) | ||||
|  | ||||
| // protoInt64 represents the protobuf encoding of integers which can be either | ||||
| // strings or integers. | ||||
| type protoInt64 int64 | ||||
|  | ||||
| // Int64 returns the protoInt64 as an int64. | ||||
| func (i *protoInt64) Int64() int64 { return int64(*i) } | ||||
|  | ||||
| // UnmarshalJSON decodes both strings and integers. | ||||
| func (i *protoInt64) UnmarshalJSON(data []byte) error { | ||||
| 	if data[0] == '"' { | ||||
| 		var str string | ||||
| 		if err := json.Unmarshal(data, &str); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		parsedInt, err := strconv.ParseInt(str, 10, 64) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		*i = protoInt64(parsedInt) | ||||
| 	} else { | ||||
| 		var parsedInt int64 | ||||
| 		if err := json.Unmarshal(data, &parsedInt); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		*i = protoInt64(parsedInt) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // protoUint64 represents the protobuf encoding of integers which can be either | ||||
| // strings or integers. | ||||
| type protoUint64 uint64 | ||||
|  | ||||
| // Int64 returns the protoUint64 as a uint64. | ||||
| func (i *protoUint64) Uint64() uint64 { return uint64(*i) } | ||||
|  | ||||
| // UnmarshalJSON decodes both strings and integers. | ||||
| func (i *protoUint64) UnmarshalJSON(data []byte) error { | ||||
| 	if data[0] == '"' { | ||||
| 		var str string | ||||
| 		if err := json.Unmarshal(data, &str); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		parsedUint, err := strconv.ParseUint(str, 10, 64) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		*i = protoUint64(parsedUint) | ||||
| 	} else { | ||||
| 		var parsedUint uint64 | ||||
| 		if err := json.Unmarshal(data, &parsedUint); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		*i = protoUint64(parsedUint) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										66
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/resource.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/resource.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
|  | ||||
| package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| // Resource information. | ||||
| type Resource struct { | ||||
| 	// Attrs are the set of attributes that describe the resource. Attribute | ||||
| 	// keys MUST be unique (it is not allowed to have more than one attribute | ||||
| 	// with the same key). | ||||
| 	Attrs []Attr `json:"attributes,omitempty"` | ||||
| 	// DroppedAttrs is the number of dropped attributes. If the value | ||||
| 	// is 0, then no attributes were dropped. | ||||
| 	DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"` | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes the OTLP formatted JSON contained in data into r. | ||||
| func (r *Resource) UnmarshalJSON(data []byte) error { | ||||
| 	decoder := json.NewDecoder(bytes.NewReader(data)) | ||||
|  | ||||
| 	t, err := decoder.Token() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if t != json.Delim('{') { | ||||
| 		return errors.New("invalid Resource type") | ||||
| 	} | ||||
|  | ||||
| 	for decoder.More() { | ||||
| 		keyIface, err := decoder.Token() | ||||
| 		if err != nil { | ||||
| 			if errors.Is(err, io.EOF) { | ||||
| 				// Empty. | ||||
| 				return nil | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		key, ok := keyIface.(string) | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("invalid Resource field: %#v", keyIface) | ||||
| 		} | ||||
|  | ||||
| 		switch key { | ||||
| 		case "attributes": | ||||
| 			err = decoder.Decode(&r.Attrs) | ||||
| 		case "droppedAttributesCount", "dropped_attributes_count": | ||||
| 			err = decoder.Decode(&r.DroppedAttrs) | ||||
| 		default: | ||||
| 			// Skip unknown. | ||||
| 		} | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										67
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/scope.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/scope.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
|  | ||||
| package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| // Scope is the identifying values of the instrumentation scope. | ||||
| type Scope struct { | ||||
| 	Name         string `json:"name,omitempty"` | ||||
| 	Version      string `json:"version,omitempty"` | ||||
| 	Attrs        []Attr `json:"attributes,omitempty"` | ||||
| 	DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"` | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes the OTLP formatted JSON contained in data into r. | ||||
| func (s *Scope) UnmarshalJSON(data []byte) error { | ||||
| 	decoder := json.NewDecoder(bytes.NewReader(data)) | ||||
|  | ||||
| 	t, err := decoder.Token() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if t != json.Delim('{') { | ||||
| 		return errors.New("invalid Scope type") | ||||
| 	} | ||||
|  | ||||
| 	for decoder.More() { | ||||
| 		keyIface, err := decoder.Token() | ||||
| 		if err != nil { | ||||
| 			if errors.Is(err, io.EOF) { | ||||
| 				// Empty. | ||||
| 				return nil | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		key, ok := keyIface.(string) | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("invalid Scope field: %#v", keyIface) | ||||
| 		} | ||||
|  | ||||
| 		switch key { | ||||
| 		case "name": | ||||
| 			err = decoder.Decode(&s.Name) | ||||
| 		case "version": | ||||
| 			err = decoder.Decode(&s.Version) | ||||
| 		case "attributes": | ||||
| 			err = decoder.Decode(&s.Attrs) | ||||
| 		case "droppedAttributesCount", "dropped_attributes_count": | ||||
| 			err = decoder.Decode(&s.DroppedAttrs) | ||||
| 		default: | ||||
| 			// Skip unknown. | ||||
| 		} | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										472
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/span.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										472
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/span.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,472 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
|  | ||||
| package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/hex" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // A Span represents a single operation performed by a single component of the | ||||
| // system. | ||||
| type Span struct { | ||||
| 	// A unique identifier for a trace. All spans from the same trace share | ||||
| 	// the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR | ||||
| 	// of length other than 16 bytes is considered invalid (empty string in OTLP/JSON | ||||
| 	// is zero-length and thus is also invalid). | ||||
| 	// | ||||
| 	// This field is required. | ||||
| 	TraceID TraceID `json:"traceId,omitempty"` | ||||
| 	// A unique identifier for a span within a trace, assigned when the span | ||||
| 	// is created. The ID is an 8-byte array. An ID with all zeroes OR of length | ||||
| 	// other than 8 bytes is considered invalid (empty string in OTLP/JSON | ||||
| 	// is zero-length and thus is also invalid). | ||||
| 	// | ||||
| 	// This field is required. | ||||
| 	SpanID SpanID `json:"spanId,omitempty"` | ||||
| 	// trace_state conveys information about request position in multiple distributed tracing graphs. | ||||
| 	// It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header | ||||
| 	// See also https://github.com/w3c/distributed-tracing for more details about this field. | ||||
| 	TraceState string `json:"traceState,omitempty"` | ||||
| 	// The `span_id` of this span's parent span. If this is a root span, then this | ||||
| 	// field must be empty. The ID is an 8-byte array. | ||||
| 	ParentSpanID SpanID `json:"parentSpanId,omitempty"` | ||||
| 	// Flags, a bit field. | ||||
| 	// | ||||
| 	// Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace | ||||
| 	// Context specification. To read the 8-bit W3C trace flag, use | ||||
| 	// `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. | ||||
| 	// | ||||
| 	// See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. | ||||
| 	// | ||||
| 	// Bits 8 and 9 represent the 3 states of whether a span's parent | ||||
| 	// is remote. The states are (unknown, is not remote, is remote). | ||||
| 	// To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. | ||||
| 	// To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. | ||||
| 	// | ||||
| 	// When creating span messages, if the message is logically forwarded from another source | ||||
| 	// with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD | ||||
| 	// be copied as-is. If creating from a source that does not have an equivalent flags field | ||||
| 	// (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST | ||||
| 	// be set to zero. | ||||
| 	// Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. | ||||
| 	// | ||||
| 	// [Optional]. | ||||
| 	Flags uint32 `json:"flags,omitempty"` | ||||
| 	// A description of the span's operation. | ||||
| 	// | ||||
| 	// For example, the name can be a qualified method name or a file name | ||||
| 	// and a line number where the operation is called. A best practice is to use | ||||
| 	// the same display name at the same call point in an application. | ||||
| 	// This makes it easier to correlate spans in different traces. | ||||
| 	// | ||||
| 	// This field is semantically required to be set to non-empty string. | ||||
| 	// Empty value is equivalent to an unknown span name. | ||||
| 	// | ||||
| 	// This field is required. | ||||
| 	Name string `json:"name"` | ||||
| 	// Distinguishes between spans generated in a particular context. For example, | ||||
| 	// two spans with the same name may be distinguished using `CLIENT` (caller) | ||||
| 	// and `SERVER` (callee) to identify queueing latency associated with the span. | ||||
| 	Kind SpanKind `json:"kind,omitempty"` | ||||
| 	// start_time_unix_nano is the start time of the span. On the client side, this is the time | ||||
| 	// kept by the local machine where the span execution starts. On the server side, this | ||||
| 	// is the time when the server's application handler starts running. | ||||
| 	// Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. | ||||
| 	// | ||||
| 	// This field is semantically required and it is expected that end_time >= start_time. | ||||
| 	StartTime time.Time `json:"startTimeUnixNano,omitempty"` | ||||
| 	// end_time_unix_nano is the end time of the span. On the client side, this is the time | ||||
| 	// kept by the local machine where the span execution ends. On the server side, this | ||||
| 	// is the time when the server application handler stops running. | ||||
| 	// Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. | ||||
| 	// | ||||
| 	// This field is semantically required and it is expected that end_time >= start_time. | ||||
| 	EndTime time.Time `json:"endTimeUnixNano,omitempty"` | ||||
| 	// attributes is a collection of key/value pairs. Note, global attributes | ||||
| 	// like server name can be set using the resource API. Examples of attributes: | ||||
| 	// | ||||
| 	//     "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" | ||||
| 	//     "/http/server_latency": 300 | ||||
| 	//     "example.com/myattribute": true | ||||
| 	//     "example.com/score": 10.239 | ||||
| 	// | ||||
| 	// The OpenTelemetry API specification further restricts the allowed value types: | ||||
| 	// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute | ||||
| 	// Attribute keys MUST be unique (it is not allowed to have more than one | ||||
| 	// attribute with the same key). | ||||
| 	Attrs []Attr `json:"attributes,omitempty"` | ||||
| 	// dropped_attributes_count is the number of attributes that were discarded. Attributes | ||||
| 	// can be discarded because their keys are too long or because there are too many | ||||
| 	// attributes. If this value is 0, then no attributes were dropped. | ||||
| 	DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"` | ||||
| 	// events is a collection of Event items. | ||||
| 	Events []*SpanEvent `json:"events,omitempty"` | ||||
| 	// dropped_events_count is the number of dropped events. If the value is 0, then no | ||||
| 	// events were dropped. | ||||
| 	DroppedEvents uint32 `json:"droppedEventsCount,omitempty"` | ||||
| 	// links is a collection of Links, which are references from this span to a span | ||||
| 	// in the same or different trace. | ||||
| 	Links []*SpanLink `json:"links,omitempty"` | ||||
| 	// dropped_links_count is the number of dropped links after the maximum size was | ||||
| 	// enforced. If this value is 0, then no links were dropped. | ||||
| 	DroppedLinks uint32 `json:"droppedLinksCount,omitempty"` | ||||
| 	// An optional final status for this span. Semantically when Status isn't set, it means | ||||
| 	// span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0). | ||||
| 	Status *Status `json:"status,omitempty"` | ||||
| } | ||||
|  | ||||
| // MarshalJSON encodes s into OTLP formatted JSON. | ||||
| func (s Span) MarshalJSON() ([]byte, error) { | ||||
| 	startT := s.StartTime.UnixNano() | ||||
| 	if s.StartTime.IsZero() || startT < 0 { | ||||
| 		startT = 0 | ||||
| 	} | ||||
|  | ||||
| 	endT := s.EndTime.UnixNano() | ||||
| 	if s.EndTime.IsZero() || endT < 0 { | ||||
| 		endT = 0 | ||||
| 	} | ||||
|  | ||||
| 	// Override non-empty default SpanID marshal and omitempty. | ||||
| 	var parentSpanId string | ||||
| 	if !s.ParentSpanID.IsEmpty() { | ||||
| 		b := make([]byte, hex.EncodedLen(spanIDSize)) | ||||
| 		hex.Encode(b, s.ParentSpanID[:]) | ||||
| 		parentSpanId = string(b) | ||||
| 	} | ||||
|  | ||||
| 	type Alias Span | ||||
| 	return json.Marshal(struct { | ||||
| 		Alias | ||||
| 		ParentSpanID string `json:"parentSpanId,omitempty"` | ||||
| 		StartTime    uint64 `json:"startTimeUnixNano,omitempty"` | ||||
| 		EndTime      uint64 `json:"endTimeUnixNano,omitempty"` | ||||
| 	}{ | ||||
| 		Alias:        Alias(s), | ||||
| 		ParentSpanID: parentSpanId, | ||||
| 		StartTime:    uint64(startT), // nolint:gosec  // >0 checked above. | ||||
| 		EndTime:      uint64(endT),   // nolint:gosec  // >0 checked above. | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes the OTLP formatted JSON contained in data into s. | ||||
| func (s *Span) UnmarshalJSON(data []byte) error { | ||||
| 	decoder := json.NewDecoder(bytes.NewReader(data)) | ||||
|  | ||||
| 	t, err := decoder.Token() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if t != json.Delim('{') { | ||||
| 		return errors.New("invalid Span type") | ||||
| 	} | ||||
|  | ||||
| 	for decoder.More() { | ||||
| 		keyIface, err := decoder.Token() | ||||
| 		if err != nil { | ||||
| 			if errors.Is(err, io.EOF) { | ||||
| 				// Empty. | ||||
| 				return nil | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		key, ok := keyIface.(string) | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("invalid Span field: %#v", keyIface) | ||||
| 		} | ||||
|  | ||||
| 		switch key { | ||||
| 		case "traceId", "trace_id": | ||||
| 			err = decoder.Decode(&s.TraceID) | ||||
| 		case "spanId", "span_id": | ||||
| 			err = decoder.Decode(&s.SpanID) | ||||
| 		case "traceState", "trace_state": | ||||
| 			err = decoder.Decode(&s.TraceState) | ||||
| 		case "parentSpanId", "parent_span_id": | ||||
| 			err = decoder.Decode(&s.ParentSpanID) | ||||
| 		case "flags": | ||||
| 			err = decoder.Decode(&s.Flags) | ||||
| 		case "name": | ||||
| 			err = decoder.Decode(&s.Name) | ||||
| 		case "kind": | ||||
| 			err = decoder.Decode(&s.Kind) | ||||
| 		case "startTimeUnixNano", "start_time_unix_nano": | ||||
| 			var val protoUint64 | ||||
| 			err = decoder.Decode(&val) | ||||
| 			v := int64(min(val.Uint64(), math.MaxInt64)) // nolint: gosec  // Overflow checked. | ||||
| 			s.StartTime = time.Unix(0, v) | ||||
| 		case "endTimeUnixNano", "end_time_unix_nano": | ||||
| 			var val protoUint64 | ||||
| 			err = decoder.Decode(&val) | ||||
| 			v := int64(min(val.Uint64(), math.MaxInt64)) // nolint: gosec  // Overflow checked. | ||||
| 			s.EndTime = time.Unix(0, v) | ||||
| 		case "attributes": | ||||
| 			err = decoder.Decode(&s.Attrs) | ||||
| 		case "droppedAttributesCount", "dropped_attributes_count": | ||||
| 			err = decoder.Decode(&s.DroppedAttrs) | ||||
| 		case "events": | ||||
| 			err = decoder.Decode(&s.Events) | ||||
| 		case "droppedEventsCount", "dropped_events_count": | ||||
| 			err = decoder.Decode(&s.DroppedEvents) | ||||
| 		case "links": | ||||
| 			err = decoder.Decode(&s.Links) | ||||
| 		case "droppedLinksCount", "dropped_links_count": | ||||
| 			err = decoder.Decode(&s.DroppedLinks) | ||||
| 		case "status": | ||||
| 			err = decoder.Decode(&s.Status) | ||||
| 		default: | ||||
| 			// Skip unknown. | ||||
| 		} | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // SpanFlags represents constants used to interpret the | ||||
| // Span.flags field, which is protobuf 'fixed32' type and is to | ||||
| // be used as bit-fields. Each non-zero value defined in this enum is | ||||
| // a bit-mask.  To extract the bit-field, for example, use an | ||||
| // expression like: | ||||
| // | ||||
| //	(span.flags & SPAN_FLAGS_TRACE_FLAGS_MASK) | ||||
| // | ||||
| // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. | ||||
| // | ||||
| // Note that Span flags were introduced in version 1.1 of the | ||||
| // OpenTelemetry protocol.  Older Span producers do not set this | ||||
| // field, consequently consumers should not rely on the absence of a | ||||
| // particular flag bit to indicate the presence of a particular feature. | ||||
| type SpanFlags int32 | ||||
|  | ||||
| const ( | ||||
| 	// SpanFlagsTraceFlagsMask is a mask for trace-flags. | ||||
| 	// | ||||
| 	// Bits 0-7 are used for trace flags. | ||||
| 	SpanFlagsTraceFlagsMask SpanFlags = 255 | ||||
| 	// SpanFlagsContextHasIsRemoteMask is a mask for HAS_IS_REMOTE status. | ||||
| 	// | ||||
| 	// Bits 8 and 9 are used to indicate that the parent span or link span is | ||||
| 	// remote. Bit 8 (`HAS_IS_REMOTE`) indicates whether the value is known. | ||||
| 	SpanFlagsContextHasIsRemoteMask SpanFlags = 256 | ||||
| 	// SpanFlagsContextIsRemoteMask is a mask for IS_REMOTE status. | ||||
| 	// | ||||
| 	// Bits 8 and 9 are used to indicate that the parent span or link span is | ||||
| 	// remote. Bit 9 (`IS_REMOTE`) indicates whether the span or link is | ||||
| 	// remote. | ||||
| 	SpanFlagsContextIsRemoteMask SpanFlags = 512 | ||||
| ) | ||||
|  | ||||
| // SpanKind is the type of span. Can be used to specify additional relationships between spans | ||||
| // in addition to a parent/child relationship. | ||||
| type SpanKind int32 | ||||
|  | ||||
| const ( | ||||
| 	// SpanKindInternal indicates that the span represents an internal | ||||
| 	// operation within an application, as opposed to an operation happening at | ||||
| 	// the boundaries. | ||||
| 	SpanKindInternal SpanKind = 1 | ||||
| 	// SpanKindServer indicates that the span covers server-side handling of an | ||||
| 	// RPC or other remote network request. | ||||
| 	SpanKindServer SpanKind = 2 | ||||
| 	// SpanKindClient indicates that the span describes a request to some | ||||
| 	// remote service. | ||||
| 	SpanKindClient SpanKind = 3 | ||||
| 	// SpanKindProducer indicates that the span describes a producer sending a | ||||
| 	// message to a broker. Unlike SpanKindClient and SpanKindServer, there is | ||||
| 	// often no direct critical path latency relationship between producer and | ||||
| 	// consumer spans. A SpanKindProducer span ends when the message was | ||||
| 	// accepted by the broker while the logical processing of the message might | ||||
| 	// span a much longer time. | ||||
| 	SpanKindProducer SpanKind = 4 | ||||
| 	// SpanKindConsumer indicates that the span describes a consumer receiving | ||||
| 	// a message from a broker. Like SpanKindProducer, there is often no direct | ||||
| 	// critical path latency relationship between producer and consumer spans. | ||||
| 	SpanKindConsumer SpanKind = 5 | ||||
| ) | ||||
|  | ||||
| // SpanEvent is a time-stamped annotation of the span, consisting of | ||||
| // user-supplied text description and key-value pairs. | ||||
| type SpanEvent struct { | ||||
| 	// time_unix_nano is the time the event occurred. | ||||
| 	Time time.Time `json:"timeUnixNano,omitempty"` | ||||
| 	// name of the event. | ||||
| 	// This field is semantically required to be set to non-empty string. | ||||
| 	Name string `json:"name,omitempty"` | ||||
| 	// attributes is a collection of attribute key/value pairs on the event. | ||||
| 	// Attribute keys MUST be unique (it is not allowed to have more than one | ||||
| 	// attribute with the same key). | ||||
| 	Attrs []Attr `json:"attributes,omitempty"` | ||||
| 	// dropped_attributes_count is the number of dropped attributes. If the value is 0, | ||||
| 	// then no attributes were dropped. | ||||
| 	DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"` | ||||
| } | ||||
|  | ||||
| // MarshalJSON encodes e into OTLP formatted JSON. | ||||
| func (e SpanEvent) MarshalJSON() ([]byte, error) { | ||||
| 	t := e.Time.UnixNano() | ||||
| 	if e.Time.IsZero() || t < 0 { | ||||
| 		t = 0 | ||||
| 	} | ||||
|  | ||||
| 	type Alias SpanEvent | ||||
| 	return json.Marshal(struct { | ||||
| 		Alias | ||||
| 		Time uint64 `json:"timeUnixNano,omitempty"` | ||||
| 	}{ | ||||
| 		Alias: Alias(e), | ||||
| 		Time:  uint64(t), // nolint: gosec  // >0 checked above | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes the OTLP formatted JSON contained in data into se. | ||||
| func (se *SpanEvent) UnmarshalJSON(data []byte) error { | ||||
| 	decoder := json.NewDecoder(bytes.NewReader(data)) | ||||
|  | ||||
| 	t, err := decoder.Token() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if t != json.Delim('{') { | ||||
| 		return errors.New("invalid SpanEvent type") | ||||
| 	} | ||||
|  | ||||
| 	for decoder.More() { | ||||
| 		keyIface, err := decoder.Token() | ||||
| 		if err != nil { | ||||
| 			if errors.Is(err, io.EOF) { | ||||
| 				// Empty. | ||||
| 				return nil | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		key, ok := keyIface.(string) | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("invalid SpanEvent field: %#v", keyIface) | ||||
| 		} | ||||
|  | ||||
| 		switch key { | ||||
| 		case "timeUnixNano", "time_unix_nano": | ||||
| 			var val protoUint64 | ||||
| 			err = decoder.Decode(&val) | ||||
| 			v := int64(min(val.Uint64(), math.MaxInt64)) // nolint: gosec  // Overflow checked. | ||||
| 			se.Time = time.Unix(0, v) | ||||
| 		case "name": | ||||
| 			err = decoder.Decode(&se.Name) | ||||
| 		case "attributes": | ||||
| 			err = decoder.Decode(&se.Attrs) | ||||
| 		case "droppedAttributesCount", "dropped_attributes_count": | ||||
| 			err = decoder.Decode(&se.DroppedAttrs) | ||||
| 		default: | ||||
| 			// Skip unknown. | ||||
| 		} | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // SpanLink is a reference from the current span to another span in the same | ||||
| // trace or in a different trace. For example, this can be used in batching | ||||
| // operations, where a single batch handler processes multiple requests from | ||||
| // different traces or when the handler receives a request from a different | ||||
| // project. | ||||
| type SpanLink struct { | ||||
| 	// A unique identifier of a trace that this linked span is part of. The ID is a | ||||
| 	// 16-byte array. | ||||
| 	TraceID TraceID `json:"traceId,omitempty"` | ||||
| 	// A unique identifier for the linked span. The ID is an 8-byte array. | ||||
| 	SpanID SpanID `json:"spanId,omitempty"` | ||||
| 	// The trace_state associated with the link. | ||||
| 	TraceState string `json:"traceState,omitempty"` | ||||
| 	// attributes is a collection of attribute key/value pairs on the link. | ||||
| 	// Attribute keys MUST be unique (it is not allowed to have more than one | ||||
| 	// attribute with the same key). | ||||
| 	Attrs []Attr `json:"attributes,omitempty"` | ||||
| 	// dropped_attributes_count is the number of dropped attributes. If the value is 0, | ||||
| 	// then no attributes were dropped. | ||||
| 	DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"` | ||||
| 	// Flags, a bit field. | ||||
| 	// | ||||
| 	// Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace | ||||
| 	// Context specification. To read the 8-bit W3C trace flag, use | ||||
| 	// `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. | ||||
| 	// | ||||
| 	// See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. | ||||
| 	// | ||||
| 	// Bits 8 and 9 represent the 3 states of whether the link is remote. | ||||
| 	// The states are (unknown, is not remote, is remote). | ||||
| 	// To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. | ||||
| 	// To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. | ||||
| 	// | ||||
| 	// Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. | ||||
| 	// When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero. | ||||
| 	// | ||||
| 	// [Optional]. | ||||
| 	Flags uint32 `json:"flags,omitempty"` | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes the OTLP formatted JSON contained in data into sl. | ||||
| func (sl *SpanLink) UnmarshalJSON(data []byte) error { | ||||
| 	decoder := json.NewDecoder(bytes.NewReader(data)) | ||||
|  | ||||
| 	t, err := decoder.Token() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if t != json.Delim('{') { | ||||
| 		return errors.New("invalid SpanLink type") | ||||
| 	} | ||||
|  | ||||
| 	for decoder.More() { | ||||
| 		keyIface, err := decoder.Token() | ||||
| 		if err != nil { | ||||
| 			if errors.Is(err, io.EOF) { | ||||
| 				// Empty. | ||||
| 				return nil | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		key, ok := keyIface.(string) | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("invalid SpanLink field: %#v", keyIface) | ||||
| 		} | ||||
|  | ||||
| 		switch key { | ||||
| 		case "traceId", "trace_id": | ||||
| 			err = decoder.Decode(&sl.TraceID) | ||||
| 		case "spanId", "span_id": | ||||
| 			err = decoder.Decode(&sl.SpanID) | ||||
| 		case "traceState", "trace_state": | ||||
| 			err = decoder.Decode(&sl.TraceState) | ||||
| 		case "attributes": | ||||
| 			err = decoder.Decode(&sl.Attrs) | ||||
| 		case "droppedAttributesCount", "dropped_attributes_count": | ||||
| 			err = decoder.Decode(&sl.DroppedAttrs) | ||||
| 		case "flags": | ||||
| 			err = decoder.Decode(&sl.Flags) | ||||
| 		default: | ||||
| 			// Skip unknown. | ||||
| 		} | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										42
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
|  | ||||
| package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" | ||||
|  | ||||
| // StatusCode is the status of a Span. | ||||
| // | ||||
| // For the semantics of status codes see | ||||
| // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status | ||||
| type StatusCode int32 | ||||
|  | ||||
| const ( | ||||
| 	// StatusCodeUnset is the default status. | ||||
| 	StatusCodeUnset StatusCode = 0 | ||||
| 	// StatusCodeOK is used when the Span has been validated by an Application | ||||
| 	// developer or Operator to have completed successfully. | ||||
| 	StatusCodeOK StatusCode = 1 | ||||
| 	// StatusCodeError is used when the Span contains an error. | ||||
| 	StatusCodeError StatusCode = 2 | ||||
| ) | ||||
|  | ||||
| var statusCodeStrings = []string{ | ||||
| 	"Unset", | ||||
| 	"OK", | ||||
| 	"Error", | ||||
| } | ||||
|  | ||||
| func (s StatusCode) String() string { | ||||
| 	if s >= 0 && int(s) < len(statusCodeStrings) { | ||||
| 		return statusCodeStrings[s] | ||||
| 	} | ||||
| 	return "<unknown telemetry.StatusCode>" | ||||
| } | ||||
|  | ||||
| // Status defines a logical error model that is suitable for different | ||||
| // programming environments, including REST APIs and RPC APIs. | ||||
| type Status struct { | ||||
| 	// A developer-facing human readable error message. | ||||
| 	Message string `json:"message,omitempty"` | ||||
| 	// The status code. | ||||
| 	Code StatusCode `json:"code,omitempty"` | ||||
| } | ||||
							
								
								
									
										189
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/traces.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/traces.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,189 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
|  | ||||
| package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| // Traces represents the traces data that can be stored in a persistent storage, | ||||
| // OR can be embedded by other protocols that transfer OTLP traces data but do | ||||
| // not implement the OTLP protocol. | ||||
| // | ||||
| // The main difference between this message and collector protocol is that | ||||
| // in this message there will not be any "control" or "metadata" specific to | ||||
| // OTLP protocol. | ||||
| // | ||||
| // When new fields are added into this message, the OTLP request MUST be updated | ||||
| // as well. | ||||
| type Traces struct { | ||||
| 	// An array of ResourceSpans. | ||||
| 	// For data coming from a single resource this array will typically contain | ||||
| 	// one element. Intermediary nodes that receive data from multiple origins | ||||
| 	// typically batch the data before forwarding further and in that case this | ||||
| 	// array will contain multiple elements. | ||||
| 	ResourceSpans []*ResourceSpans `json:"resourceSpans,omitempty"` | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes the OTLP formatted JSON contained in data into td. | ||||
| func (td *Traces) UnmarshalJSON(data []byte) error { | ||||
| 	decoder := json.NewDecoder(bytes.NewReader(data)) | ||||
|  | ||||
| 	t, err := decoder.Token() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if t != json.Delim('{') { | ||||
| 		return errors.New("invalid TracesData type") | ||||
| 	} | ||||
|  | ||||
| 	for decoder.More() { | ||||
| 		keyIface, err := decoder.Token() | ||||
| 		if err != nil { | ||||
| 			if errors.Is(err, io.EOF) { | ||||
| 				// Empty. | ||||
| 				return nil | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		key, ok := keyIface.(string) | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("invalid TracesData field: %#v", keyIface) | ||||
| 		} | ||||
|  | ||||
| 		switch key { | ||||
| 		case "resourceSpans", "resource_spans": | ||||
| 			err = decoder.Decode(&td.ResourceSpans) | ||||
| 		default: | ||||
| 			// Skip unknown. | ||||
| 		} | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // ResourceSpans is a collection of ScopeSpans from a Resource. | ||||
| type ResourceSpans struct { | ||||
| 	// The resource for the spans in this message. | ||||
| 	// If this field is not set then no resource info is known. | ||||
| 	Resource Resource `json:"resource"` | ||||
| 	// A list of ScopeSpans that originate from a resource. | ||||
| 	ScopeSpans []*ScopeSpans `json:"scopeSpans,omitempty"` | ||||
| 	// This schema_url applies to the data in the "resource" field. It does not apply | ||||
| 	// to the data in the "scope_spans" field which have their own schema_url field. | ||||
| 	SchemaURL string `json:"schemaUrl,omitempty"` | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes the OTLP formatted JSON contained in data into rs. | ||||
| func (rs *ResourceSpans) UnmarshalJSON(data []byte) error { | ||||
| 	decoder := json.NewDecoder(bytes.NewReader(data)) | ||||
|  | ||||
| 	t, err := decoder.Token() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if t != json.Delim('{') { | ||||
| 		return errors.New("invalid ResourceSpans type") | ||||
| 	} | ||||
|  | ||||
| 	for decoder.More() { | ||||
| 		keyIface, err := decoder.Token() | ||||
| 		if err != nil { | ||||
| 			if errors.Is(err, io.EOF) { | ||||
| 				// Empty. | ||||
| 				return nil | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		key, ok := keyIface.(string) | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("invalid ResourceSpans field: %#v", keyIface) | ||||
| 		} | ||||
|  | ||||
| 		switch key { | ||||
| 		case "resource": | ||||
| 			err = decoder.Decode(&rs.Resource) | ||||
| 		case "scopeSpans", "scope_spans": | ||||
| 			err = decoder.Decode(&rs.ScopeSpans) | ||||
| 		case "schemaUrl", "schema_url": | ||||
| 			err = decoder.Decode(&rs.SchemaURL) | ||||
| 		default: | ||||
| 			// Skip unknown. | ||||
| 		} | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // ScopeSpans is a collection of Spans produced by an InstrumentationScope. | ||||
| type ScopeSpans struct { | ||||
| 	// The instrumentation scope information for the spans in this message. | ||||
| 	// Semantically when InstrumentationScope isn't set, it is equivalent with | ||||
| 	// an empty instrumentation scope name (unknown). | ||||
| 	Scope *Scope `json:"scope"` | ||||
| 	// A list of Spans that originate from an instrumentation scope. | ||||
| 	Spans []*Span `json:"spans,omitempty"` | ||||
| 	// The Schema URL, if known. This is the identifier of the Schema that the span data | ||||
| 	// is recorded in. To learn more about Schema URL see | ||||
| 	// https://opentelemetry.io/docs/specs/otel/schemas/#schema-url | ||||
| 	// This schema_url applies to all spans and span events in the "spans" field. | ||||
| 	SchemaURL string `json:"schemaUrl,omitempty"` | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes the OTLP formatted JSON contained in data into ss. | ||||
| func (ss *ScopeSpans) UnmarshalJSON(data []byte) error { | ||||
| 	decoder := json.NewDecoder(bytes.NewReader(data)) | ||||
|  | ||||
| 	t, err := decoder.Token() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if t != json.Delim('{') { | ||||
| 		return errors.New("invalid ScopeSpans type") | ||||
| 	} | ||||
|  | ||||
| 	for decoder.More() { | ||||
| 		keyIface, err := decoder.Token() | ||||
| 		if err != nil { | ||||
| 			if errors.Is(err, io.EOF) { | ||||
| 				// Empty. | ||||
| 				return nil | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		key, ok := keyIface.(string) | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("invalid ScopeSpans field: %#v", keyIface) | ||||
| 		} | ||||
|  | ||||
| 		switch key { | ||||
| 		case "scope": | ||||
| 			err = decoder.Decode(&ss.Scope) | ||||
| 		case "spans": | ||||
| 			err = decoder.Decode(&ss.Spans) | ||||
| 		case "schemaUrl", "schema_url": | ||||
| 			err = decoder.Decode(&ss.SchemaURL) | ||||
| 		default: | ||||
| 			// Skip unknown. | ||||
| 		} | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										453
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/value.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										453
									
								
								vendor/go.opentelemetry.io/otel/trace/internal/telemetry/value.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,453 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // SPDX-License-Identifier: Apache-2.0 | ||||
|  | ||||
| package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"cmp" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math" | ||||
| 	"slices" | ||||
| 	"strconv" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| // A Value represents a structured value. | ||||
| // A zero value is valid and represents an empty value. | ||||
| type Value struct { | ||||
| 	// Ensure forward compatibility by explicitly making this not comparable. | ||||
| 	noCmp [0]func() //nolint: unused  // This is indeed used. | ||||
|  | ||||
| 	// num holds the value for Int64, Float64, and Bool. It holds the length | ||||
| 	// for String, Bytes, Slice, Map. | ||||
| 	num uint64 | ||||
| 	// any holds either the KindBool, KindInt64, KindFloat64, stringptr, | ||||
| 	// bytesptr, sliceptr, or mapptr. If KindBool, KindInt64, or KindFloat64 | ||||
| 	// then the value of Value is in num as described above. Otherwise, it | ||||
| 	// contains the value wrapped in the appropriate type. | ||||
| 	any any | ||||
| } | ||||
|  | ||||
| type ( | ||||
| 	// sliceptr represents a value in Value.any for KindString Values. | ||||
| 	stringptr *byte | ||||
| 	// bytesptr represents a value in Value.any for KindBytes Values. | ||||
| 	bytesptr *byte | ||||
| 	// sliceptr represents a value in Value.any for KindSlice Values. | ||||
| 	sliceptr *Value | ||||
| 	// mapptr represents a value in Value.any for KindMap Values. | ||||
| 	mapptr *Attr | ||||
| ) | ||||
|  | ||||
| // ValueKind is the kind of a [Value]. | ||||
| type ValueKind int | ||||
|  | ||||
| // ValueKind values. | ||||
| const ( | ||||
| 	ValueKindEmpty ValueKind = iota | ||||
| 	ValueKindBool | ||||
| 	ValueKindFloat64 | ||||
| 	ValueKindInt64 | ||||
| 	ValueKindString | ||||
| 	ValueKindBytes | ||||
| 	ValueKindSlice | ||||
| 	ValueKindMap | ||||
| ) | ||||
|  | ||||
| var valueKindStrings = []string{ | ||||
| 	"Empty", | ||||
| 	"Bool", | ||||
| 	"Float64", | ||||
| 	"Int64", | ||||
| 	"String", | ||||
| 	"Bytes", | ||||
| 	"Slice", | ||||
| 	"Map", | ||||
| } | ||||
|  | ||||
| func (k ValueKind) String() string { | ||||
| 	if k >= 0 && int(k) < len(valueKindStrings) { | ||||
| 		return valueKindStrings[k] | ||||
| 	} | ||||
| 	return "<unknown telemetry.ValueKind>" | ||||
| } | ||||
|  | ||||
| // StringValue returns a new [Value] for a string. | ||||
| func StringValue(v string) Value { | ||||
| 	return Value{ | ||||
| 		num: uint64(len(v)), | ||||
| 		any: stringptr(unsafe.StringData(v)), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // IntValue returns a [Value] for an int. | ||||
| func IntValue(v int) Value { return Int64Value(int64(v)) } | ||||
|  | ||||
| // Int64Value returns a [Value] for an int64. | ||||
| func Int64Value(v int64) Value { | ||||
| 	return Value{ | ||||
| 		num: uint64(v), // nolint: gosec  // Store raw bytes. | ||||
| 		any: ValueKindInt64, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Float64Value returns a [Value] for a float64. | ||||
| func Float64Value(v float64) Value { | ||||
| 	return Value{num: math.Float64bits(v), any: ValueKindFloat64} | ||||
| } | ||||
|  | ||||
| // BoolValue returns a [Value] for a bool. | ||||
| func BoolValue(v bool) Value { //nolint:revive // Not a control flag. | ||||
| 	var n uint64 | ||||
| 	if v { | ||||
| 		n = 1 | ||||
| 	} | ||||
| 	return Value{num: n, any: ValueKindBool} | ||||
| } | ||||
|  | ||||
| // BytesValue returns a [Value] for a byte slice. The passed slice must not be | ||||
| // changed after it is passed. | ||||
| func BytesValue(v []byte) Value { | ||||
| 	return Value{ | ||||
| 		num: uint64(len(v)), | ||||
| 		any: bytesptr(unsafe.SliceData(v)), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // SliceValue returns a [Value] for a slice of [Value]. The passed slice must | ||||
| // not be changed after it is passed. | ||||
| func SliceValue(vs ...Value) Value { | ||||
| 	return Value{ | ||||
| 		num: uint64(len(vs)), | ||||
| 		any: sliceptr(unsafe.SliceData(vs)), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // MapValue returns a new [Value] for a slice of key-value pairs. The passed | ||||
| // slice must not be changed after it is passed. | ||||
| func MapValue(kvs ...Attr) Value { | ||||
| 	return Value{ | ||||
| 		num: uint64(len(kvs)), | ||||
| 		any: mapptr(unsafe.SliceData(kvs)), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // AsString returns the value held by v as a string. | ||||
| func (v Value) AsString() string { | ||||
| 	if sp, ok := v.any.(stringptr); ok { | ||||
| 		return unsafe.String(sp, v.num) | ||||
| 	} | ||||
| 	// TODO: error handle | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // asString returns the value held by v as a string. It will panic if the Value | ||||
| // is not KindString. | ||||
| func (v Value) asString() string { | ||||
| 	return unsafe.String(v.any.(stringptr), v.num) | ||||
| } | ||||
|  | ||||
| // AsInt64 returns the value held by v as an int64. | ||||
| func (v Value) AsInt64() int64 { | ||||
| 	if v.Kind() != ValueKindInt64 { | ||||
| 		// TODO: error handle | ||||
| 		return 0 | ||||
| 	} | ||||
| 	return v.asInt64() | ||||
| } | ||||
|  | ||||
| // asInt64 returns the value held by v as an int64. If v is not of KindInt64, | ||||
| // this will return garbage. | ||||
| func (v Value) asInt64() int64 { | ||||
| 	// Assumes v.num was a valid int64 (overflow not checked). | ||||
| 	return int64(v.num) // nolint: gosec | ||||
| } | ||||
|  | ||||
| // AsBool returns the value held by v as a bool. | ||||
| func (v Value) AsBool() bool { | ||||
| 	if v.Kind() != ValueKindBool { | ||||
| 		// TODO: error handle | ||||
| 		return false | ||||
| 	} | ||||
| 	return v.asBool() | ||||
| } | ||||
|  | ||||
| // asBool returns the value held by v as a bool. If v is not of KindBool, this | ||||
| // will return garbage. | ||||
| func (v Value) asBool() bool { return v.num == 1 } | ||||
|  | ||||
| // AsFloat64 returns the value held by v as a float64. | ||||
| func (v Value) AsFloat64() float64 { | ||||
| 	if v.Kind() != ValueKindFloat64 { | ||||
| 		// TODO: error handle | ||||
| 		return 0 | ||||
| 	} | ||||
| 	return v.asFloat64() | ||||
| } | ||||
|  | ||||
| // asFloat64 returns the value held by v as a float64. If v is not of | ||||
| // KindFloat64, this will return garbage. | ||||
| func (v Value) asFloat64() float64 { return math.Float64frombits(v.num) } | ||||
|  | ||||
| // AsBytes returns the value held by v as a []byte. | ||||
| func (v Value) AsBytes() []byte { | ||||
| 	if sp, ok := v.any.(bytesptr); ok { | ||||
| 		return unsafe.Slice((*byte)(sp), v.num) | ||||
| 	} | ||||
| 	// TODO: error handle | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // asBytes returns the value held by v as a []byte. It will panic if the Value | ||||
| // is not KindBytes. | ||||
| func (v Value) asBytes() []byte { | ||||
| 	return unsafe.Slice((*byte)(v.any.(bytesptr)), v.num) | ||||
| } | ||||
|  | ||||
| // AsSlice returns the value held by v as a []Value. | ||||
| func (v Value) AsSlice() []Value { | ||||
| 	if sp, ok := v.any.(sliceptr); ok { | ||||
| 		return unsafe.Slice((*Value)(sp), v.num) | ||||
| 	} | ||||
| 	// TODO: error handle | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // asSlice returns the value held by v as a []Value. It will panic if the Value | ||||
| // is not KindSlice. | ||||
| func (v Value) asSlice() []Value { | ||||
| 	return unsafe.Slice((*Value)(v.any.(sliceptr)), v.num) | ||||
| } | ||||
|  | ||||
| // AsMap returns the value held by v as a []Attr. | ||||
| func (v Value) AsMap() []Attr { | ||||
| 	if sp, ok := v.any.(mapptr); ok { | ||||
| 		return unsafe.Slice((*Attr)(sp), v.num) | ||||
| 	} | ||||
| 	// TODO: error handle | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // asMap returns the value held by v as a []Attr. It will panic if the | ||||
| // Value is not KindMap. | ||||
| func (v Value) asMap() []Attr { | ||||
| 	return unsafe.Slice((*Attr)(v.any.(mapptr)), v.num) | ||||
| } | ||||
|  | ||||
| // Kind returns the Kind of v. | ||||
| func (v Value) Kind() ValueKind { | ||||
| 	switch x := v.any.(type) { | ||||
| 	case ValueKind: | ||||
| 		return x | ||||
| 	case stringptr: | ||||
| 		return ValueKindString | ||||
| 	case bytesptr: | ||||
| 		return ValueKindBytes | ||||
| 	case sliceptr: | ||||
| 		return ValueKindSlice | ||||
| 	case mapptr: | ||||
| 		return ValueKindMap | ||||
| 	default: | ||||
| 		return ValueKindEmpty | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Empty reports whether v does not hold any value. | ||||
| func (v Value) Empty() bool { return v.Kind() == ValueKindEmpty } | ||||
|  | ||||
| // Equal reports whether v is equal to w. | ||||
| func (v Value) Equal(w Value) bool { | ||||
| 	k1 := v.Kind() | ||||
| 	k2 := w.Kind() | ||||
| 	if k1 != k2 { | ||||
| 		return false | ||||
| 	} | ||||
| 	switch k1 { | ||||
| 	case ValueKindInt64, ValueKindBool: | ||||
| 		return v.num == w.num | ||||
| 	case ValueKindString: | ||||
| 		return v.asString() == w.asString() | ||||
| 	case ValueKindFloat64: | ||||
| 		return v.asFloat64() == w.asFloat64() | ||||
| 	case ValueKindSlice: | ||||
| 		return slices.EqualFunc(v.asSlice(), w.asSlice(), Value.Equal) | ||||
| 	case ValueKindMap: | ||||
| 		sv := sortMap(v.asMap()) | ||||
| 		sw := sortMap(w.asMap()) | ||||
| 		return slices.EqualFunc(sv, sw, Attr.Equal) | ||||
| 	case ValueKindBytes: | ||||
| 		return bytes.Equal(v.asBytes(), w.asBytes()) | ||||
| 	case ValueKindEmpty: | ||||
| 		return true | ||||
| 	default: | ||||
| 		// TODO: error handle | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func sortMap(m []Attr) []Attr { | ||||
| 	sm := make([]Attr, len(m)) | ||||
| 	copy(sm, m) | ||||
| 	slices.SortFunc(sm, func(a, b Attr) int { | ||||
| 		return cmp.Compare(a.Key, b.Key) | ||||
| 	}) | ||||
|  | ||||
| 	return sm | ||||
| } | ||||
|  | ||||
| // String returns Value's value as a string, formatted like [fmt.Sprint]. | ||||
| // | ||||
| // The returned string is meant for debugging; | ||||
| // the string representation is not stable. | ||||
| func (v Value) String() string { | ||||
| 	switch v.Kind() { | ||||
| 	case ValueKindString: | ||||
| 		return v.asString() | ||||
| 	case ValueKindInt64: | ||||
| 		// Assumes v.num was a valid int64 (overflow not checked). | ||||
| 		return strconv.FormatInt(int64(v.num), 10) // nolint: gosec | ||||
| 	case ValueKindFloat64: | ||||
| 		return strconv.FormatFloat(v.asFloat64(), 'g', -1, 64) | ||||
| 	case ValueKindBool: | ||||
| 		return strconv.FormatBool(v.asBool()) | ||||
| 	case ValueKindBytes: | ||||
| 		return string(v.asBytes()) | ||||
| 	case ValueKindMap: | ||||
| 		return fmt.Sprint(v.asMap()) | ||||
| 	case ValueKindSlice: | ||||
| 		return fmt.Sprint(v.asSlice()) | ||||
| 	case ValueKindEmpty: | ||||
| 		return "<nil>" | ||||
| 	default: | ||||
| 		// Try to handle this as gracefully as possible. | ||||
| 		// | ||||
| 		// Don't panic here. The goal here is to have developers find this | ||||
| 		// first if a slog.Kind is is not handled. It is | ||||
| 		// preferable to have user's open issue asking why their attributes | ||||
| 		// have a "unhandled: " prefix than say that their code is panicking. | ||||
| 		return fmt.Sprintf("<unhandled telemetry.ValueKind: %s>", v.Kind()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // MarshalJSON encodes v into OTLP formatted JSON. | ||||
| func (v *Value) MarshalJSON() ([]byte, error) { | ||||
| 	switch v.Kind() { | ||||
| 	case ValueKindString: | ||||
| 		return json.Marshal(struct { | ||||
| 			Value string `json:"stringValue"` | ||||
| 		}{v.asString()}) | ||||
| 	case ValueKindInt64: | ||||
| 		return json.Marshal(struct { | ||||
| 			Value string `json:"intValue"` | ||||
| 		}{strconv.FormatInt(int64(v.num), 10)}) // nolint: gosec  // From raw bytes. | ||||
| 	case ValueKindFloat64: | ||||
| 		return json.Marshal(struct { | ||||
| 			Value float64 `json:"doubleValue"` | ||||
| 		}{v.asFloat64()}) | ||||
| 	case ValueKindBool: | ||||
| 		return json.Marshal(struct { | ||||
| 			Value bool `json:"boolValue"` | ||||
| 		}{v.asBool()}) | ||||
| 	case ValueKindBytes: | ||||
| 		return json.Marshal(struct { | ||||
| 			Value []byte `json:"bytesValue"` | ||||
| 		}{v.asBytes()}) | ||||
| 	case ValueKindMap: | ||||
| 		return json.Marshal(struct { | ||||
| 			Value struct { | ||||
| 				Values []Attr `json:"values"` | ||||
| 			} `json:"kvlistValue"` | ||||
| 		}{struct { | ||||
| 			Values []Attr `json:"values"` | ||||
| 		}{v.asMap()}}) | ||||
| 	case ValueKindSlice: | ||||
| 		return json.Marshal(struct { | ||||
| 			Value struct { | ||||
| 				Values []Value `json:"values"` | ||||
| 			} `json:"arrayValue"` | ||||
| 		}{struct { | ||||
| 			Values []Value `json:"values"` | ||||
| 		}{v.asSlice()}}) | ||||
| 	case ValueKindEmpty: | ||||
| 		return nil, nil | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("unknown Value kind: %s", v.Kind().String()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON decodes the OTLP formatted JSON contained in data into v. | ||||
| func (v *Value) UnmarshalJSON(data []byte) error { | ||||
| 	decoder := json.NewDecoder(bytes.NewReader(data)) | ||||
|  | ||||
| 	t, err := decoder.Token() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if t != json.Delim('{') { | ||||
| 		return errors.New("invalid Value type") | ||||
| 	} | ||||
|  | ||||
| 	for decoder.More() { | ||||
| 		keyIface, err := decoder.Token() | ||||
| 		if err != nil { | ||||
| 			if errors.Is(err, io.EOF) { | ||||
| 				// Empty. | ||||
| 				return nil | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		key, ok := keyIface.(string) | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("invalid Value key: %#v", keyIface) | ||||
| 		} | ||||
|  | ||||
| 		switch key { | ||||
| 		case "stringValue", "string_value": | ||||
| 			var val string | ||||
| 			err = decoder.Decode(&val) | ||||
| 			*v = StringValue(val) | ||||
| 		case "boolValue", "bool_value": | ||||
| 			var val bool | ||||
| 			err = decoder.Decode(&val) | ||||
| 			*v = BoolValue(val) | ||||
| 		case "intValue", "int_value": | ||||
| 			var val protoInt64 | ||||
| 			err = decoder.Decode(&val) | ||||
| 			*v = Int64Value(val.Int64()) | ||||
| 		case "doubleValue", "double_value": | ||||
| 			var val float64 | ||||
| 			err = decoder.Decode(&val) | ||||
| 			*v = Float64Value(val) | ||||
| 		case "bytesValue", "bytes_value": | ||||
| 			var val64 string | ||||
| 			if err := decoder.Decode(&val64); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			var val []byte | ||||
| 			val, err = base64.StdEncoding.DecodeString(val64) | ||||
| 			*v = BytesValue(val) | ||||
| 		case "arrayValue", "array_value": | ||||
| 			var val struct{ Values []Value } | ||||
| 			err = decoder.Decode(&val) | ||||
| 			*v = SliceValue(val.Values...) | ||||
| 		case "kvlistValue", "kvlist_value": | ||||
| 			var val struct{ Values []Attr } | ||||
| 			err = decoder.Decode(&val) | ||||
| 			*v = MapValue(val.Values...) | ||||
| 		default: | ||||
| 			// Skip unknown. | ||||
| 			continue | ||||
| 		} | ||||
| 		// Use first valid. Ignore the rest. | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Only unknown fields. Return nil without unmarshaling any value. | ||||
| 	return nil | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 anthonyrawlins
					anthonyrawlins