TUN-9467: bump coredns to solve CVE

* TUN-9467: bump coredns to solve CVE
This commit is contained in:
João Oliveirinha
2025-06-12 10:46:10 +00:00
committed by João "Pisco" Fernandes
parent f8d12c9d39
commit a408612f26
459 changed files with 30077 additions and 16165 deletions

27
vendor/go.opentelemetry.io/auto/sdk/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,27 @@
# Contributing to go.opentelemetry.io/auto/sdk
The `go.opentelemetry.io/auto/sdk` module is a purpose built OpenTelemetry SDK.
It is designed to be:
0. An OpenTelemetry compliant SDK
1. Instrumented by auto-instrumentation (serializable into OTLP JSON)
2. Lightweight
3. User-friendly
These design choices are listed in the order of their importance.
The primary design goal of this module is to be an OpenTelemetry SDK.
This means that it needs to implement the Go APIs found in `go.opentelemetry.io/otel`.
Having met the requirement of SDK compliance, this module needs to provide code that the `go.opentelemetry.io/auto` module can instrument.
The chosen approach to meet this goal is to ensure the telemetry from the SDK is serializable into JSON encoded OTLP.
This ensures then that the serialized form is compatible with other OpenTelemetry systems, and the auto-instrumentation can use these systems to deserialize any telemetry it is sent.
Outside of these first two goals, the intended use becomes relevant.
This package is intended to be used in the `go.opentelemetry.io/otel` global API as a default when the auto-instrumentation is running.
Because of this, this package needs to not add unnecessary dependencies to that API.
Ideally, it adds none.
It also needs to operate efficiently.
Finally, this module is designed to be user-friendly to Go development.
It hides complexity in order to provide simpler APIs when the previous goals can all still be met.

201
vendor/go.opentelemetry.io/auto/sdk/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

15
vendor/go.opentelemetry.io/auto/sdk/VERSIONING.md generated vendored Normal file
View File

@@ -0,0 +1,15 @@
# Versioning
This document describes the versioning policy for this module.
This policy is designed so the following goals can be achieved.
**Users are provided a codebase of value that is stable and secure.**
## Policy
* Versioning of this module will be idiomatic of a Go project using [Go modules](https://github.com/golang/go/wiki/Modules).
* [Semantic import versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) will be used.
* Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html).
* Any `v2` or higher version of this module will be included as a `/vN` at the end of the module path used in `go.mod` files and in the package import path.
* GitHub releases will be made for all releases.

14
vendor/go.opentelemetry.io/auto/sdk/doc.go generated vendored Normal file
View File

@@ -0,0 +1,14 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
/*
Package sdk provides an auto-instrumentable OpenTelemetry SDK.
An [go.opentelemetry.io/auto.Instrumentation] can be configured to target the
process running this SDK. In that case, all telemetry the SDK produces will be
processed and handled by that [go.opentelemetry.io/auto.Instrumentation].
By default, if there is no [go.opentelemetry.io/auto.Instrumentation] set to
auto-instrument the SDK, the SDK will not generate any telemetry.
*/
package sdk

View File

@@ -0,0 +1,58 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package 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 returns if a is equal to b.
func (a Attr) Equal(b Attr) bool {
return a.Key == b.Key && a.Value.Equal(b.Value)
}

View 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

View File

@@ -0,0 +1,103 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package 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 returns false if id contains at least one non-zero byte.
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 returns true if the span ID contains at least one non-zero byte.
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 []byte, 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
}

View File

@@ -0,0 +1,67 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package 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
}

View File

@@ -0,0 +1,66 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package 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
}

View File

@@ -0,0 +1,67 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package 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
}

View File

@@ -0,0 +1,456 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package telemetry
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"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),
EndTime: uint64(endT),
})
}
// 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)
s.StartTime = time.Unix(0, int64(val.Uint64()))
case "endTimeUnixNano", "end_time_unix_nano":
var val protoUint64
err = decoder.Decode(&val)
s.EndTime = time.Unix(0, int64(val.Uint64()))
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 (
// Bits 0-7 are used for trace flags.
SpanFlagsTraceFlagsMask SpanFlags = 255
// 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.
// Bit 9 (`IS_REMOTE`) indicates whether the span or link is remote.
SpanFlagsContextHasIsRemoteMask SpanFlags = 256
// SpanFlagsContextHasIsRemoteMask indicates the Span 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 (
// Indicates that the span represents an internal operation within an application,
// as opposed to an operation happening at the boundaries. Default value.
SpanKindInternal SpanKind = 1
// Indicates that the span covers server-side handling of an RPC or other
// remote network request.
SpanKindServer SpanKind = 2
// Indicates that the span describes a request to some remote service.
SpanKindClient SpanKind = 3
// Indicates that the span describes a producer sending a message to a broker.
// Unlike CLIENT and SERVER, there is often no direct critical path latency relationship
// between producer and consumer spans. A PRODUCER 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
// Indicates that the span describes consumer receiving a message from a broker.
// Like the PRODUCER kind, there is often no direct critical path latency relationship
// between producer and consumer spans.
SpanKindConsumer SpanKind = 5
)
// Event 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),
})
}
// 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)
se.Time = time.Unix(0, int64(val.Uint64()))
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
}
// A pointer 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
}

View File

@@ -0,0 +1,40 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package telemetry
// 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 (
// The default status.
StatusCodeUnset StatusCode = 0
// The Span has been validated by an Application developer or Operator to
// have completed successfully.
StatusCodeOK StatusCode = 1
// 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>"
}
// The Status type 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"`
}

View File

@@ -0,0 +1,189 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package 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
}
// 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
}
// 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
}

View File

@@ -0,0 +1,452 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
//go:generate stringer -type=ValueKind -trimprefix=ValueKind
package 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), 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 returns if v does not hold any value.
func (v Value) Empty() bool { return v.Kind() == ValueKindEmpty }
// Equal returns if 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 fmt.Sprint(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)})
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
}

94
vendor/go.opentelemetry.io/auto/sdk/limit.go generated vendored Normal file
View File

@@ -0,0 +1,94 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package sdk
import (
"log/slog"
"os"
"strconv"
)
// maxSpan are the span limits resolved during startup.
var maxSpan = newSpanLimits()
type spanLimits struct {
// Attrs is the number of allowed attributes for a span.
//
// This is resolved from the environment variable value for the
// OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT key if it exists. Otherwise, the
// environment variable value for OTEL_ATTRIBUTE_COUNT_LIMIT, or 128 if
// that is not set, is used.
Attrs int
// AttrValueLen is the maximum attribute value length allowed for a span.
//
// This is resolved from the environment variable value for the
// OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT key if it exists. Otherwise, the
// environment variable value for OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, or -1
// if that is not set, is used.
AttrValueLen int
// Events is the number of allowed events for a span.
//
// This is resolved from the environment variable value for the
// OTEL_SPAN_EVENT_COUNT_LIMIT key, or 128 is used if that is not set.
Events int
// EventAttrs is the number of allowed attributes for a span event.
//
// The is resolved from the environment variable value for the
// OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT key, or 128 is used if that is not set.
EventAttrs int
// Links is the number of allowed Links for a span.
//
// This is resolved from the environment variable value for the
// OTEL_SPAN_LINK_COUNT_LIMIT, or 128 is used if that is not set.
Links int
// LinkAttrs is the number of allowed attributes for a span link.
//
// This is resolved from the environment variable value for the
// OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, or 128 is used if that is not set.
LinkAttrs int
}
func newSpanLimits() spanLimits {
return spanLimits{
Attrs: firstEnv(
128,
"OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT",
"OTEL_ATTRIBUTE_COUNT_LIMIT",
),
AttrValueLen: firstEnv(
-1, // Unlimited.
"OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT",
"OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT",
),
Events: firstEnv(128, "OTEL_SPAN_EVENT_COUNT_LIMIT"),
EventAttrs: firstEnv(128, "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT"),
Links: firstEnv(128, "OTEL_SPAN_LINK_COUNT_LIMIT"),
LinkAttrs: firstEnv(128, "OTEL_LINK_ATTRIBUTE_COUNT_LIMIT"),
}
}
// firstEnv returns the parsed integer value of the first matching environment
// variable from keys. The defaultVal is returned if the value is not an
// integer or no match is found.
func firstEnv(defaultVal int, keys ...string) int {
for _, key := range keys {
strV := os.Getenv(key)
if strV == "" {
continue
}
v, err := strconv.Atoi(strV)
if err == nil {
return v
}
slog.Warn(
"invalid limit environment variable",
"error", err,
"key", key,
"value", strV,
)
}
return defaultVal
}

432
vendor/go.opentelemetry.io/auto/sdk/span.go generated vendored Normal file
View File

@@ -0,0 +1,432 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package sdk
import (
"encoding/json"
"fmt"
"reflect"
"runtime"
"strings"
"sync"
"sync/atomic"
"time"
"unicode/utf8"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/noop"
"go.opentelemetry.io/auto/sdk/internal/telemetry"
)
type span struct {
noop.Span
spanContext trace.SpanContext
sampled atomic.Bool
mu sync.Mutex
traces *telemetry.Traces
span *telemetry.Span
}
func (s *span) SpanContext() trace.SpanContext {
if s == nil {
return trace.SpanContext{}
}
// s.spanContext is immutable, do not acquire lock s.mu.
return s.spanContext
}
func (s *span) IsRecording() bool {
if s == nil {
return false
}
return s.sampled.Load()
}
func (s *span) SetStatus(c codes.Code, msg string) {
if s == nil || !s.sampled.Load() {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if s.span.Status == nil {
s.span.Status = new(telemetry.Status)
}
s.span.Status.Message = msg
switch c {
case codes.Unset:
s.span.Status.Code = telemetry.StatusCodeUnset
case codes.Error:
s.span.Status.Code = telemetry.StatusCodeError
case codes.Ok:
s.span.Status.Code = telemetry.StatusCodeOK
}
}
func (s *span) SetAttributes(attrs ...attribute.KeyValue) {
if s == nil || !s.sampled.Load() {
return
}
s.mu.Lock()
defer s.mu.Unlock()
limit := maxSpan.Attrs
if limit == 0 {
// No attributes allowed.
s.span.DroppedAttrs += uint32(len(attrs))
return
}
m := make(map[string]int)
for i, a := range s.span.Attrs {
m[a.Key] = i
}
for _, a := range attrs {
val := convAttrValue(a.Value)
if val.Empty() {
s.span.DroppedAttrs++
continue
}
if idx, ok := m[string(a.Key)]; ok {
s.span.Attrs[idx] = telemetry.Attr{
Key: string(a.Key),
Value: val,
}
} else if limit < 0 || len(s.span.Attrs) < limit {
s.span.Attrs = append(s.span.Attrs, telemetry.Attr{
Key: string(a.Key),
Value: val,
})
m[string(a.Key)] = len(s.span.Attrs) - 1
} else {
s.span.DroppedAttrs++
}
}
}
// convCappedAttrs converts up to limit attrs into a []telemetry.Attr. The
// number of dropped attributes is also returned.
func convCappedAttrs(limit int, attrs []attribute.KeyValue) ([]telemetry.Attr, uint32) {
if limit == 0 {
return nil, uint32(len(attrs))
}
if limit < 0 {
// Unlimited.
return convAttrs(attrs), 0
}
limit = min(len(attrs), limit)
return convAttrs(attrs[:limit]), uint32(len(attrs) - limit)
}
func convAttrs(attrs []attribute.KeyValue) []telemetry.Attr {
if len(attrs) == 0 {
// Avoid allocations if not necessary.
return nil
}
out := make([]telemetry.Attr, 0, len(attrs))
for _, attr := range attrs {
key := string(attr.Key)
val := convAttrValue(attr.Value)
if val.Empty() {
continue
}
out = append(out, telemetry.Attr{Key: key, Value: val})
}
return out
}
func convAttrValue(value attribute.Value) telemetry.Value {
switch value.Type() {
case attribute.BOOL:
return telemetry.BoolValue(value.AsBool())
case attribute.INT64:
return telemetry.Int64Value(value.AsInt64())
case attribute.FLOAT64:
return telemetry.Float64Value(value.AsFloat64())
case attribute.STRING:
v := truncate(maxSpan.AttrValueLen, value.AsString())
return telemetry.StringValue(v)
case attribute.BOOLSLICE:
slice := value.AsBoolSlice()
out := make([]telemetry.Value, 0, len(slice))
for _, v := range slice {
out = append(out, telemetry.BoolValue(v))
}
return telemetry.SliceValue(out...)
case attribute.INT64SLICE:
slice := value.AsInt64Slice()
out := make([]telemetry.Value, 0, len(slice))
for _, v := range slice {
out = append(out, telemetry.Int64Value(v))
}
return telemetry.SliceValue(out...)
case attribute.FLOAT64SLICE:
slice := value.AsFloat64Slice()
out := make([]telemetry.Value, 0, len(slice))
for _, v := range slice {
out = append(out, telemetry.Float64Value(v))
}
return telemetry.SliceValue(out...)
case attribute.STRINGSLICE:
slice := value.AsStringSlice()
out := make([]telemetry.Value, 0, len(slice))
for _, v := range slice {
v = truncate(maxSpan.AttrValueLen, v)
out = append(out, telemetry.StringValue(v))
}
return telemetry.SliceValue(out...)
}
return telemetry.Value{}
}
// truncate returns a truncated version of s such that it contains less than
// the limit number of characters. Truncation is applied by returning the limit
// number of valid characters contained in s.
//
// If limit is negative, it returns the original string.
//
// UTF-8 is supported. When truncating, all invalid characters are dropped
// before applying truncation.
//
// If s already contains less than the limit number of bytes, it is returned
// unchanged. No invalid characters are removed.
func truncate(limit int, s string) string {
// This prioritize performance in the following order based on the most
// common expected use-cases.
//
// - Short values less than the default limit (128).
// - Strings with valid encodings that exceed the limit.
// - No limit.
// - Strings with invalid encodings that exceed the limit.
if limit < 0 || len(s) <= limit {
return s
}
// Optimistically, assume all valid UTF-8.
var b strings.Builder
count := 0
for i, c := range s {
if c != utf8.RuneError {
count++
if count > limit {
return s[:i]
}
continue
}
_, size := utf8.DecodeRuneInString(s[i:])
if size == 1 {
// Invalid encoding.
b.Grow(len(s) - 1)
_, _ = b.WriteString(s[:i])
s = s[i:]
break
}
}
// Fast-path, no invalid input.
if b.Cap() == 0 {
return s
}
// Truncate while validating UTF-8.
for i := 0; i < len(s) && count < limit; {
c := s[i]
if c < utf8.RuneSelf {
// Optimization for single byte runes (common case).
_ = b.WriteByte(c)
i++
count++
continue
}
_, size := utf8.DecodeRuneInString(s[i:])
if size == 1 {
// We checked for all 1-byte runes above, this is a RuneError.
i++
continue
}
_, _ = b.WriteString(s[i : i+size])
i += size
count++
}
return b.String()
}
func (s *span) End(opts ...trace.SpanEndOption) {
if s == nil || !s.sampled.Swap(false) {
return
}
// s.end exists so the lock (s.mu) is not held while s.ended is called.
s.ended(s.end(opts))
}
func (s *span) end(opts []trace.SpanEndOption) []byte {
s.mu.Lock()
defer s.mu.Unlock()
cfg := trace.NewSpanEndConfig(opts...)
if t := cfg.Timestamp(); !t.IsZero() {
s.span.EndTime = cfg.Timestamp()
} else {
s.span.EndTime = time.Now()
}
b, _ := json.Marshal(s.traces) // TODO: do not ignore this error.
return b
}
// Expected to be implemented in eBPF.
//
//go:noinline
func (*span) ended(buf []byte) { ended(buf) }
// ended is used for testing.
var ended = func([]byte) {}
func (s *span) RecordError(err error, opts ...trace.EventOption) {
if s == nil || err == nil || !s.sampled.Load() {
return
}
cfg := trace.NewEventConfig(opts...)
attrs := cfg.Attributes()
attrs = append(attrs,
semconv.ExceptionType(typeStr(err)),
semconv.ExceptionMessage(err.Error()),
)
if cfg.StackTrace() {
buf := make([]byte, 2048)
n := runtime.Stack(buf, false)
attrs = append(attrs, semconv.ExceptionStacktrace(string(buf[0:n])))
}
s.mu.Lock()
defer s.mu.Unlock()
s.addEvent(semconv.ExceptionEventName, cfg.Timestamp(), attrs)
}
func typeStr(i any) string {
t := reflect.TypeOf(i)
if t.PkgPath() == "" && t.Name() == "" {
// Likely a builtin type.
return t.String()
}
return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
}
func (s *span) AddEvent(name string, opts ...trace.EventOption) {
if s == nil || !s.sampled.Load() {
return
}
cfg := trace.NewEventConfig(opts...)
s.mu.Lock()
defer s.mu.Unlock()
s.addEvent(name, cfg.Timestamp(), cfg.Attributes())
}
// addEvent adds an event with name and attrs at tStamp to the span. The span
// lock (s.mu) needs to be held by the caller.
func (s *span) addEvent(name string, tStamp time.Time, attrs []attribute.KeyValue) {
limit := maxSpan.Events
if limit == 0 {
s.span.DroppedEvents++
return
}
if limit > 0 && len(s.span.Events) == limit {
// Drop head while avoiding allocation of more capacity.
copy(s.span.Events[:limit-1], s.span.Events[1:])
s.span.Events = s.span.Events[:limit-1]
s.span.DroppedEvents++
}
e := &telemetry.SpanEvent{Time: tStamp, Name: name}
e.Attrs, e.DroppedAttrs = convCappedAttrs(maxSpan.EventAttrs, attrs)
s.span.Events = append(s.span.Events, e)
}
func (s *span) AddLink(link trace.Link) {
if s == nil || !s.sampled.Load() {
return
}
l := maxSpan.Links
s.mu.Lock()
defer s.mu.Unlock()
if l == 0 {
s.span.DroppedLinks++
return
}
if l > 0 && len(s.span.Links) == l {
// Drop head while avoiding allocation of more capacity.
copy(s.span.Links[:l-1], s.span.Links[1:])
s.span.Links = s.span.Links[:l-1]
s.span.DroppedLinks++
}
s.span.Links = append(s.span.Links, convLink(link))
}
func convLinks(links []trace.Link) []*telemetry.SpanLink {
out := make([]*telemetry.SpanLink, 0, len(links))
for _, link := range links {
out = append(out, convLink(link))
}
return out
}
func convLink(link trace.Link) *telemetry.SpanLink {
l := &telemetry.SpanLink{
TraceID: telemetry.TraceID(link.SpanContext.TraceID()),
SpanID: telemetry.SpanID(link.SpanContext.SpanID()),
TraceState: link.SpanContext.TraceState().String(),
Flags: uint32(link.SpanContext.TraceFlags()),
}
l.Attrs, l.DroppedAttrs = convCappedAttrs(maxSpan.LinkAttrs, link.Attributes)
return l
}
func (s *span) SetName(name string) {
if s == nil || !s.sampled.Load() {
return
}
s.mu.Lock()
defer s.mu.Unlock()
s.span.Name = name
}
func (*span) TracerProvider() trace.TracerProvider { return TracerProvider() }

124
vendor/go.opentelemetry.io/auto/sdk/tracer.go generated vendored Normal file
View File

@@ -0,0 +1,124 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package sdk
import (
"context"
"time"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/noop"
"go.opentelemetry.io/auto/sdk/internal/telemetry"
)
type tracer struct {
noop.Tracer
name, schemaURL, version string
}
var _ trace.Tracer = tracer{}
func (t tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
var psc trace.SpanContext
sampled := true
span := new(span)
// Ask eBPF for sampling decision and span context info.
t.start(ctx, span, &psc, &sampled, &span.spanContext)
span.sampled.Store(sampled)
ctx = trace.ContextWithSpan(ctx, span)
if sampled {
// Only build traces if sampled.
cfg := trace.NewSpanStartConfig(opts...)
span.traces, span.span = t.traces(name, cfg, span.spanContext, psc)
}
return ctx, span
}
// Expected to be implemented in eBPF.
//
//go:noinline
func (t *tracer) start(
ctx context.Context,
spanPtr *span,
psc *trace.SpanContext,
sampled *bool,
sc *trace.SpanContext,
) {
start(ctx, spanPtr, psc, sampled, sc)
}
// start is used for testing.
var start = func(context.Context, *span, *trace.SpanContext, *bool, *trace.SpanContext) {}
func (t tracer) traces(name string, cfg trace.SpanConfig, sc, psc trace.SpanContext) (*telemetry.Traces, *telemetry.Span) {
span := &telemetry.Span{
TraceID: telemetry.TraceID(sc.TraceID()),
SpanID: telemetry.SpanID(sc.SpanID()),
Flags: uint32(sc.TraceFlags()),
TraceState: sc.TraceState().String(),
ParentSpanID: telemetry.SpanID(psc.SpanID()),
Name: name,
Kind: spanKind(cfg.SpanKind()),
}
span.Attrs, span.DroppedAttrs = convCappedAttrs(maxSpan.Attrs, cfg.Attributes())
links := cfg.Links()
if limit := maxSpan.Links; limit == 0 {
span.DroppedLinks = uint32(len(links))
} else {
if limit > 0 {
n := max(len(links)-limit, 0)
span.DroppedLinks = uint32(n)
links = links[n:]
}
span.Links = convLinks(links)
}
if t := cfg.Timestamp(); !t.IsZero() {
span.StartTime = cfg.Timestamp()
} else {
span.StartTime = time.Now()
}
return &telemetry.Traces{
ResourceSpans: []*telemetry.ResourceSpans{
{
ScopeSpans: []*telemetry.ScopeSpans{
{
Scope: &telemetry.Scope{
Name: t.name,
Version: t.version,
},
Spans: []*telemetry.Span{span},
SchemaURL: t.schemaURL,
},
},
},
},
}, span
}
func spanKind(kind trace.SpanKind) telemetry.SpanKind {
switch kind {
case trace.SpanKindInternal:
return telemetry.SpanKindInternal
case trace.SpanKindServer:
return telemetry.SpanKindServer
case trace.SpanKindClient:
return telemetry.SpanKindClient
case trace.SpanKindProducer:
return telemetry.SpanKindProducer
case trace.SpanKindConsumer:
return telemetry.SpanKindConsumer
}
return telemetry.SpanKind(0) // undefined.
}

33
vendor/go.opentelemetry.io/auto/sdk/tracer_provider.go generated vendored Normal file
View File

@@ -0,0 +1,33 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package sdk
import (
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/noop"
)
// TracerProvider returns an auto-instrumentable [trace.TracerProvider].
//
// If an [go.opentelemetry.io/auto.Instrumentation] is configured to instrument
// the process using the returned TracerProvider, all of the telemetry it
// produces will be processed and handled by that Instrumentation. By default,
// if no Instrumentation instruments the TracerProvider it will not generate
// any trace telemetry.
func TracerProvider() trace.TracerProvider { return tracerProviderInstance }
var tracerProviderInstance = new(tracerProvider)
type tracerProvider struct{ noop.TracerProvider }
var _ trace.TracerProvider = tracerProvider{}
func (p tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer {
cfg := trace.NewTracerConfig(opts...)
return tracer{
name: name,
version: cfg.InstrumentationVersion(),
schemaURL: cfg.SchemaURL(),
}
}

View File

@@ -5,3 +5,5 @@ collison
consequentially
ans
nam
valu
thirdparty

View File

@@ -5,6 +5,6 @@ check-filenames =
check-hidden =
ignore-words = .codespellignore
interactive = 1
skip = .git,go.mod,go.sum,semconv,venv,.tools
skip = .git,go.mod,go.sum,go.work,go.work.sum,semconv,venv,.tools
uri-ignore-words-list = *
write =

View File

@@ -1,6 +1,7 @@
.DS_Store
Thumbs.db
.cache/
.tools/
venv/
.idea/
@@ -12,11 +13,3 @@ go.work
go.work.sum
gen/
/example/dice/dice
/example/namedtracer/namedtracer
/example/otel-collector/otel-collector
/example/opencensus/opencensus
/example/passthrough/passthrough
/example/prometheus/prometheus
/example/zipkin/zipkin

View File

@@ -1,3 +0,0 @@
[submodule "opentelemetry-proto"]
path = exporters/otlp/internal/opentelemetry-proto
url = https://github.com/open-telemetry/opentelemetry-proto

View File

@@ -9,8 +9,11 @@ linters:
disable-all: true
# Specifically enable linters we want to use.
enable:
- asasalint
- bodyclose
- depguard
- errcheck
- errorlint
- godot
- gofumpt
- goimports
@@ -19,10 +22,16 @@ linters:
- govet
- ineffassign
- misspell
- perfsprint
- revive
- staticcheck
- testifylint
- typecheck
- unconvert
- unused
- unparam
- usestdlibvars
- usetesting
issues:
# Maximum issues count per one linter.
@@ -54,16 +63,17 @@ issues:
text: "calls to (.+) only in main[(][)] or init[(][)] functions"
linters:
- revive
# It's okay to not run gosec in a test.
# It's okay to not run gosec and perfsprint in a test.
- path: _test\.go
linters:
- gosec
# Igonoring gosec G404: Use of weak random number generator (math/rand instead of crypto/rand)
- perfsprint
# Ignoring gosec G404: Use of weak random number generator (math/rand instead of crypto/rand)
# as we commonly use it in tests and examples.
- text: "G404:"
linters:
- gosec
# Igonoring gosec G402: TLS MinVersion too low
# Ignoring gosec G402: TLS MinVersion too low
# as the https://pkg.go.dev/crypto/tls#Config handles MinVersion default well.
- text: "G402: TLS MinVersion too low."
linters:
@@ -88,6 +98,13 @@ linters-settings:
- pkg: "crypto/md5"
- pkg: "crypto/sha1"
- pkg: "crypto/**/pkix"
auto/sdk:
files:
- "!internal/global/trace.go"
- "~internal/global/trace_test.go"
deny:
- pkg: "go.opentelemetry.io/auto/sdk"
desc: Do not use SDK from automatic instrumentation.
otlp-internal:
files:
- "!**/exporters/otlp/internal/**/*.go"
@@ -120,10 +137,10 @@ linters-settings:
- "**/metric/**/*.go"
- "**/bridge/*.go"
- "**/bridge/**/*.go"
- "**/example/*.go"
- "**/example/**/*.go"
- "**/trace/*.go"
- "**/trace/**/*.go"
- "**/log/*.go"
- "**/log/**/*.go"
deny:
- pkg: "go.opentelemetry.io/otel/internal$"
desc: Do not use cross-module internal packages.
@@ -147,137 +164,71 @@ linters-settings:
locale: US
ignore-words:
- cancelled
perfsprint:
err-error: true
errorf: true
int-conversion: true
sprintf1: true
strconcat: true
revive:
# Sets the default failure confidence.
# This means that linting errors with less than 0.8 confidence will be ignored.
# Default: 0.8
confidence: 0.01
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
rules:
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#blank-imports
- name: blank-imports
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr
- name: bool-literal-in-expr
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#constant-logical-expr
- name: constant-logical-expr
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument
# TODO (#3372) re-enable linter when it is compatible. https://github.com/golangci/golangci-lint/issues/3280
- name: context-as-argument
disabled: true
arguments:
allowTypesBefore: "*testing.T"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-keys-type
- allowTypesBefore: "*testing.T"
- name: context-keys-type
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#deep-exit
- name: deep-exit
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#defer
- name: defer
disabled: false
arguments:
- ["call-chain", "loop"]
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#dot-imports
- name: dot-imports
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#duplicated-imports
- name: duplicated-imports
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return
- name: early-return
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-block
arguments:
- "preserveScope"
- name: empty-block
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines
- name: empty-lines
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-naming
- name: error-naming
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-return
- name: error-return
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-strings
- name: error-strings
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#errorf
- name: errorf
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#exported
- name: exported
disabled: false
arguments:
- "sayRepetitiveInsteadOfStutters"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#flag-parameter
- name: flag-parameter
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#identical-branches
- name: identical-branches
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return
- name: if-return
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#increment-decrement
- name: increment-decrement
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#indent-error-flow
- name: indent-error-flow
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-shadowing
- name: import-shadowing
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#package-comments
- name: increment-decrement
- name: indent-error-flow
arguments:
- "preserveScope"
- name: package-comments
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range
- name: range
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-in-closure
- name: range-val-in-closure
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-address
- name: range-val-address
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redefines-builtin-id
- name: redefines-builtin-id
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-format
- name: string-format
disabled: false
arguments:
- - panic
- '/^[^\n]*$/'
- must not contain line breaks
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#struct-tag
- name: struct-tag
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#superfluous-else
- name: superfluous-else
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-equal
- name: time-equal
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-naming
- name: var-naming
disabled: false
arguments:
- ["ID"] # AllowList
- ["Otel", "Aws", "Gcp"] # DenyList
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-declaration
- name: var-declaration
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unconditional-recursion
- "preserveScope"
- name: time-equal
- name: unconditional-recursion
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-return
- name: unexported-return
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unhandled-error
- name: unhandled-error
disabled: false
arguments:
- "fmt.Fprint"
- "fmt.Fprintf"
@@ -285,12 +236,17 @@ linters-settings:
- "fmt.Print"
- "fmt.Printf"
- "fmt.Println"
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unnecessary-stmt
- name: unnecessary-stmt
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break
- name: useless-break
disabled: false
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#waitgroup-by-value
- name: var-declaration
- name: var-naming
arguments:
- ["ID"] # AllowList
- ["Otel", "Aws", "Gcp"] # DenyList
- name: waitgroup-by-value
disabled: false
testifylint:
enable-all: true
disable:
- float-compare
- go-require
- require-error

View File

@@ -8,6 +8,317 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Unreleased]
<!-- Released section -->
<!-- Don't change this section unless doing release -->
## [1.35.0/0.57.0/0.11.0] 2025-03-05
This release is the last to support [Go 1.22].
The next release will require at least [Go 1.23].
### Added
- Add `ValueFromAttribute` and `KeyValueFromAttribute` in `go.opentelemetry.io/otel/log`. (#6180)
- Add `EventName` and `SetEventName` to `Record` in `go.opentelemetry.io/otel/log`. (#6187)
- Add `EventName` to `RecordFactory` in `go.opentelemetry.io/otel/log/logtest`. (#6187)
- `AssertRecordEqual` in `go.opentelemetry.io/otel/log/logtest` checks `Record.EventName`. (#6187)
- Add `EventName` and `SetEventName` to `Record` in `go.opentelemetry.io/otel/sdk/log`. (#6193)
- Add `EventName` to `RecordFactory` in `go.opentelemetry.io/otel/sdk/log/logtest`. (#6193)
- Emit `Record.EventName` field in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#6211)
- Emit `Record.EventName` field in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#6211)
- Emit `Record.EventName` field in `go.opentelemetry.io/otel/exporters/stdout/stdoutlog` (#6210)
- The `go.opentelemetry.io/otel/semconv/v1.28.0` package.
The package contains semantic conventions from the `v1.28.0` version of the OpenTelemetry Semantic Conventions.
See the [migration documentation](./semconv/v1.28.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.27.0`(#6236)
- The `go.opentelemetry.io/otel/semconv/v1.30.0` package.
The package contains semantic conventions from the `v1.30.0` version of the OpenTelemetry Semantic Conventions.
See the [migration documentation](./semconv/v1.30.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.28.0`(#6240)
- Document the pitfalls of using `Resource` as a comparable type.
`Resource.Equal` and `Resource.Equivalent` should be used instead. (#6272)
- Support [Go 1.24]. (#6304)
- Add `FilterProcessor` and `EnabledParameters` in `go.opentelemetry.io/otel/sdk/log`.
It replaces `go.opentelemetry.io/otel/sdk/log/internal/x.FilterProcessor`.
Compared to previous version it additionally gives the possibility to filter by resource and instrumentation scope. (#6317)
### Changed
- Update `github.com/prometheus/common` to `v0.62.0`, which changes the `NameValidationScheme` to `NoEscaping`.
This allows metrics names to keep original delimiters (e.g. `.`), rather than replacing with underscores.
This is controlled by the `Content-Type` header, or can be reverted by setting `NameValidationScheme` to `LegacyValidation` in `github.com/prometheus/common/model`. (#6198)
### Fixes
- Eliminate goroutine leak for the processor returned by `NewSimpleSpanProcessor` in `go.opentelemetry.io/otel/sdk/trace` when `Shutdown` is called and the passed `ctx` is canceled and `SpanExporter.Shutdown` has not returned. (#6368)
- Eliminate goroutine leak for the processor returned by `NewBatchSpanProcessor` in `go.opentelemetry.io/otel/sdk/trace` when `ForceFlush` is called and the passed `ctx` is canceled and `SpanExporter.Export` has not returned. (#6369)
## [1.34.0/0.56.0/0.10.0] 2025-01-17
### Changed
- Remove the notices from `Logger` to make the whole Logs API user-facing in `go.opentelemetry.io/otel/log`. (#6167)
### Fixed
- Relax minimum Go version to 1.22.0 in various modules. (#6073)
- The `Type` name logged for the `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` client is corrected from `otlphttpgrpc` to `otlptracegrpc`. (#6143)
- The `Type` name logged for the `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlphttpgrpc` client is corrected from `otlphttphttp` to `otlptracehttp`. (#6143)
## [1.33.0/0.55.0/0.9.0/0.0.12] 2024-12-12
### Added
- Add `Reset` method to `SpanRecorder` in `go.opentelemetry.io/otel/sdk/trace/tracetest`. (#5994)
- Add `EnabledInstrument` interface in `go.opentelemetry.io/otel/sdk/metric/internal/x`.
This is an experimental interface that is implemented by synchronous instruments provided by `go.opentelemetry.io/otel/sdk/metric`.
Users can use it to avoid performing computationally expensive operations when recording measurements.
It does not fall within the scope of the OpenTelemetry Go versioning and stability [policy](./VERSIONING.md) and it may be changed in backwards incompatible ways or removed in feature releases. (#6016)
### Changed
- The default global API now supports full auto-instrumentation from the `go.opentelemetry.io/auto` package.
See that package for more information. (#5920)
- Propagate non-retryable error messages to client in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#5929)
- Propagate non-retryable error messages to client in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#5929)
- Propagate non-retryable error messages to client in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#5929)
- Performance improvements for attribute value `AsStringSlice`, `AsFloat64Slice`, `AsInt64Slice`, `AsBoolSlice`. (#6011)
- Change `EnabledParameters` to have a `Severity` field instead of a getter and setter in `go.opentelemetry.io/otel/log`. (#6009)
### Fixed
- Fix inconsistent request body closing in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#5954)
- Fix inconsistent request body closing in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#5954)
- Fix inconsistent request body closing in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#5954)
- Fix invalid exemplar keys in `go.opentelemetry.io/otel/exporters/prometheus`. (#5995)
- Fix attribute value truncation in `go.opentelemetry.io/otel/sdk/trace`. (#5997)
- Fix attribute value truncation in `go.opentelemetry.io/otel/sdk/log`. (#6032)
## [1.32.0/0.54.0/0.8.0/0.0.11] 2024-11-08
### Added
- Add `go.opentelemetry.io/otel/sdk/metric/exemplar.AlwaysOffFilter`, which can be used to disable exemplar recording. (#5850)
- Add `go.opentelemetry.io/otel/sdk/metric.WithExemplarFilter`, which can be used to configure the exemplar filter used by the metrics SDK. (#5850)
- Add `ExemplarReservoirProviderSelector` and `DefaultExemplarReservoirProviderSelector` to `go.opentelemetry.io/otel/sdk/metric`, which defines the exemplar reservoir to use based on the aggregation of the metric. (#5861)
- Add `ExemplarReservoirProviderSelector` to `go.opentelemetry.io/otel/sdk/metric.Stream` to allow using views to configure the exemplar reservoir to use for a metric. (#5861)
- Add `ReservoirProvider`, `HistogramReservoirProvider` and `FixedSizeReservoirProvider` to `go.opentelemetry.io/otel/sdk/metric/exemplar` to make it convenient to use providers of Reservoirs. (#5861)
- The `go.opentelemetry.io/otel/semconv/v1.27.0` package.
The package contains semantic conventions from the `v1.27.0` version of the OpenTelemetry Semantic Conventions. (#5894)
- Add `Attributes attribute.Set` field to `Scope` in `go.opentelemetry.io/otel/sdk/instrumentation`. (#5903)
- Add `Attributes attribute.Set` field to `ScopeRecords` in `go.opentelemetry.io/otel/log/logtest`. (#5927)
- `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` adds instrumentation scope attributes. (#5934)
- `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` adds instrumentation scope attributes. (#5934)
- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` adds instrumentation scope attributes. (#5935)
- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` adds instrumentation scope attributes. (#5935)
- `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc` adds instrumentation scope attributes. (#5933)
- `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` adds instrumentation scope attributes. (#5933)
- `go.opentelemetry.io/otel/exporters/prometheus` adds instrumentation scope attributes in `otel_scope_info` metric as labels. (#5932)
### Changed
- Support scope attributes and make them as identifying for `Tracer` in `go.opentelemetry.io/otel` and `go.opentelemetry.io/otel/sdk/trace`. (#5924)
- Support scope attributes and make them as identifying for `Meter` in `go.opentelemetry.io/otel` and `go.opentelemetry.io/otel/sdk/metric`. (#5926)
- Support scope attributes and make them as identifying for `Logger` in `go.opentelemetry.io/otel` and `go.opentelemetry.io/otel/sdk/log`. (#5925)
- Make schema URL and scope attributes as identifying for `Tracer` in `go.opentelemetry.io/otel/bridge/opentracing`. (#5931)
- Clear unneeded slice elements to allow GC to collect the objects in `go.opentelemetry.io/otel/sdk/metric` and `go.opentelemetry.io/otel/sdk/trace`. (#5804)
### Fixed
- Global MeterProvider registration unwraps global instrument Observers, the undocumented Unwrap() methods are now private. (#5881)
- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` now keeps the metadata already present in the context when `WithHeaders` is used. (#5892)
- `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc` now keeps the metadata already present in the context when `WithHeaders` is used. (#5911)
- `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` now keeps the metadata already present in the context when `WithHeaders` is used. (#5915)
- Fix `go.opentelemetry.io/otel/exporters/prometheus` trying to add exemplars to Gauge metrics, which is unsupported. (#5912)
- Fix `WithEndpointURL` to always use a secure connection when an https URL is passed in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#5944)
- Fix `WithEndpointURL` to always use a secure connection when an https URL is passed in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#5944)
- Fix `WithEndpointURL` to always use a secure connection when an https URL is passed in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#5944)
- Fix `WithEndpointURL` to always use a secure connection when an https URL is passed in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#5944)
- Fix incorrect metrics generated from callbacks when multiple readers are used in `go.opentelemetry.io/otel/sdk/metric`. (#5900)
### Removed
- Remove all examples under `go.opentelemetry.io/otel/example` as they are moved to [Contrib repository](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/examples). (#5930)
## [1.31.0/0.53.0/0.7.0/0.0.10] 2024-10-11
### Added
- Add `go.opentelemetry.io/otel/sdk/metric/exemplar` package which includes `Exemplar`, `Filter`, `TraceBasedFilter`, `AlwaysOnFilter`, `HistogramReservoir`, `FixedSizeReservoir`, `Reservoir`, `Value` and `ValueType` types. These will be used for configuring the exemplar reservoir for the metrics sdk. (#5747, #5862)
- Add `WithExportBufferSize` option to log batch processor.(#5877)
### Changed
- Enable exemplars by default in `go.opentelemetry.io/otel/sdk/metric`. Exemplars can be disabled by setting `OTEL_METRICS_EXEMPLAR_FILTER=always_off` (#5778)
- `Logger.Enabled` in `go.opentelemetry.io/otel/log` now accepts a newly introduced `EnabledParameters` type instead of `Record`. (#5791)
- `FilterProcessor.Enabled` in `go.opentelemetry.io/otel/sdk/log/internal/x` now accepts `EnabledParameters` instead of `Record`. (#5791)
- The `Record` type in `go.opentelemetry.io/otel/log` is no longer comparable. (#5847)
- Performance improvements for the trace SDK `SetAttributes` method in `Span`. (#5864)
- Reduce memory allocations for the `Event` and `Link` lists in `Span`. (#5858)
- Performance improvements for the trace SDK `AddEvent`, `AddLink`, `RecordError` and `End` methods in `Span`. (#5874)
### Deprecated
- Deprecate all examples under `go.opentelemetry.io/otel/example` as they are moved to [Contrib repository](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/examples). (#5854)
### Fixed
- The race condition for multiple `FixedSize` exemplar reservoirs identified in #5814 is resolved. (#5819)
- Fix log records duplication in case of heterogeneous resource attributes by correctly mapping each log record to it's resource and scope. (#5803)
- Fix timer channel drain to avoid hanging on Go 1.23. (#5868)
- Fix delegation for global meter providers, and panic when calling otel.SetMeterProvider. (#5827)
- Change the `reflect.TypeOf` to use a nil pointer to not allocate on the heap unless necessary. (#5827)
## [1.30.0/0.52.0/0.6.0/0.0.9] 2024-09-09
### Added
- Support `OTEL_EXPORTER_OTLP_LOGS_INSECURE` and `OTEL_EXPORTER_OTLP_INSECURE` environments in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#5739)
- The `WithResource` option for `NewMeterProvider` now merges the provided resources with the ones from environment variables. (#5773)
- The `WithResource` option for `NewLoggerProvider` now merges the provided resources with the ones from environment variables. (#5773)
- Add UTF-8 support to `go.opentelemetry.io/otel/exporters/prometheus`. (#5755)
### Fixed
- Fix memory leak in the global `MeterProvider` when identical instruments are repeatedly created. (#5754)
- Fix panic on instruments creation when setting meter provider. (#5758)
- Fix an issue where `SetMeterProvider` in `go.opentelemetry.io/otel` might miss the delegation for instruments and registries. (#5780)
### Removed
- Drop support for [Go 1.21]. (#5736, #5740, #5800)
## [1.29.0/0.51.0/0.5.0] 2024-08-23
This release is the last to support [Go 1.21].
The next release will require at least [Go 1.22].
### Added
- Add MacOS ARM64 platform to the compatibility testing suite. (#5577)
- Add `InstrumentationScope` field to `SpanStub` in `go.opentelemetry.io/otel/sdk/trace/tracetest`, as a replacement for the deprecated `InstrumentationLibrary`. (#5627)
- Make the initial release of `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`.
This new module contains an OTLP exporter that transmits log telemetry using gRPC.
This module is unstable and breaking changes may be introduced.
See our [versioning policy](VERSIONING.md) for more information about these stability guarantees. (#5629)
- Add `Walk` function to `TraceState` in `go.opentelemetry.io/otel/trace` to iterate all the key-value pairs. (#5651)
- Bridge the trace state in `go.opentelemetry.io/otel/bridge/opencensus`. (#5651)
- Zero value of `SimpleProcessor` in `go.opentelemetry.io/otel/sdk/log` no longer panics. (#5665)
- The `FilterProcessor` interface type is added in `go.opentelemetry.io/otel/sdk/log/internal/x`.
This is an optional and experimental interface that log `Processor`s can implement to instruct the `Logger` if a `Record` will be processed or not.
It replaces the existing `Enabled` method that is removed from the `Processor` interface itself.
It does not fall within the scope of the OpenTelemetry Go versioning and stability [policy](./VERSIONING.md) and it may be changed in backwards incompatible ways or removed in feature releases. (#5692)
- Support [Go 1.23]. (#5720)
### Changed
- `NewMemberRaw`, `NewKeyProperty` and `NewKeyValuePropertyRaw` in `go.opentelemetry.io/otel/baggage` allow UTF-8 string in key. (#5132)
- `Processor.OnEmit` in `go.opentelemetry.io/otel/sdk/log` now accepts a pointer to `Record` instead of a value so that the record modifications done in a processor are propagated to subsequent registered processors. (#5636)
- `SimpleProcessor.Enabled` in `go.opentelemetry.io/otel/sdk/log` now returns `false` if the exporter is `nil`. (#5665)
- Update the concurrency requirements of `Exporter` in `go.opentelemetry.io/otel/sdk/log`. (#5666)
- `SimpleProcessor` in `go.opentelemetry.io/otel/sdk/log` synchronizes `OnEmit` calls. (#5666)
- The `Processor` interface in `go.opentelemetry.io/otel/sdk/log` no longer includes the `Enabled` method.
See the `FilterProcessor` interface type added in `go.opentelemetry.io/otel/sdk/log/internal/x` to continue providing this functionality. (#5692)
- The `SimpleProcessor` type in `go.opentelemetry.io/otel/sdk/log` is no longer comparable. (#5693)
- The `BatchProcessor` type in `go.opentelemetry.io/otel/sdk/log` is no longer comparable. (#5693)
### Fixed
- Correct comments for the priority of the `WithEndpoint` and `WithEndpointURL` options and their corresponding environment variables in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#5584)
- Pass the underlying error rather than a generic retry-able failure in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`, `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` and `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#5541)
- Correct the `Tracer`, `Meter`, and `Logger` names used in `go.opentelemetry.io/otel/example/dice`. (#5612)
- Correct the `Tracer` names used in `go.opentelemetry.io/otel/example/namedtracer`. (#5612)
- Correct the `Tracer` name used in `go.opentelemetry.io/otel/example/opencensus`. (#5612)
- Correct the `Tracer` and `Meter` names used in `go.opentelemetry.io/otel/example/otel-collector`. (#5612)
- Correct the `Tracer` names used in `go.opentelemetry.io/otel/example/passthrough`. (#5612)
- Correct the `Meter` name used in `go.opentelemetry.io/otel/example/prometheus`. (#5612)
- Correct the `Tracer` names used in `go.opentelemetry.io/otel/example/zipkin`. (#5612)
- Correct comments for the priority of the `WithEndpoint` and `WithEndpointURL` options and their corresponding environment variables in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#5641)
- Correct comments for the priority of the `WithEndpoint` and `WithEndpointURL` options and their corresponding environment variables in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#5650)
- Stop percent encoding header environment variables in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` (#5705)
- Remove invalid environment variable header keys in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` (#5705)
### Removed
- The `Enabled` method of the `SimpleProcessor` in `go.opentelemetry.io/otel/sdk/log` is removed. (#5692)
- The `Enabled` method of the `BatchProcessor` in `go.opentelemetry.io/otel/sdk/log` is removed. (#5692)
## [1.28.0/0.50.0/0.4.0] 2024-07-02
### Added
- The `IsEmpty` method is added to the `Instrument` type in `go.opentelemetry.io/otel/sdk/metric`.
This method is used to check if an `Instrument` instance is a zero-value. (#5431)
- Store and provide the emitted `context.Context` in `ScopeRecords` of `go.opentelemetry.io/otel/sdk/log/logtest`. (#5468)
- The `go.opentelemetry.io/otel/semconv/v1.26.0` package.
The package contains semantic conventions from the `v1.26.0` version of the OpenTelemetry Semantic Conventions. (#5476)
- The `AssertRecordEqual` method to `go.opentelemetry.io/otel/log/logtest` to allow comparison of two log records in tests. (#5499)
- The `WithHeaders` option to `go.opentelemetry.io/otel/exporters/zipkin` to allow configuring custom http headers while exporting spans. (#5530)
### Changed
- `Tracer.Start` in `go.opentelemetry.io/otel/trace/noop` no longer allocates a span for empty span context. (#5457)
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/example/otel-collector`. (#5490)
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/example/zipkin`. (#5490)
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/exporters/zipkin`. (#5490)
- The exporter no longer exports the deprecated "otel.library.name" or "otel.library.version" attributes.
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/sdk/resource`. (#5490)
- Upgrade `go.opentelemetry.io/otel/semconv/v1.25.0` to `go.opentelemetry.io/otel/semconv/v1.26.0` in `go.opentelemetry.io/otel/sdk/trace`. (#5490)
- `SimpleProcessor.OnEmit` in `go.opentelemetry.io/otel/sdk/log` no longer allocates a slice which makes it possible to have a zero-allocation log processing using `SimpleProcessor`. (#5493)
- Use non-generic functions in the `Start` method of `"go.opentelemetry.io/otel/sdk/trace".Trace` to reduce memory allocation. (#5497)
- `service.instance.id` is populated for a `Resource` created with `"go.opentelemetry.io/otel/sdk/resource".Default` with a default value when `OTEL_GO_X_RESOURCE` is set. (#5520)
- Improve performance of metric instruments in `go.opentelemetry.io/otel/sdk/metric` by removing unnecessary calls to `time.Now`. (#5545)
### Fixed
- Log a warning to the OpenTelemetry internal logger when a `Record` in `go.opentelemetry.io/otel/sdk/log` drops an attribute due to a limit being reached. (#5376)
- Identify the `Tracer` returned from the global `TracerProvider` in `go.opentelemetry.io/otel/global` with its schema URL. (#5426)
- Identify the `Meter` returned from the global `MeterProvider` in `go.opentelemetry.io/otel/global` with its schema URL. (#5426)
- Log a warning to the OpenTelemetry internal logger when a `Span` in `go.opentelemetry.io/otel/sdk/trace` drops an attribute, event, or link due to a limit being reached. (#5434)
- Document instrument name requirements in `go.opentelemetry.io/otel/metric`. (#5435)
- Prevent random number generation data-race for experimental rand exemplars in `go.opentelemetry.io/otel/sdk/metric`. (#5456)
- Fix counting number of dropped attributes of `Record` in `go.opentelemetry.io/otel/sdk/log`. (#5464)
- Fix panic in baggage creation when a member contains `0x80` char in key or value. (#5494)
- Correct comments for the priority of the `WithEndpoint` and `WithEndpointURL` options and their corresponding environment variables in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#5508)
- Retry trace and span ID generation if it generated an invalid one in `go.opentelemetry.io/otel/sdk/trace`. (#5514)
- Fix stale timestamps reported by the last-value aggregation. (#5517)
- Indicate the `Exporter` in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` must be created by the `New` method. (#5521)
- Improved performance in all `{Bool,Int64,Float64,String}SliceValue` functions of `go.opentelemetry.io/attributes` by reducing the number of allocations. (#5549)
- Replace invalid percent-encoded octet sequences with replacement char in `go.opentelemetry.io/otel/baggage`. (#5528)
## [1.27.0/0.49.0/0.3.0] 2024-05-21
### Added
- Add example for `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`. (#5242)
- Add `RecordFactory` in `go.opentelemetry.io/otel/sdk/log/logtest` to facilitate testing exporter and processor implementations. (#5258)
- Add `RecordFactory` in `go.opentelemetry.io/otel/log/logtest` to facilitate testing bridge implementations. (#5263)
- The count of dropped records from the `BatchProcessor` in `go.opentelemetry.io/otel/sdk/log` is logged. (#5276)
- Add metrics in the `otel-collector` example. (#5283)
- Add the synchronous gauge instrument to `go.opentelemetry.io/otel/metric`. (#5304)
- An `int64` or `float64` synchronous gauge instrument can now be created from a `Meter`.
- All implementations of the API (`go.opentelemetry.io/otel/metric/noop`, `go.opentelemetry.io/otel/sdk/metric`) are updated to support this instrument.
- Add logs to `go.opentelemetry.io/otel/example/dice`. (#5349)
### Changed
- The `Shutdown` method of `Exporter` in `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` ignores the context cancellation and always returns `nil`. (#5189)
- The `ForceFlush` and `Shutdown` methods of the exporter returned by `New` in `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` ignore the context cancellation and always return `nil`. (#5189)
- Apply the value length limits to `Record` attributes in `go.opentelemetry.io/otel/sdk/log`. (#5230)
- De-duplicate map attributes added to a `Record` in `go.opentelemetry.io/otel/sdk/log`. (#5230)
- `go.opentelemetry.io/otel/exporters/stdout/stdoutlog` won't print timestamps when `WithoutTimestamps` option is set. (#5241)
- The `go.opentelemetry.io/otel/exporters/stdout/stdoutlog` exporter won't print `AttributeValueLengthLimit` and `AttributeCountLimit` fields now, instead it prints the `DroppedAttributes` field. (#5272)
- Improved performance in the `Stringer` implementation of `go.opentelemetry.io/otel/baggage.Member` by reducing the number of allocations. (#5286)
- Set the start time for last-value aggregates in `go.opentelemetry.io/otel/sdk/metric`. (#5305)
- The `Span` in `go.opentelemetry.io/otel/sdk/trace` will record links without span context if either non-empty `TraceState` or attributes are provided. (#5315)
- Upgrade all dependencies of `go.opentelemetry.io/otel/semconv/v1.24.0` to `go.opentelemetry.io/otel/semconv/v1.25.0`. (#5374)
### Fixed
- Comparison of unordered maps for `go.opentelemetry.io/otel/log.KeyValue` and `go.opentelemetry.io/otel/log.Value`. (#5306)
- Fix the empty output of `go.opentelemetry.io/otel/log.Value` in `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`. (#5311)
- Split the behavior of `Recorder` in `go.opentelemetry.io/otel/log/logtest` so it behaves as a `LoggerProvider` only. (#5365)
- Fix wrong package name of the error message when parsing endpoint URL in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#5371)
- Identify the `Logger` returned from the global `LoggerProvider` in `go.opentelemetry.io/otel/log/global` with its schema URL. (#5375)
## [1.26.0/0.48.0/0.2.0-alpha] 2024-04-24
### Added
@@ -33,6 +344,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Update `go.opentelemetry.io/proto/otlp` from v1.1.0 to v1.2.0. (#5177)
- Improve performance of baggage member character validation in `go.opentelemetry.io/otel/baggage`. (#5214)
- The `otel-collector` example now uses docker compose to bring up services instead of kubernetes. (#5244)
### Fixed
- Slice attribute values in `go.opentelemetry.io/otel/attribute` are now emitted as their JSON representation. (#5159)
## [1.25.0/0.47.0/0.0.8/0.1.0-alpha] 2024-04-05
@@ -93,7 +409,7 @@ The next release will require at least [Go 1.21].
This module includes OpenTelemetry Go's implementation of the Logs Bridge API.
This module is in an alpha state, it is subject to breaking changes.
See our [versioning policy](./VERSIONING.md) for more info. (#4961)
- ARM64 platform to the compatibility testing suite. (#4994)
- Add ARM64 platform to the compatibility testing suite. (#4994)
### Fixed
@@ -210,7 +526,7 @@ See our [versioning policy](VERSIONING.md) for more information about these stab
## [1.20.0/0.43.0] 2023-11-10
This release brings a breaking change for custom trace API implementations. Some interfaces (`TracerProvider`, `Tracer`, `Span`) now embed the `go.opentelemetry.io/otel/trace/embedded` types. Implementors need to update their implementations based on what they want the default behavior to be. See the "API Implementations" section of the [trace API] package documentation for more information about how to accomplish this.
This release brings a breaking change for custom trace API implementations. Some interfaces (`TracerProvider`, `Tracer`, `Span`) now embed the `go.opentelemetry.io/otel/trace/embedded` types. Implementers need to update their implementations based on what they want the default behavior to be. See the "API Implementations" section of the [trace API] package documentation for more information about how to accomplish this.
### Added
@@ -242,15 +558,15 @@ This release brings a breaking change for custom trace API implementations. Some
- `go.opentelemetry.io/otel/bridge/opencensus.NewMetricProducer` returns a `*MetricProducer` struct instead of the metric.Producer interface. (#4583)
- The `TracerProvider` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.TracerProvider` type.
This extends the `TracerProvider` interface and is is a breaking change for any existing implementation.
Implementors need to update their implementations based on what they want the default behavior of the interface to be.
Implementers need to update their implementations based on what they want the default behavior of the interface to be.
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more information about how to accomplish this. (#4620)
- The `Tracer` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.Tracer` type.
This extends the `Tracer` interface and is is a breaking change for any existing implementation.
Implementors need to update their implementations based on what they want the default behavior of the interface to be.
Implementers need to update their implementations based on what they want the default behavior of the interface to be.
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more information about how to accomplish this. (#4620)
- The `Span` in `go.opentelemetry.io/otel/trace` now embeds the `go.opentelemetry.io/otel/trace/embedded.Span` type.
This extends the `Span` interface and is is a breaking change for any existing implementation.
Implementors need to update their implementations based on what they want the default behavior of the interface to be.
Implementers need to update their implementations based on what they want the default behavior of the interface to be.
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more information about how to accomplish this. (#4620)
- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` does no longer depend on `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#4660)
- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` does no longer depend on `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#4660)
@@ -886,7 +1202,7 @@ The next release will require at least [Go 1.19].
- Exported `Status` codes in the `go.opentelemetry.io/otel/exporters/zipkin` exporter are now exported as all upper case values. (#3340)
- `Aggregation`s from `go.opentelemetry.io/otel/sdk/metric` with no data are not exported. (#3394, #3436)
- Re-enabled Attribute Filters in the Metric SDK. (#3396)
- Asynchronous callbacks are only called if they are registered with at least one instrument that does not use drop aggragation. (#3408)
- Asynchronous callbacks are only called if they are registered with at least one instrument that does not use drop aggregation. (#3408)
- Do not report empty partial-success responses in the `go.opentelemetry.io/otel/exporters/otlp` exporters. (#3438, #3432)
- Handle partial success responses in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` exporters. (#3162, #3440)
- Prevent duplicate Prometheus description, unit, and type. (#3469)
@@ -1754,7 +2070,7 @@ with major version 0.
- Setting error status while recording error with Span from oteltest package. (#1729)
- The concept of a remote and local Span stored in a context is unified to just the current Span.
Because of this `"go.opentelemetry.io/otel/trace".RemoteSpanContextFromContext` is removed as it is no longer needed.
Instead, `"go.opentelemetry.io/otel/trace".SpanContextFromContex` can be used to return the current Span.
Instead, `"go.opentelemetry.io/otel/trace".SpanContextFromContext` can be used to return the current Span.
If needed, that Span's `SpanContext.IsRemote()` can then be used to determine if it is remote or not. (#1731)
- The `HasRemoteParent` field of the `"go.opentelemetry.io/otel/sdk/trace".SamplingParameters` is removed.
This field is redundant to the information returned from the `Remote` method of the `SpanContext` held in the `ParentContext` field. (#1749)
@@ -1931,7 +2247,7 @@ with major version 0.
- `NewExporter` from `exporters/otlp` now takes a `ProtocolDriver` as a parameter. (#1369)
- Many OTLP Exporter options became gRPC ProtocolDriver options. (#1369)
- Unify endpoint API that related to OTel exporter. (#1401)
- Optimize metric histogram aggregator to re-use its slice of buckets. (#1435)
- Optimize metric histogram aggregator to reuse its slice of buckets. (#1435)
- Metric aggregator Count() and histogram Bucket.Counts are consistently `uint64`. (1430)
- Histogram aggregator accepts functional options, uses default boundaries if none given. (#1434)
- `SamplingResult` now passed a `Tracestate` from the parent `SpanContext` (#1432)
@@ -2328,7 +2644,7 @@ This release migrates the default OpenTelemetry SDK into its own Go module, deco
- Prometheus exporter will not apply stale updates or forget inactive metrics. (#903)
- Add test for api.standard `HTTPClientAttributesFromHTTPRequest`. (#905)
- Bump github.com/golangci/golangci-lint from 1.27.0 to 1.28.1 in /tools. (#901, #913)
- Update otel-colector example to use the v0.5.0 collector. (#915)
- Update otel-collector example to use the v0.5.0 collector. (#915)
- The `grpctrace` instrumentation uses a span name conforming to the OpenTelemetry semantic conventions (does not contain a leading slash (`/`)). (#922)
- The `grpctrace` instrumentation includes an `rpc.method` attribute now set to the gRPC method name. (#900, #922)
- The `grpctrace` instrumentation `rpc.service` attribute now contains the package name if one exists.
@@ -2921,7 +3237,16 @@ It contains api and sdk for trace and meter.
- CircleCI build CI manifest files.
- CODEOWNERS file to track owners of this project.
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.26.0...HEAD
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.35.0...HEAD
[1.35.0/0.57.0/0.11.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.35.0
[1.34.0/0.56.0/0.10.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.34.0
[1.33.0/0.55.0/0.9.0/0.0.12]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.33.0
[1.32.0/0.54.0/0.8.0/0.0.11]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.32.0
[1.31.0/0.53.0/0.7.0/0.0.10]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.31.0
[1.30.0/0.52.0/0.6.0/0.0.9]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.30.0
[1.29.0/0.51.0/0.5.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.29.0
[1.28.0/0.50.0/0.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.28.0
[1.27.0/0.49.0/0.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.27.0
[1.26.0/0.48.0/0.2.0-alpha]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.26.0
[1.25.0/0.47.0/0.0.8/0.1.0-alpha]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.25.0
[1.24.0/0.46.0/0.0.1-alpha]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.24.0
@@ -3002,6 +3327,10 @@ It contains api and sdk for trace and meter.
[0.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.1
[0.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.0
<!-- Released section ended -->
[Go 1.24]: https://go.dev/doc/go1.24
[Go 1.23]: https://go.dev/doc/go1.23
[Go 1.22]: https://go.dev/doc/go1.22
[Go 1.21]: https://go.dev/doc/go1.21
[Go 1.20]: https://go.dev/doc/go1.20

View File

@@ -5,13 +5,13 @@
#####################################################
#
# Learn about membership in OpenTelemetry community:
# https://github.com/open-telemetry/community/blob/main/community-membership.md
# https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md
#
#
# Learn about CODEOWNERS file format:
# https://help.github.com/en/articles/about-code-owners
#
* @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @MadVikingGod @pellared @hanyuancheung @dmathieu
* @MrAlias @XSAM @dashpole @pellared @dmathieu
CODEOWNERS @MrAlias @MadVikingGod @pellared @dashpole
CODEOWNERS @MrAlias @pellared @dashpole @XSAM @dmathieu

View File

@@ -181,6 +181,18 @@ patterns in the spec.
For a deeper discussion, see
[this](https://github.com/open-telemetry/opentelemetry-specification/issues/165).
## Tests
Each functionality should be covered by tests.
Performance-critical functionality should also be covered by benchmarks.
- Pull requests adding a performance-critical functionality
should have `go test -bench` output in their description.
- Pull requests changing a performance-critical functionality
should have [`benchstat`](https://pkg.go.dev/golang.org/x/perf/cmd/benchstat)
output in their description.
## Documentation
Each (non-internal, non-test) package must be documented using
@@ -570,12 +582,18 @@ functionality should be added, each one will need their own super-set
interfaces and will duplicate the pattern. For this reason, the simple targeted
interface that defines the specific functionality should be preferred.
See also:
[Keeping Your Modules Compatible: Working with interfaces](https://go.dev/blog/module-compatibility#working-with-interfaces).
### Testing
The tests should never leak goroutines.
Use the term `ConcurrentSafe` in the test name when it aims to verify the
absence of race conditions.
absence of race conditions. The top-level tests with this term will be run
many times in the `test-concurrent-safe` CI job to increase the chance of
catching concurrency issues. This does not apply to subtests when this term
is not in their root name.
### Internal packages
@@ -623,31 +641,34 @@ should be canceled.
## Approvers and Maintainers
### Approvers
### Triagers
- [Evan Torrie](https://github.com/evantorrie), Verizon Media
- [Sam Xie](https://github.com/XSAM), Cisco/AppDynamics
- [Chester Cheung](https://github.com/hanyuancheung), Tencent
- [Damien Mathieu](https://github.com/dmathieu), Elastic
- [Anthony Mirabella](https://github.com/Aneurysm9), AWS
- [Cheng-Zhen Yang](https://github.com/scorpionknifes), Independent
### Approvers
### Maintainers
- [Damien Mathieu](https://github.com/dmathieu), Elastic
- [David Ashpole](https://github.com/dashpole), Google
- [Aaron Clawson](https://github.com/MadVikingGod), LightStep
- [Robert Pająk](https://github.com/pellared), Splunk
- [Sam Xie](https://github.com/XSAM), Cisco/AppDynamics
- [Tyler Yahn](https://github.com/MrAlias), Splunk
### Emeritus
- [Liz Fong-Jones](https://github.com/lizthegrey), Honeycomb
- [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep
- [Josh MacDonald](https://github.com/jmacd), LightStep
- [Aaron Clawson](https://github.com/MadVikingGod)
- [Anthony Mirabella](https://github.com/Aneurysm9)
- [Chester Cheung](https://github.com/hanyuancheung)
- [Evan Torrie](https://github.com/evantorrie)
- [Gustavo Silva Paiva](https://github.com/paivagustavo)
- [Josh MacDonald](https://github.com/jmacd)
- [Liz Fong-Jones](https://github.com/lizthegrey)
### Become an Approver or a Maintainer
See the [community membership document in OpenTelemetry community
repo](https://github.com/open-telemetry/community/blob/main/community-membership.md).
repo](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md).
[Approver]: #approvers
[Maintainer]: #maintainers

View File

@@ -11,11 +11,15 @@ ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {}
GO = go
TIMEOUT = 60
# User to run as in docker images.
DOCKER_USER=$(shell id -u):$(shell id -g)
DEPENDENCIES_DOCKERFILE=./dependencies.Dockerfile
.DEFAULT_GOAL := precommit
.PHONY: precommit ci
precommit: generate dependabot-generate license-check misspell go-mod-tidy golangci-lint-fix verify-readmes test-default
ci: generate dependabot-check license-check lint vanity-import-check verify-readmes build test-default check-clean-work-tree test-coverage
precommit: generate toolchain-check license-check misspell go-mod-tidy golangci-lint-fix verify-readmes verify-mods test-default
ci: generate toolchain-check license-check lint vanity-import-check verify-readmes verify-mods build test-default check-clean-work-tree test-coverage
# Tools
@@ -39,9 +43,6 @@ $(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/build-tools/crosslink
SEMCONVKIT = $(TOOLS)/semconvkit
$(TOOLS)/semconvkit: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/semconvkit
DBOTCONF = $(TOOLS)/dbotconf
$(TOOLS)/dbotconf: PACKAGE=go.opentelemetry.io/build-tools/dbotconf
GOLANGCI_LINT = $(TOOLS)/golangci-lint
$(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint
@@ -57,9 +58,6 @@ $(TOOLS)/stringer: PACKAGE=golang.org/x/tools/cmd/stringer
PORTO = $(TOOLS)/porto
$(TOOLS)/porto: PACKAGE=github.com/jcchavezs/porto/cmd/porto
GOJQ = $(TOOLS)/gojq
$(TOOLS)/gojq: PACKAGE=github.com/itchyny/gojq/cmd/gojq
GOTMPL = $(TOOLS)/gotmpl
$(GOTMPL): PACKAGE=go.opentelemetry.io/build-tools/gotmpl
@@ -70,7 +68,7 @@ GOVULNCHECK = $(TOOLS)/govulncheck
$(TOOLS)/govulncheck: PACKAGE=golang.org/x/vuln/cmd/govulncheck
.PHONY: tools
tools: $(CROSSLINK) $(DBOTCONF) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(SEMCONVGEN) $(MULTIMOD) $(SEMCONVKIT) $(GOTMPL) $(GORELEASE)
tools: $(CROSSLINK) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(SEMCONVGEN) $(MULTIMOD) $(SEMCONVKIT) $(GOTMPL) $(GORELEASE)
# Virtualized python tools via docker
@@ -87,20 +85,20 @@ PIP := $(PYTOOLS)/pip
WORKDIR := /workdir
# The python image to use for the virtual environment.
PYTHONIMAGE := python:3.11.3-slim-bullseye
PYTHONIMAGE := $(shell awk '$$4=="python" {print $$2}' $(DEPENDENCIES_DOCKERFILE))
# Run the python image with the current directory mounted.
DOCKERPY := docker run --rm -v "$(CURDIR):$(WORKDIR)" -w $(WORKDIR) $(PYTHONIMAGE)
DOCKERPY := docker run --rm -u $(DOCKER_USER) -v "$(CURDIR):$(WORKDIR)" -w $(WORKDIR) $(PYTHONIMAGE)
# Create a virtual environment for Python tools.
$(PYTOOLS):
# The `--upgrade` flag is needed to ensure that the virtual environment is
# created with the latest pip version.
@$(DOCKERPY) bash -c "python3 -m venv $(VENVDIR) && $(PIP) install --upgrade pip"
@$(DOCKERPY) bash -c "python3 -m venv $(VENVDIR) && $(PIP) install --upgrade --cache-dir=$(WORKDIR)/.cache/pip pip"
# Install python packages into the virtual environment.
$(PYTOOLS)/%: $(PYTOOLS)
@$(DOCKERPY) $(PIP) install -r requirements.txt
@$(DOCKERPY) $(PIP) install --cache-dir=$(WORKDIR)/.cache/pip -r requirements.txt
CODESPELL = $(PYTOOLS)/codespell
$(CODESPELL): PACKAGE=codespell
@@ -125,7 +123,7 @@ vanity-import-fix: $(PORTO)
# Generate go.work file for local development.
.PHONY: go-work
go-work: $(CROSSLINK)
$(CROSSLINK) work --root=$(shell pwd)
$(CROSSLINK) work --root=$(shell pwd) --go=1.22.7
# Build
@@ -148,12 +146,14 @@ build-tests/%:
# Tests
TEST_TARGETS := test-default test-bench test-short test-verbose test-race
TEST_TARGETS := test-default test-bench test-short test-verbose test-race test-concurrent-safe
.PHONY: $(TEST_TARGETS) test
test-default test-race: ARGS=-race
test-bench: ARGS=-run=xxxxxMatchNothingxxxxx -test.benchtime=1ms -bench=.
test-short: ARGS=-short
test-verbose: ARGS=-v -race
test-concurrent-safe: ARGS=-run=ConcurrentSafe -count=100 -race
test-concurrent-safe: TIMEOUT=120
$(TEST_TARGETS): test
test: $(OTEL_GO_MOD_DIRS:%=test/%)
test/%: DIR=$*
@@ -181,17 +181,14 @@ test-coverage: $(GOCOVMERGE)
done; \
$(GOCOVMERGE) $$(find . -name coverage.out) > coverage.txt
# Adding a directory will include all benchmarks in that directory if a filter is not specified.
BENCHMARK_TARGETS := sdk/trace
.PHONY: benchmark
benchmark: $(BENCHMARK_TARGETS:%=benchmark/%)
BENCHMARK_FILTER = .
# You can override the filter for a particular directory by adding a rule here.
benchmark/sdk/trace: BENCHMARK_FILTER = SpanWithAttributes_8/AlwaysSample
benchmark: $(OTEL_GO_MOD_DIRS:%=benchmark/%)
benchmark/%:
@echo "$(GO) test -timeout $(TIMEOUT)s -run=xxxxxMatchNothingxxxxx -bench=$(BENCHMARK_FILTER) $*..." \
@echo "$(GO) test -run=xxxxxMatchNothingxxxxx -bench=. $*..." \
&& cd $* \
$(foreach filter, $(BENCHMARK_FILTER), && $(GO) test -timeout $(TIMEOUT)s -run=xxxxxMatchNothingxxxxx -bench=$(filter))
&& $(GO) list ./... \
| grep -v third_party \
| xargs $(GO) test -run=xxxxxMatchNothingxxxxx -bench=.
.PHONY: golangci-lint golangci-lint-fix
golangci-lint-fix: ARGS=--fix
@@ -242,6 +239,16 @@ govulncheck/%: $(GOVULNCHECK)
codespell: $(CODESPELL)
@$(DOCKERPY) $(CODESPELL)
.PHONY: toolchain-check
toolchain-check:
@toolchainRes=$$(for f in $(ALL_GO_MOD_DIRS); do \
awk '/^toolchain/ { found=1; next } END { if (found) print FILENAME }' $$f/go.mod; \
done); \
if [ -n "$${toolchainRes}" ]; then \
echo "toolchain checking failed:"; echo "$${toolchainRes}"; \
exit 1; \
fi
.PHONY: license-check
license-check:
@licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*' ! -path './.git/*' ) ; do \
@@ -252,15 +259,6 @@ license-check:
exit 1; \
fi
DEPENDABOT_CONFIG = .github/dependabot.yml
.PHONY: dependabot-check
dependabot-check: $(DBOTCONF)
@$(DBOTCONF) verify $(DEPENDABOT_CONFIG) || ( echo "(run: make dependabot-generate)"; exit 1 )
.PHONY: dependabot-generate
dependabot-generate: $(DBOTCONF)
@$(DBOTCONF) generate > $(DEPENDABOT_CONFIG)
.PHONY: check-clean-work-tree
check-clean-work-tree:
@if ! git diff --quiet; then \
@@ -271,16 +269,30 @@ check-clean-work-tree:
exit 1; \
fi
# The weaver docker image to use for semconv-generate.
WEAVER_IMAGE := $(shell awk '$$4=="weaver" {print $$2}' $(DEPENDENCIES_DOCKERFILE))
SEMCONVPKG ?= "semconv/"
.PHONY: semconv-generate
semconv-generate: $(SEMCONVGEN) $(SEMCONVKIT)
semconv-generate: $(SEMCONVKIT)
[ "$(TAG)" ] || ( echo "TAG unset: missing opentelemetry semantic-conventions tag"; exit 1 )
[ "$(OTEL_SEMCONV_REPO)" ] || ( echo "OTEL_SEMCONV_REPO unset: missing path to opentelemetry semantic-conventions repo"; exit 1 )
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=span -p conventionType=trace -f trace.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)"
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=attribute_group -p conventionType=trace -f attribute_group.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)"
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=event -p conventionType=event -f event.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)"
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=resource -p conventionType=resource -f resource.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)"
$(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=metric -f metric.go -t "$(SEMCONVPKG)/metric_template.j2" -s "$(TAG)"
# Ensure the target directory for source code is available.
mkdir -p $(PWD)/$(SEMCONVPKG)/${TAG}
# Note: We mount a home directory for downloading/storing the semconv repository.
# Weaver will automatically clean the cache when finished, but the directories will remain.
mkdir -p ~/.weaver
docker run --rm \
-u $(DOCKER_USER) \
--env HOME=/tmp/weaver \
--mount 'type=bind,source=$(PWD)/semconv,target=/home/weaver/templates/registry/go,readonly' \
--mount 'type=bind,source=$(PWD)/semconv/${TAG},target=/home/weaver/target' \
--mount 'type=bind,source=$(HOME)/.weaver,target=/tmp/weaver/.weaver' \
$(WEAVER_IMAGE) registry generate \
--registry=https://github.com/open-telemetry/semantic-conventions/archive/refs/tags/$(TAG).zip[model] \
--templates=/home/weaver/templates \
--param tag=$(TAG) \
go \
/home/weaver/target
$(SEMCONVKIT) -output "$(SEMCONVPKG)/$(TAG)" -tag "$(TAG)"
.PHONY: gorelease
@@ -292,16 +304,20 @@ gorelease/%:| $(GORELEASE)
&& $(GORELEASE) \
|| echo ""
.PHONY: verify-mods
verify-mods: $(MULTIMOD)
$(MULTIMOD) verify
.PHONY: prerelease
prerelease: $(MULTIMOD)
prerelease: verify-mods
@[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 )
$(MULTIMOD) verify && $(MULTIMOD) prerelease -m ${MODSET}
$(MULTIMOD) prerelease -m ${MODSET}
COMMIT ?= "HEAD"
.PHONY: add-tags
add-tags: $(MULTIMOD)
add-tags: verify-mods
@[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 )
$(MULTIMOD) verify && $(MULTIMOD) tag -m ${MODSET} -c ${COMMIT}
$(MULTIMOD) tag -m ${MODSET} -c ${COMMIT}
.PHONY: lint-markdown
lint-markdown:

View File

@@ -1,9 +1,11 @@
# OpenTelemetry-Go
[![CI](https://github.com/open-telemetry/opentelemetry-go/workflows/ci/badge.svg)](https://github.com/open-telemetry/opentelemetry-go/actions?query=workflow%3Aci+branch%3Amain)
[![ci](https://github.com/open-telemetry/opentelemetry-go/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/open-telemetry/opentelemetry-go/actions/workflows/ci.yml)
[![codecov.io](https://codecov.io/gh/open-telemetry/opentelemetry-go/coverage.svg?branch=main)](https://app.codecov.io/gh/open-telemetry/opentelemetry-go?branch=main)
[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel)](https://pkg.go.dev/go.opentelemetry.io/otel)
[![Go Report Card](https://goreportcard.com/badge/go.opentelemetry.io/otel)](https://goreportcard.com/report/go.opentelemetry.io/otel)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/open-telemetry/opentelemetry-go/badge)](https://scorecard.dev/viewer/?uri=github.com/open-telemetry/opentelemetry-go)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9996/badge)](https://www.bestpractices.dev/projects/9996)
[![Slack](https://img.shields.io/badge/slack-@cncf/otel--go-brightgreen.svg?logo=slack)](https://cloud-native.slack.com/archives/C01NPAXACKT)
OpenTelemetry-Go is the [Go](https://golang.org/) implementation of [OpenTelemetry](https://opentelemetry.io/).
@@ -15,7 +17,7 @@ It provides a set of APIs to directly measure performance and behavior of your s
|---------|--------------------|
| Traces | Stable |
| Metrics | Stable |
| Logs | In development[^1] |
| Logs | Beta[^1] |
Progress and status specific to this repository is tracked in our
[project boards](https://github.com/open-telemetry/opentelemetry-go/projects)
@@ -47,20 +49,29 @@ stop ensuring compatibility with these versions in the following manner:
Currently, this project supports the following environments.
| OS | Go Version | Architecture |
|---------|------------|--------------|
| Ubuntu | 1.22 | amd64 |
| Ubuntu | 1.21 | amd64 |
| Ubuntu | 1.22 | 386 |
| Ubuntu | 1.21 | 386 |
| Linux | 1.22 | arm64 |
| Linux | 1.21 | arm64 |
| MacOS | 1.22 | amd64 |
| MacOS | 1.21 | amd64 |
| Windows | 1.22 | amd64 |
| Windows | 1.21 | amd64 |
| Windows | 1.22 | 386 |
| Windows | 1.21 | 386 |
| OS | Go Version | Architecture |
|----------|------------|--------------|
| Ubuntu | 1.24 | amd64 |
| Ubuntu | 1.23 | amd64 |
| Ubuntu | 1.22 | amd64 |
| Ubuntu | 1.24 | 386 |
| Ubuntu | 1.23 | 386 |
| Ubuntu | 1.22 | 386 |
| Ubuntu | 1.24 | arm64 |
| Ubuntu | 1.23 | arm64 |
| Ubuntu | 1.22 | arm64 |
| macOS 13 | 1.24 | amd64 |
| macOS 13 | 1.23 | amd64 |
| macOS 13 | 1.22 | amd64 |
| macOS | 1.24 | arm64 |
| macOS | 1.23 | arm64 |
| macOS | 1.22 | arm64 |
| Windows | 1.24 | amd64 |
| Windows | 1.23 | amd64 |
| Windows | 1.22 | amd64 |
| Windows | 1.24 | 386 |
| Windows | 1.23 | 386 |
| Windows | 1.22 | 386 |
While this project should work for other systems, no compatibility guarantees
are made for those systems currently.
@@ -87,8 +98,8 @@ If you need to extend the telemetry an instrumentation library provides or want
to build your own instrumentation for your application directly you will need
to use the
[Go otel](https://pkg.go.dev/go.opentelemetry.io/otel)
package. The included [examples](./example/) are a good way to see some
practical uses of this process.
package. The [examples](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/examples)
are a good way to see some practical uses of this process.
### Export
@@ -97,12 +108,12 @@ export pipeline to send that telemetry to an observability platform.
All officially supported exporters for the OpenTelemetry project are contained in the [exporters directory](./exporters).
| Exporter | Metrics | Traces |
|---------------------------------------|:-------:|:------:|
| [OTLP](./exporters/otlp/) | ✓ | ✓ |
| [Prometheus](./exporters/prometheus/) | ✓ | |
| [stdout](./exporters/stdout/) | ✓ | ✓ |
| [Zipkin](./exporters/zipkin/) | | ✓ |
| Exporter | Logs | Metrics | Traces |
|---------------------------------------|:----:|:-------:|:------:|
| [OTLP](./exporters/otlp/) | ✓ | ✓ | ✓ |
| [Prometheus](./exporters/prometheus/) | | ✓ | |
| [stdout](./exporters/stdout/) | ✓ | ✓ | ✓ |
| [Zipkin](./exporters/zipkin/) | | | ✓ |
## Contributing

View File

@@ -5,17 +5,14 @@
New versions of the [OpenTelemetry Semantic Conventions] mean new versions of the `semconv` package need to be generated.
The `semconv-generate` make target is used for this.
1. Checkout a local copy of the [OpenTelemetry Semantic Conventions] to the desired release tag.
2. Pull the latest `otel/semconvgen` image: `docker pull otel/semconvgen:latest`
3. Run the `make semconv-generate ...` target from this repository.
1. Set the `TAG` environment variable to the semantic convention tag you want to generate.
2. Run the `make semconv-generate ...` target from this repository.
For example,
```sh
export TAG="v1.21.0" # Change to the release version you are generating.
export OTEL_SEMCONV_REPO="/absolute/path/to/opentelemetry/semantic-conventions"
docker pull otel/semconvgen:latest
make semconv-generate # Uses the exported TAG and OTEL_SEMCONV_REPO.
export TAG="v1.30.0" # Change to the release version you are generating.
make semconv-generate # Uses the exported TAG.
```
This should create a new sub-package of [`semconv`](./semconv).
@@ -27,6 +24,12 @@ You can run `make gorelease` that runs [gorelease](https://pkg.go.dev/golang.org
You can check/report problems with `gorelease` [here](https://golang.org/issues/26420).
## Verify changes for contrib repository
If the changes in the main repository are going to affect the contrib repository, it is important to verify that the changes are compatible with the contrib repository.
Follow [the steps](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/RELEASING.md#verify-otel-changes) in the contrib repository to verify OTel changes.
## Pre-Release
First, decide which module sets will be released and update their versions
@@ -63,6 +66,7 @@ Update go.mod for submodules to depend on the new release which will happen in t
```
- Move all the `Unreleased` changes into a new section following the title scheme (`[<new tag>] - <date of release>`).
- Make sure the new section is under the comment for released section, like `<!-- Released section -->`, so it is protected from being overwritten in the future.
- Update all the appropriate links at the bottom.
4. Push the changes to upstream and create a Pull Request on GitHub.
@@ -104,17 +108,6 @@ It is critical you make sure the version you push upstream is correct.
Finally create a Release for the new `<new tag>` on GitHub.
The release body should include all the release notes from the Changelog for this release.
## Verify Examples
After releasing verify that examples build outside of the repository.
```
./verify_examples.sh
```
The script copies examples into a different directory removes any `replace` declarations in `go.mod` and builds them.
This ensures they build with the published release, not the local copy.
## Post-Release
### Contrib Repository
@@ -134,6 +127,6 @@ Importantly, bump any package versions referenced to be the latest one you just
Bump the dependencies in the following Go services:
- [`accountingservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/accountingservice)
- [`checkoutservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/checkoutservice)
- [`productcatalogservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/productcatalogservice)
- [`accounting`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/accounting)
- [`checkoutservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/checkout)
- [`productcatalogservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/product-catalog)

View File

@@ -26,7 +26,7 @@ is designed so the following goals can be achieved.
go.opentelemetry.io/otel/v2 v2.0.1`) and in the package import path
(e.g., `import "go.opentelemetry.io/otel/v2/trace"`). This includes the
paths used in `go get` commands (e.g., `go get
go.opentelemetry.io/otel/v2@v2.0.1`. Note there is both a `/v2` and a
go.opentelemetry.io/otel/v2@v2.0.1`). Note there is both a `/v2` and a
`@v2.0.1` in that example. One way to think about it is that the module
name now includes the `/v2`, so include `/v2` whenever you are using the
module name).

View File

@@ -347,45 +347,25 @@ func computeDistinct(kvs []KeyValue) Distinct {
func computeDistinctFixed(kvs []KeyValue) interface{} {
switch len(kvs) {
case 1:
ptr := new([1]KeyValue)
copy((*ptr)[:], kvs)
return *ptr
return [1]KeyValue(kvs)
case 2:
ptr := new([2]KeyValue)
copy((*ptr)[:], kvs)
return *ptr
return [2]KeyValue(kvs)
case 3:
ptr := new([3]KeyValue)
copy((*ptr)[:], kvs)
return *ptr
return [3]KeyValue(kvs)
case 4:
ptr := new([4]KeyValue)
copy((*ptr)[:], kvs)
return *ptr
return [4]KeyValue(kvs)
case 5:
ptr := new([5]KeyValue)
copy((*ptr)[:], kvs)
return *ptr
return [5]KeyValue(kvs)
case 6:
ptr := new([6]KeyValue)
copy((*ptr)[:], kvs)
return *ptr
return [6]KeyValue(kvs)
case 7:
ptr := new([7]KeyValue)
copy((*ptr)[:], kvs)
return *ptr
return [7]KeyValue(kvs)
case 8:
ptr := new([8]KeyValue)
copy((*ptr)[:], kvs)
return *ptr
return [8]KeyValue(kvs)
case 9:
ptr := new([9]KeyValue)
copy((*ptr)[:], kvs)
return *ptr
return [9]KeyValue(kvs)
case 10:
ptr := new([10]KeyValue)
copy((*ptr)[:], kvs)
return *ptr
return [10]KeyValue(kvs)
default:
return nil
}

View File

@@ -231,15 +231,27 @@ func (v Value) Emit() string {
case BOOL:
return strconv.FormatBool(v.AsBool())
case INT64SLICE:
return fmt.Sprint(v.asInt64Slice())
j, err := json.Marshal(v.asInt64Slice())
if err != nil {
return fmt.Sprintf("invalid: %v", v.asInt64Slice())
}
return string(j)
case INT64:
return strconv.FormatInt(v.AsInt64(), 10)
case FLOAT64SLICE:
return fmt.Sprint(v.asFloat64Slice())
j, err := json.Marshal(v.asFloat64Slice())
if err != nil {
return fmt.Sprintf("invalid: %v", v.asFloat64Slice())
}
return string(j)
case FLOAT64:
return fmt.Sprint(v.AsFloat64())
case STRINGSLICE:
return fmt.Sprint(v.asStringSlice())
j, err := json.Marshal(v.asStringSlice())
if err != nil {
return fmt.Sprintf("invalid: %v", v.asStringSlice())
}
return string(j)
case STRING:
return v.stringly
default:

View File

@@ -44,9 +44,15 @@ type Property struct {
// NewKeyProperty returns a new Property for key.
//
// The passed key must be valid, non-empty UTF-8 string.
// If key is invalid, an error will be returned.
// However, the specific Propagators that are used to transmit baggage entries across
// component boundaries may impose their own restrictions on Property key.
// For example, the W3C Baggage specification restricts the Property keys to strings that
// satisfy the token definition from RFC7230, Section 3.2.6.
// For maximum compatibility, alphanumeric value are strongly recommended to be used as Property key.
func NewKeyProperty(key string) (Property, error) {
if !validateKey(key) {
if !validateBaggageName(key) {
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
}
@@ -62,6 +68,10 @@ func NewKeyProperty(key string) (Property, error) {
// Notice: Consider using [NewKeyValuePropertyRaw] instead
// that does not require percent-encoding of the value.
func NewKeyValueProperty(key, value string) (Property, error) {
if !validateKey(key) {
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
}
if !validateValue(value) {
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
}
@@ -74,11 +84,20 @@ func NewKeyValueProperty(key, value string) (Property, error) {
// NewKeyValuePropertyRaw returns a new Property for key with value.
//
// The passed key must be compliant with W3C Baggage specification.
// The passed key must be valid, non-empty UTF-8 string.
// The passed value must be valid UTF-8 string.
// However, the specific Propagators that are used to transmit baggage entries across
// component boundaries may impose their own restrictions on Property key.
// For example, the W3C Baggage specification restricts the Property keys to strings that
// satisfy the token definition from RFC7230, Section 3.2.6.
// For maximum compatibility, alphanumeric value are strongly recommended to be used as Property key.
func NewKeyValuePropertyRaw(key, value string) (Property, error) {
if !validateKey(key) {
if !validateBaggageName(key) {
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
}
if !validateBaggageValue(value) {
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
}
p := Property{
key: key,
@@ -115,12 +134,15 @@ func (p Property) validate() error {
return fmt.Errorf("invalid property: %w", err)
}
if !validateKey(p.key) {
if !validateBaggageName(p.key) {
return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key))
}
if !p.hasValue && p.value != "" {
return errFunc(errors.New("inconsistent value"))
}
if p.hasValue && !validateBaggageValue(p.value) {
return errFunc(fmt.Errorf("%w: %q", errInvalidValue, p.value))
}
return nil
}
@@ -138,7 +160,15 @@ func (p Property) Value() (string, bool) {
// String encodes Property into a header string compliant with the W3C Baggage
// specification.
// It would return empty string if the key is invalid with the W3C Baggage
// specification. This could happen for a UTF-8 key, as it may contain
// invalid characters.
func (p Property) String() string {
// W3C Baggage specification does not allow percent-encoded keys.
if !validateKey(p.key) {
return ""
}
if p.hasValue {
return fmt.Sprintf("%s%s%v", p.key, keyValueDelimiter, valueEscape(p.value))
}
@@ -203,9 +233,14 @@ func (p properties) validate() error {
// String encodes properties into a header string compliant with the W3C Baggage
// specification.
func (p properties) String() string {
props := make([]string, len(p))
for i, prop := range p {
props[i] = prop.String()
props := make([]string, 0, len(p))
for _, prop := range p {
s := prop.String()
// Ignored empty properties.
if s != "" {
props = append(props, s)
}
}
return strings.Join(props, propertyDelimiter)
}
@@ -230,6 +265,10 @@ type Member struct {
// Notice: Consider using [NewMemberRaw] instead
// that does not require percent-encoding of the value.
func NewMember(key, value string, props ...Property) (Member, error) {
if !validateKey(key) {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
}
if !validateValue(value) {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
}
@@ -242,7 +281,13 @@ func NewMember(key, value string, props ...Property) (Member, error) {
// NewMemberRaw returns a new Member from the passed arguments.
//
// The passed key must be compliant with W3C Baggage specification.
// The passed key must be valid, non-empty UTF-8 string.
// The passed value must be valid UTF-8 string.
// However, the specific Propagators that are used to transmit baggage entries across
// component boundaries may impose their own restrictions on baggage key.
// For example, the W3C Baggage specification restricts the baggage keys to strings that
// satisfy the token definition from RFC7230, Section 3.2.6.
// For maximum compatibility, alphanumeric value are strongly recommended to be used as baggage key.
func NewMemberRaw(key, value string, props ...Property) (Member, error) {
m := Member{
key: key,
@@ -294,19 +339,45 @@ func parseMember(member string) (Member, error) {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
}
val := strings.TrimSpace(v)
if !validateValue(val) {
rawVal := strings.TrimSpace(v)
if !validateValue(rawVal) {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, v)
}
// Decode a percent-encoded value.
value, err := url.PathUnescape(val)
unescapeVal, err := url.PathUnescape(rawVal)
if err != nil {
return newInvalidMember(), fmt.Errorf("%w: %v", errInvalidValue, err)
return newInvalidMember(), fmt.Errorf("%w: %w", errInvalidValue, err)
}
value := replaceInvalidUTF8Sequences(len(rawVal), unescapeVal)
return Member{key: key, value: value, properties: props, hasData: true}, nil
}
// replaceInvalidUTF8Sequences replaces invalid UTF-8 sequences with '<27>'.
func replaceInvalidUTF8Sequences(c int, unescapeVal string) string {
if utf8.ValidString(unescapeVal) {
return unescapeVal
}
// W3C baggage spec:
// https://github.com/w3c/baggage/blob/8c215efbeebd3fa4b1aceb937a747e56444f22f3/baggage/HTTP_HEADER_FORMAT.md?plain=1#L69
var b strings.Builder
b.Grow(c)
for i := 0; i < len(unescapeVal); {
r, size := utf8.DecodeRuneInString(unescapeVal[i:])
if r == utf8.RuneError && size == 1 {
// Invalid UTF-8 sequence found, replace it with '<27>'
_, _ = b.WriteString("<22>")
} else {
_, _ = b.WriteRune(r)
}
i += size
}
return b.String()
}
// validate ensures m conforms to the W3C Baggage specification.
// A key must be an ASCII string, returning an error otherwise.
func (m Member) validate() error {
@@ -314,9 +385,12 @@ func (m Member) validate() error {
return fmt.Errorf("%w: %q", errInvalidMember, m)
}
if !validateKey(m.key) {
if !validateBaggageName(m.key) {
return fmt.Errorf("%w: %q", errInvalidKey, m.key)
}
if !validateBaggageValue(m.value) {
return fmt.Errorf("%w: %q", errInvalidValue, m.value)
}
return m.properties.validate()
}
@@ -331,13 +405,18 @@ func (m Member) Properties() []Property { return m.properties.Copy() }
// String encodes Member into a header string compliant with the W3C Baggage
// specification.
// It would return empty string if the key is invalid with the W3C Baggage
// specification. This could happen for a UTF-8 key, as it may contain
// invalid characters.
func (m Member) String() string {
// A key is just an ASCII string. A value is restricted to be
// US-ASCII characters excluding CTLs, whitespace,
// DQUOTE, comma, semicolon, and backslash.
s := fmt.Sprintf("%s%s%s", m.key, keyValueDelimiter, valueEscape(m.value))
// W3C Baggage specification does not allow percent-encoded keys.
if !validateKey(m.key) {
return ""
}
s := m.key + keyValueDelimiter + valueEscape(m.value)
if len(m.properties) > 0 {
s = fmt.Sprintf("%s%s%s", s, propertyDelimiter, m.properties.String())
s += propertyDelimiter + m.properties.String()
}
return s
}
@@ -448,7 +527,7 @@ func (b Baggage) Member(key string) Member {
}
// Members returns all the baggage list-members.
// The order of the returned list-members does not have significance.
// The order of the returned list-members is not significant.
//
// The returned members are not validated, as we assume the validation happened
// when they were added to the Baggage.
@@ -469,8 +548,8 @@ func (b Baggage) Members() []Member {
return members
}
// SetMember returns a copy the Baggage with the member included. If the
// baggage contains a Member with the same key the existing Member is
// SetMember returns a copy of the Baggage with the member included. If the
// baggage contains a Member with the same key, the existing Member is
// replaced.
//
// If member is invalid according to the W3C Baggage specification, an error
@@ -528,14 +607,22 @@ func (b Baggage) Len() int {
// String encodes Baggage into a header string compliant with the W3C Baggage
// specification.
// It would ignore members where the member key is invalid with the W3C Baggage
// specification. This could happen for a UTF-8 key, as it may contain
// invalid characters.
func (b Baggage) String() string {
members := make([]string, 0, len(b.list))
for k, v := range b.list {
members = append(members, Member{
s := Member{
key: k,
value: v.Value,
properties: fromInternalProperties(v.Properties),
}.String())
}.String()
// Ignored empty members.
if s != "" {
members = append(members, s)
}
}
return strings.Join(members, listDelimiter)
}
@@ -607,10 +694,12 @@ func parsePropertyInternal(s string) (p Property, ok bool) {
}
// Decode a percent-encoded value.
value, err := url.PathUnescape(s[valueStart:valueEnd])
rawVal := s[valueStart:valueEnd]
unescapeVal, err := url.PathUnescape(rawVal)
if err != nil {
return
}
value := replaceInvalidUTF8Sequences(len(rawVal), unescapeVal)
ok = true
p.key = s[keyStart:keyEnd]
@@ -720,6 +809,24 @@ var safeKeyCharset = [utf8.RuneSelf]bool{
'~': true,
}
// validateBaggageName checks if the string is a valid OpenTelemetry Baggage name.
// Baggage name is a valid, non-empty UTF-8 string.
func validateBaggageName(s string) bool {
if len(s) == 0 {
return false
}
return utf8.ValidString(s)
}
// validateBaggageValue checks if the string is a valid OpenTelemetry Baggage value.
// Baggage value is a valid UTF-8 strings.
// Empty string is also a valid UTF-8 string.
func validateBaggageValue(s string) bool {
return utf8.ValidString(s)
}
// validateKey checks if the string is a valid W3C Baggage key.
func validateKey(s string) bool {
if len(s) == 0 {
return false
@@ -735,9 +842,10 @@ func validateKey(s string) bool {
}
func validateKeyChar(c int32) bool {
return c >= 0 && c <= int32(utf8.RuneSelf) && safeKeyCharset[c]
return c >= 0 && c < int32(utf8.RuneSelf) && safeKeyCharset[c]
}
// validateValue checks if the string is a valid W3C Baggage value.
func validateValue(s string) bool {
for _, c := range s {
if !validateValueChar(c) {
@@ -850,7 +958,7 @@ var safeValueCharset = [utf8.RuneSelf]bool{
}
func validateValueChar(c int32) bool {
return c >= 0 && c <= int32(utf8.RuneSelf) && safeValueCharset[c]
return c >= 0 && c < int32(utf8.RuneSelf) && safeValueCharset[c]
}
// valueEscape escapes the string so it can be safely placed inside a baggage value,

View File

@@ -5,6 +5,7 @@ package codes // import "go.opentelemetry.io/otel/codes"
import (
"encoding/json"
"errors"
"fmt"
"strconv"
)
@@ -63,7 +64,7 @@ func (c *Code) UnmarshalJSON(b []byte) error {
return nil
}
if c == nil {
return fmt.Errorf("nil receiver passed to UnmarshalJSON")
return errors.New("nil receiver passed to UnmarshalJSON")
}
var x interface{}
@@ -83,7 +84,7 @@ func (c *Code) UnmarshalJSON(b []byte) error {
return fmt.Errorf("invalid code: %q", ci)
}
*c = Code(ci)
*c = Code(ci) // nolint: gosec // Bit size of 32 check above.
return nil
}
return fmt.Errorf("invalid code: %q", string(b))

View File

@@ -0,0 +1,3 @@
# This is a renovate-friendly source of Docker images.
FROM python:3.13.2-slim-bullseye@sha256:31b581c8218e1f3c58672481b3b7dba8e898852866b408c6a984c22832523935 AS python
FROM otel/weaver:v0.13.2@sha256:ae7346b992e477f629ea327e0979e8a416a97f7956ab1f7e95ac1f44edf1a893 AS weaver

View File

@@ -17,6 +17,8 @@ To read more about tracing, see go.opentelemetry.io/otel/trace.
To read more about metrics, see go.opentelemetry.io/otel/metric.
To read more about logs, see go.opentelemetry.io/otel/log.
To read more about propagation, see go.opentelemetry.io/otel/propagation and
go.opentelemetry.io/otel/baggage.
*/

View File

@@ -14,33 +14,33 @@ import (
// BoolSliceValue converts a bool slice into an array with same elements as slice.
func BoolSliceValue(v []bool) interface{} {
var zero bool
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero)))
copy(cp.Elem().Slice(0, len(v)).Interface().([]bool), v)
return cp.Elem().Interface()
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem()
reflect.Copy(cp, reflect.ValueOf(v))
return cp.Interface()
}
// Int64SliceValue converts an int64 slice into an array with same elements as slice.
func Int64SliceValue(v []int64) interface{} {
var zero int64
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero)))
copy(cp.Elem().Slice(0, len(v)).Interface().([]int64), v)
return cp.Elem().Interface()
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem()
reflect.Copy(cp, reflect.ValueOf(v))
return cp.Interface()
}
// Float64SliceValue converts a float64 slice into an array with same elements as slice.
func Float64SliceValue(v []float64) interface{} {
var zero float64
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero)))
copy(cp.Elem().Slice(0, len(v)).Interface().([]float64), v)
return cp.Elem().Interface()
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem()
reflect.Copy(cp, reflect.ValueOf(v))
return cp.Interface()
}
// StringSliceValue converts a string slice into an array with same elements as slice.
func StringSliceValue(v []string) interface{} {
var zero string
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero)))
copy(cp.Elem().Slice(0, len(v)).Interface().([]string), v)
return cp.Elem().Interface()
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem()
reflect.Copy(cp, reflect.ValueOf(v))
return cp.Interface()
}
// AsBoolSlice converts a bool array into a slice into with same elements as array.
@@ -49,12 +49,11 @@ func AsBoolSlice(v interface{}) []bool {
if rv.Type().Kind() != reflect.Array {
return nil
}
var zero bool
correctLen := rv.Len()
correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero))
cpy := reflect.New(correctType)
_ = reflect.Copy(cpy.Elem(), rv)
return cpy.Elem().Slice(0, correctLen).Interface().([]bool)
cpy := make([]bool, rv.Len())
if len(cpy) > 0 {
_ = reflect.Copy(reflect.ValueOf(cpy), rv)
}
return cpy
}
// AsInt64Slice converts an int64 array into a slice into with same elements as array.
@@ -63,12 +62,11 @@ func AsInt64Slice(v interface{}) []int64 {
if rv.Type().Kind() != reflect.Array {
return nil
}
var zero int64
correctLen := rv.Len()
correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero))
cpy := reflect.New(correctType)
_ = reflect.Copy(cpy.Elem(), rv)
return cpy.Elem().Slice(0, correctLen).Interface().([]int64)
cpy := make([]int64, rv.Len())
if len(cpy) > 0 {
_ = reflect.Copy(reflect.ValueOf(cpy), rv)
}
return cpy
}
// AsFloat64Slice converts a float64 array into a slice into with same elements as array.
@@ -77,12 +75,11 @@ func AsFloat64Slice(v interface{}) []float64 {
if rv.Type().Kind() != reflect.Array {
return nil
}
var zero float64
correctLen := rv.Len()
correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero))
cpy := reflect.New(correctType)
_ = reflect.Copy(cpy.Elem(), rv)
return cpy.Elem().Slice(0, correctLen).Interface().([]float64)
cpy := make([]float64, rv.Len())
if len(cpy) > 0 {
_ = reflect.Copy(reflect.ValueOf(cpy), rv)
}
return cpy
}
// AsStringSlice converts a string array into a slice into with same elements as array.
@@ -91,10 +88,9 @@ func AsStringSlice(v interface{}) []string {
if rv.Type().Kind() != reflect.Array {
return nil
}
var zero string
correctLen := rv.Len()
correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero))
cpy := reflect.New(correctType)
_ = reflect.Copy(cpy.Elem(), rv)
return cpy.Elem().Slice(0, correctLen).Interface().([]string)
cpy := make([]string, rv.Len())
if len(cpy) > 0 {
_ = reflect.Copy(reflect.ValueOf(cpy), rv)
}
return cpy
}

View File

@@ -13,7 +13,7 @@ import (
// unwrapper unwraps to return the underlying instrument implementation.
type unwrapper interface {
Unwrap() metric.Observable
unwrap() metric.Observable
}
type afCounter struct {
@@ -40,7 +40,7 @@ func (i *afCounter) setDelegate(m metric.Meter) {
i.delegate.Store(ctr)
}
func (i *afCounter) Unwrap() metric.Observable {
func (i *afCounter) unwrap() metric.Observable {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(metric.Float64ObservableCounter)
}
@@ -71,7 +71,7 @@ func (i *afUpDownCounter) setDelegate(m metric.Meter) {
i.delegate.Store(ctr)
}
func (i *afUpDownCounter) Unwrap() metric.Observable {
func (i *afUpDownCounter) unwrap() metric.Observable {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(metric.Float64ObservableUpDownCounter)
}
@@ -102,7 +102,7 @@ func (i *afGauge) setDelegate(m metric.Meter) {
i.delegate.Store(ctr)
}
func (i *afGauge) Unwrap() metric.Observable {
func (i *afGauge) unwrap() metric.Observable {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(metric.Float64ObservableGauge)
}
@@ -133,7 +133,7 @@ func (i *aiCounter) setDelegate(m metric.Meter) {
i.delegate.Store(ctr)
}
func (i *aiCounter) Unwrap() metric.Observable {
func (i *aiCounter) unwrap() metric.Observable {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(metric.Int64ObservableCounter)
}
@@ -164,7 +164,7 @@ func (i *aiUpDownCounter) setDelegate(m metric.Meter) {
i.delegate.Store(ctr)
}
func (i *aiUpDownCounter) Unwrap() metric.Observable {
func (i *aiUpDownCounter) unwrap() metric.Observable {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(metric.Int64ObservableUpDownCounter)
}
@@ -195,7 +195,7 @@ func (i *aiGauge) setDelegate(m metric.Meter) {
i.delegate.Store(ctr)
}
func (i *aiGauge) Unwrap() metric.Observable {
func (i *aiGauge) unwrap() metric.Observable {
if ctr := i.delegate.Load(); ctr != nil {
return ctr.(metric.Int64ObservableGauge)
}
@@ -281,6 +281,32 @@ func (i *sfHistogram) Record(ctx context.Context, x float64, opts ...metric.Reco
}
}
type sfGauge struct {
embedded.Float64Gauge
name string
opts []metric.Float64GaugeOption
delegate atomic.Value // metric.Float64Gauge
}
var _ metric.Float64Gauge = (*sfGauge)(nil)
func (i *sfGauge) setDelegate(m metric.Meter) {
ctr, err := m.Float64Gauge(i.name, i.opts...)
if err != nil {
GetErrorHandler().Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *sfGauge) Record(ctx context.Context, x float64, opts ...metric.RecordOption) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(metric.Float64Gauge).Record(ctx, x, opts...)
}
}
type siCounter struct {
embedded.Int64Counter
@@ -358,3 +384,29 @@ func (i *siHistogram) Record(ctx context.Context, x int64, opts ...metric.Record
ctr.(metric.Int64Histogram).Record(ctx, x, opts...)
}
}
type siGauge struct {
embedded.Int64Gauge
name string
opts []metric.Int64GaugeOption
delegate atomic.Value // metric.Int64Gauge
}
var _ metric.Int64Gauge = (*siGauge)(nil)
func (i *siGauge) setDelegate(m metric.Meter) {
ctr, err := m.Int64Gauge(i.name, i.opts...)
if err != nil {
GetErrorHandler().Handle(err)
return
}
i.delegate.Store(ctr)
}
func (i *siGauge) Record(ctx context.Context, x int64, opts ...metric.RecordOption) {
if ctr := i.delegate.Load(); ctr != nil {
ctr.(metric.Int64Gauge).Record(ctx, x, opts...)
}
}

View File

@@ -5,8 +5,9 @@ package global // import "go.opentelemetry.io/otel/internal/global"
import (
"container/list"
"context"
"reflect"
"sync"
"sync/atomic"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/embedded"
@@ -65,6 +66,8 @@ func (p *meterProvider) Meter(name string, opts ...metric.MeterOption) metric.Me
key := il{
name: name,
version: c.InstrumentationVersion(),
schema: c.SchemaURL(),
attrs: c.InstrumentationAttributes(),
}
if p.meters == nil {
@@ -75,7 +78,7 @@ func (p *meterProvider) Meter(name string, opts ...metric.MeterOption) metric.Me
return val
}
t := &meter{name: name, opts: opts}
t := &meter{name: name, opts: opts, instruments: make(map[instID]delegatedInstrument)}
p.meters[key] = t
return t
}
@@ -91,17 +94,29 @@ type meter struct {
opts []metric.MeterOption
mtx sync.Mutex
instruments []delegatedInstrument
instruments map[instID]delegatedInstrument
registry list.List
delegate atomic.Value // metric.Meter
delegate metric.Meter
}
type delegatedInstrument interface {
setDelegate(metric.Meter)
}
// instID are the identifying properties of a instrument.
type instID struct {
// name is the name of the stream.
name string
// description is the description of the stream.
description string
// kind defines the functional group of the instrument.
kind reflect.Type
// unit is the unit of the stream.
unit string
}
// setDelegate configures m to delegate all Meter functionality to Meters
// created by provider.
//
@@ -109,12 +124,12 @@ type delegatedInstrument interface {
//
// It is guaranteed by the caller that this happens only once.
func (m *meter) setDelegate(provider metric.MeterProvider) {
meter := provider.Meter(m.name, m.opts...)
m.delegate.Store(meter)
m.mtx.Lock()
defer m.mtx.Unlock()
meter := provider.Meter(m.name, m.opts...)
m.delegate = meter
for _, inst := range m.instruments {
inst.setDelegate(meter)
}
@@ -132,147 +147,336 @@ func (m *meter) setDelegate(provider metric.MeterProvider) {
}
func (m *meter) Int64Counter(name string, options ...metric.Int64CounterOption) (metric.Int64Counter, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Int64Counter(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Int64Counter(name, options...)
}
cfg := metric.NewInt64CounterConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*siCounter)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Int64Counter), nil
}
i := &siCounter{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
func (m *meter) Int64UpDownCounter(name string, options ...metric.Int64UpDownCounterOption) (metric.Int64UpDownCounter, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Int64UpDownCounter(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Int64UpDownCounter(name, options...)
}
cfg := metric.NewInt64UpDownCounterConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*siUpDownCounter)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Int64UpDownCounter), nil
}
i := &siUpDownCounter{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
func (m *meter) Int64Histogram(name string, options ...metric.Int64HistogramOption) (metric.Int64Histogram, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Int64Histogram(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Int64Histogram(name, options...)
}
cfg := metric.NewInt64HistogramConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*siHistogram)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Int64Histogram), nil
}
i := &siHistogram{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
func (m *meter) Int64Gauge(name string, options ...metric.Int64GaugeOption) (metric.Int64Gauge, error) {
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Int64Gauge(name, options...)
}
cfg := metric.NewInt64GaugeConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*siGauge)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Int64Gauge), nil
}
i := &siGauge{name: name, opts: options}
m.instruments[id] = i
return i, nil
}
func (m *meter) Int64ObservableCounter(name string, options ...metric.Int64ObservableCounterOption) (metric.Int64ObservableCounter, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Int64ObservableCounter(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Int64ObservableCounter(name, options...)
}
cfg := metric.NewInt64ObservableCounterConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*aiCounter)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Int64ObservableCounter), nil
}
i := &aiCounter{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
func (m *meter) Int64ObservableUpDownCounter(name string, options ...metric.Int64ObservableUpDownCounterOption) (metric.Int64ObservableUpDownCounter, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Int64ObservableUpDownCounter(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Int64ObservableUpDownCounter(name, options...)
}
cfg := metric.NewInt64ObservableUpDownCounterConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*aiUpDownCounter)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Int64ObservableUpDownCounter), nil
}
i := &aiUpDownCounter{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
func (m *meter) Int64ObservableGauge(name string, options ...metric.Int64ObservableGaugeOption) (metric.Int64ObservableGauge, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Int64ObservableGauge(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Int64ObservableGauge(name, options...)
}
cfg := metric.NewInt64ObservableGaugeConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*aiGauge)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Int64ObservableGauge), nil
}
i := &aiGauge{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
func (m *meter) Float64Counter(name string, options ...metric.Float64CounterOption) (metric.Float64Counter, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Float64Counter(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Float64Counter(name, options...)
}
cfg := metric.NewFloat64CounterConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*sfCounter)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Float64Counter), nil
}
i := &sfCounter{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
func (m *meter) Float64UpDownCounter(name string, options ...metric.Float64UpDownCounterOption) (metric.Float64UpDownCounter, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Float64UpDownCounter(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Float64UpDownCounter(name, options...)
}
cfg := metric.NewFloat64UpDownCounterConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*sfUpDownCounter)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Float64UpDownCounter), nil
}
i := &sfUpDownCounter{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
func (m *meter) Float64Histogram(name string, options ...metric.Float64HistogramOption) (metric.Float64Histogram, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Float64Histogram(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Float64Histogram(name, options...)
}
cfg := metric.NewFloat64HistogramConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*sfHistogram)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Float64Histogram), nil
}
i := &sfHistogram{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
func (m *meter) Float64Gauge(name string, options ...metric.Float64GaugeOption) (metric.Float64Gauge, error) {
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Float64Gauge(name, options...)
}
cfg := metric.NewFloat64GaugeConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*sfGauge)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Float64Gauge), nil
}
i := &sfGauge{name: name, opts: options}
m.instruments[id] = i
return i, nil
}
func (m *meter) Float64ObservableCounter(name string, options ...metric.Float64ObservableCounterOption) (metric.Float64ObservableCounter, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Float64ObservableCounter(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Float64ObservableCounter(name, options...)
}
cfg := metric.NewFloat64ObservableCounterConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*afCounter)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Float64ObservableCounter), nil
}
i := &afCounter{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
func (m *meter) Float64ObservableUpDownCounter(name string, options ...metric.Float64ObservableUpDownCounterOption) (metric.Float64ObservableUpDownCounter, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Float64ObservableUpDownCounter(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Float64ObservableUpDownCounter(name, options...)
}
cfg := metric.NewFloat64ObservableUpDownCounterConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*afUpDownCounter)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Float64ObservableUpDownCounter), nil
}
i := &afUpDownCounter{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
func (m *meter) Float64ObservableGauge(name string, options ...metric.Float64ObservableGaugeOption) (metric.Float64ObservableGauge, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
return del.Float64ObservableGauge(name, options...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.Float64ObservableGauge(name, options...)
}
cfg := metric.NewFloat64ObservableGaugeConfig(options...)
id := instID{
name: name,
kind: reflect.TypeOf((*afGauge)(nil)),
description: cfg.Description(),
unit: cfg.Unit(),
}
if f, ok := m.instruments[id]; ok {
return f.(metric.Float64ObservableGauge), nil
}
i := &afGauge{name: name, opts: options}
m.instruments = append(m.instruments, i)
m.instruments[id] = i
return i, nil
}
// RegisterCallback captures the function that will be called during Collect.
func (m *meter) RegisterCallback(f metric.Callback, insts ...metric.Observable) (metric.Registration, error) {
if del, ok := m.delegate.Load().(metric.Meter); ok {
insts = unwrapInstruments(insts)
return del.RegisterCallback(f, insts...)
}
m.mtx.Lock()
defer m.mtx.Unlock()
if m.delegate != nil {
return m.delegate.RegisterCallback(unwrapCallback(f), unwrapInstruments(insts)...)
}
reg := &registration{instruments: insts, function: f}
e := m.registry.PushBack(reg)
reg.unreg = func() error {
@@ -284,15 +488,11 @@ func (m *meter) RegisterCallback(f metric.Callback, insts ...metric.Observable)
return reg, nil
}
type wrapped interface {
unwrap() metric.Observable
}
func unwrapInstruments(instruments []metric.Observable) []metric.Observable {
out := make([]metric.Observable, 0, len(instruments))
for _, inst := range instruments {
if in, ok := inst.(wrapped); ok {
if in, ok := inst.(unwrapper); ok {
out = append(out, in.unwrap())
} else {
out = append(out, inst)
@@ -312,9 +512,61 @@ type registration struct {
unregMu sync.Mutex
}
func (c *registration) setDelegate(m metric.Meter) {
insts := unwrapInstruments(c.instruments)
type unwrapObs struct {
embedded.Observer
obs metric.Observer
}
// unwrapFloat64Observable returns an expected metric.Float64Observable after
// unwrapping the global object.
func unwrapFloat64Observable(inst metric.Float64Observable) metric.Float64Observable {
if unwrapped, ok := inst.(unwrapper); ok {
if floatObs, ok := unwrapped.unwrap().(metric.Float64Observable); ok {
// Note: if the unwrapped object does not
// unwrap as an observable for either of the
// predicates here, it means an internal bug in
// this package. We avoid logging an error in
// this case, because the SDK has to try its
// own type conversion on the object. The SDK
// will see this and be forced to respond with
// its own error.
//
// This code uses a double-nested if statement
// to avoid creating a branch that is
// impossible to cover.
inst = floatObs
}
}
return inst
}
// unwrapInt64Observable returns an expected metric.Int64Observable after
// unwrapping the global object.
func unwrapInt64Observable(inst metric.Int64Observable) metric.Int64Observable {
if unwrapped, ok := inst.(unwrapper); ok {
if unint, ok := unwrapped.unwrap().(metric.Int64Observable); ok {
// See the comment in unwrapFloat64Observable().
inst = unint
}
}
return inst
}
func (uo *unwrapObs) ObserveFloat64(inst metric.Float64Observable, value float64, opts ...metric.ObserveOption) {
uo.obs.ObserveFloat64(unwrapFloat64Observable(inst), value, opts...)
}
func (uo *unwrapObs) ObserveInt64(inst metric.Int64Observable, value int64, opts ...metric.ObserveOption) {
uo.obs.ObserveInt64(unwrapInt64Observable(inst), value, opts...)
}
func unwrapCallback(f metric.Callback) metric.Callback {
return func(ctx context.Context, obs metric.Observer) error {
return f(ctx, &unwrapObs{obs: obs})
}
}
func (c *registration) setDelegate(m metric.Meter) {
c.unregMu.Lock()
defer c.unregMu.Unlock()
@@ -323,9 +575,10 @@ func (c *registration) setDelegate(m metric.Meter) {
return
}
reg, err := m.RegisterCallback(c.function, insts...)
reg, err := m.RegisterCallback(unwrapCallback(c.function), unwrapInstruments(c.instruments)...)
if err != nil {
GetErrorHandler().Handle(err)
return
}
c.unreg = reg.Unregister

View File

@@ -25,6 +25,7 @@ import (
"sync"
"sync/atomic"
"go.opentelemetry.io/auto/sdk"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
@@ -86,6 +87,8 @@ func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
key := il{
name: name,
version: c.InstrumentationVersion(),
schema: c.SchemaURL(),
attrs: c.InstrumentationAttributes(),
}
if p.tracers == nil {
@@ -104,6 +107,8 @@ func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
type il struct {
name string
version string
schema string
attrs attribute.Set
}
// tracer is a placeholder for a trace.Tracer.
@@ -141,6 +146,30 @@ func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStart
return delegate.(trace.Tracer).Start(ctx, name, opts...)
}
return t.newSpan(ctx, autoInstEnabled, name, opts)
}
// autoInstEnabled determines if the auto-instrumentation SDK span is returned
// from the tracer when not backed by a delegate and auto-instrumentation has
// attached to this process.
//
// The auto-instrumentation is expected to overwrite this value to true when it
// attaches. By default, this will point to false and mean a tracer will return
// a nonRecordingSpan by default.
var autoInstEnabled = new(bool)
func (t *tracer) newSpan(ctx context.Context, autoSpan *bool, name string, opts []trace.SpanStartOption) (context.Context, trace.Span) {
// autoInstEnabled is passed to newSpan via the autoSpan parameter. This is
// so the auto-instrumentation can define a uprobe for (*t).newSpan and be
// provided with the address of the bool autoInstEnabled points to. It
// needs to be a parameter so that pointer can be reliably determined, it
// should not be read from the global.
if *autoSpan {
tracer := sdk.TracerProvider().Tracer(t.name, t.opts...)
return tracer.Start(ctx, name, opts...)
}
s := nonRecordingSpan{sc: trace.SpanContextFromContext(ctx), tracer: t}
ctx = trace.ContextWithSpan(ctx, s)
return ctx, s

View File

@@ -20,11 +20,13 @@ func RawToBool(r uint64) bool {
}
func Int64ToRaw(i int64) uint64 {
return uint64(i)
// Assumes original was a valid int64 (overflow not checked).
return uint64(i) // nolint: gosec
}
func RawToInt64(r uint64) int64 {
return int64(r)
// Assumes original was a valid int64 (overflow not checked).
return int64(r) // nolint: gosec
}
func Float64ToRaw(f float64) uint64 {
@@ -36,9 +38,11 @@ func RawToFloat64(r uint64) float64 {
}
func RawPtrToFloat64Ptr(r *uint64) *float64 {
return (*float64)(unsafe.Pointer(r))
// Assumes original was a valid *float64 (overflow not checked).
return (*float64)(unsafe.Pointer(r)) // nolint: gosec
}
func RawPtrToInt64Ptr(r *uint64) *int64 {
return (*int64)(unsafe.Pointer(r))
// Assumes original was a valid *int64 (overflow not checked).
return (*int64)(unsafe.Pointer(r)) // nolint: gosec
}

View File

@@ -39,7 +39,7 @@ type Float64ObservableCounter interface {
}
// Float64ObservableCounterConfig contains options for asynchronous counter
// instruments that record int64 values.
// instruments that record float64 values.
type Float64ObservableCounterConfig struct {
description string
unit string
@@ -97,7 +97,7 @@ type Float64ObservableUpDownCounter interface {
}
// Float64ObservableUpDownCounterConfig contains options for asynchronous
// counter instruments that record int64 values.
// counter instruments that record float64 values.
type Float64ObservableUpDownCounterConfig struct {
description string
unit string
@@ -154,7 +154,7 @@ type Float64ObservableGauge interface {
}
// Float64ObservableGaugeConfig contains options for asynchronous counter
// instruments that record int64 values.
// instruments that record float64 values.
type Float64ObservableGaugeConfig struct {
description string
unit string
@@ -213,7 +213,7 @@ type Float64Observer interface {
}
// Float64Callback is a function registered with a Meter that makes
// observations for a Float64Observerable instrument it is registered with.
// observations for a Float64Observable instrument it is registered with.
// Calls to the Float64Observer record measurement values for the
// Float64Observable.
//

View File

@@ -212,7 +212,7 @@ type Int64Observer interface {
}
// Int64Callback is a function registered with a Meter that makes observations
// for an Int64Observerable instrument it is registered with. Calls to the
// for an Int64Observable instrument it is registered with. Calls to the
// Int64Observer record measurement values for the Int64Observable.
//
// The function needs to complete in a finite amount of time and the deadline

View File

@@ -57,6 +57,23 @@ asynchronous measurement, a Gauge ([Int64ObservableGauge] and
See the [OpenTelemetry documentation] for more information about instruments
and their intended use.
# Instrument Name
OpenTelemetry defines an [instrument name syntax] that restricts what
instrument names are allowed.
Instrument names should ...
- Not be empty.
- Have an alphabetic character as their first letter.
- Have any letter after the first be an alphanumeric character, _, .,
-, or /.
- Have a maximum length of 255 letters.
To ensure compatibility with observability platforms, all instruments created
need to conform to this syntax. Not all implementations of the API will validate
these names, it is the callers responsibility to ensure compliance.
# Measurements
Measurements are made by recording values and information about the values with
@@ -153,6 +170,7 @@ It is strongly recommended that authors only embed
That implementation is the only one OpenTelemetry authors can guarantee will
fully implement all the API interfaces when a user updates their API.
[instrument name syntax]: https://opentelemetry.io/docs/specs/otel/metrics/api/#instrument-name-syntax
[OpenTelemetry documentation]: https://opentelemetry.io/docs/concepts/signals/metrics/
[GetMeterProvider]: https://pkg.go.dev/go.opentelemetry.io/otel#GetMeterProvider
*/

View File

@@ -102,6 +102,16 @@ type Float64Counter interface{ float64Counter() }
// the API package).
type Float64Histogram interface{ float64Histogram() }
// Float64Gauge is embedded in [go.opentelemetry.io/otel/metric.Float64Gauge].
//
// Embed this interface in your implementation of the
// [go.opentelemetry.io/otel/metric.Float64Gauge] if you want users to
// experience a compilation error, signaling they need to update to your latest
// implementation, when the [go.opentelemetry.io/otel/metric.Float64Gauge]
// interface is extended (which is something that can happen without a major
// version bump of the API package).
type Float64Gauge interface{ float64Gauge() }
// Float64ObservableCounter is embedded in
// [go.opentelemetry.io/otel/metric.Float64ObservableCounter].
//
@@ -174,6 +184,16 @@ type Int64Counter interface{ int64Counter() }
// the API package).
type Int64Histogram interface{ int64Histogram() }
// Int64Gauge is embedded in [go.opentelemetry.io/otel/metric.Int64Gauge].
//
// Embed this interface in your implementation of the
// [go.opentelemetry.io/otel/metric.Int64Gauge] if you want users to experience
// a compilation error, signaling they need to update to your latest
// implementation, when the [go.opentelemetry.io/otel/metric.Int64Gauge]
// interface is extended (which is something that can happen without a major
// version bump of the API package).
type Int64Gauge interface{ int64Gauge() }
// Int64ObservableCounter is embedded in
// [go.opentelemetry.io/otel/metric.Int64ObservableCounter].
//

View File

@@ -16,6 +16,7 @@ type InstrumentOption interface {
Int64CounterOption
Int64UpDownCounterOption
Int64HistogramOption
Int64GaugeOption
Int64ObservableCounterOption
Int64ObservableUpDownCounterOption
Int64ObservableGaugeOption
@@ -23,6 +24,7 @@ type InstrumentOption interface {
Float64CounterOption
Float64UpDownCounterOption
Float64HistogramOption
Float64GaugeOption
Float64ObservableCounterOption
Float64ObservableUpDownCounterOption
Float64ObservableGaugeOption
@@ -51,6 +53,11 @@ func (o descOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64Histogra
return c
}
func (o descOpt) applyFloat64Gauge(c Float64GaugeConfig) Float64GaugeConfig {
c.description = string(o)
return c
}
func (o descOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig {
c.description = string(o)
return c
@@ -81,6 +88,11 @@ func (o descOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfi
return c
}
func (o descOpt) applyInt64Gauge(c Int64GaugeConfig) Int64GaugeConfig {
c.description = string(o)
return c
}
func (o descOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig {
c.description = string(o)
return c
@@ -116,6 +128,11 @@ func (o unitOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64Histogra
return c
}
func (o unitOpt) applyFloat64Gauge(c Float64GaugeConfig) Float64GaugeConfig {
c.unit = string(o)
return c
}
func (o unitOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig {
c.unit = string(o)
return c
@@ -146,6 +163,11 @@ func (o unitOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfi
return c
}
func (o unitOpt) applyInt64Gauge(c Int64GaugeConfig) Int64GaugeConfig {
c.unit = string(o)
return c
}
func (o unitOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig {
c.unit = string(o)
return c
@@ -329,7 +351,7 @@ func WithAttributeSet(attributes attribute.Set) MeasurementOption {
//
// cp := make([]attribute.KeyValue, len(attributes))
// copy(cp, attributes)
// WithAttributes(attribute.NewSet(cp...))
// WithAttributeSet(attribute.NewSet(cp...))
//
// [attribute.NewSet] may modify the passed attributes so this will make a copy
// of attributes before creating a set in order to ensure this function is

View File

@@ -47,17 +47,41 @@ type Meter interface {
// Int64Counter returns a new Int64Counter instrument identified by name
// and configured with options. The instrument is used to synchronously
// record increasing int64 measurements during a computational operation.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Int64Counter(name string, options ...Int64CounterOption) (Int64Counter, error)
// Int64UpDownCounter returns a new Int64UpDownCounter instrument
// identified by name and configured with options. The instrument is used
// to synchronously record int64 measurements during a computational
// operation.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Int64UpDownCounter(name string, options ...Int64UpDownCounterOption) (Int64UpDownCounter, error)
// Int64Histogram returns a new Int64Histogram instrument identified by
// name and configured with options. The instrument is used to
// synchronously record the distribution of int64 measurements during a
// computational operation.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Int64Histogram(name string, options ...Int64HistogramOption) (Int64Histogram, error)
// Int64Gauge returns a new Int64Gauge instrument identified by name and
// configured with options. The instrument is used to synchronously record
// instantaneous int64 measurements during a computational operation.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Int64Gauge(name string, options ...Int64GaugeOption) (Int64Gauge, error)
// Int64ObservableCounter returns a new Int64ObservableCounter identified
// by name and configured with options. The instrument is used to
// asynchronously record increasing int64 measurements once per a
@@ -67,7 +91,12 @@ type Meter interface {
// the WithInt64Callback option to register the callback here, or use the
// RegisterCallback method of this Meter to register one later. See the
// Measurements section of the package documentation for more information.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Int64ObservableCounter(name string, options ...Int64ObservableCounterOption) (Int64ObservableCounter, error)
// Int64ObservableUpDownCounter returns a new Int64ObservableUpDownCounter
// instrument identified by name and configured with options. The
// instrument is used to asynchronously record int64 measurements once per
@@ -77,7 +106,12 @@ type Meter interface {
// the WithInt64Callback option to register the callback here, or use the
// RegisterCallback method of this Meter to register one later. See the
// Measurements section of the package documentation for more information.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Int64ObservableUpDownCounter(name string, options ...Int64ObservableUpDownCounterOption) (Int64ObservableUpDownCounter, error)
// Int64ObservableGauge returns a new Int64ObservableGauge instrument
// identified by name and configured with options. The instrument is used
// to asynchronously record instantaneous int64 measurements once per a
@@ -87,23 +121,51 @@ type Meter interface {
// the WithInt64Callback option to register the callback here, or use the
// RegisterCallback method of this Meter to register one later. See the
// Measurements section of the package documentation for more information.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Int64ObservableGauge(name string, options ...Int64ObservableGaugeOption) (Int64ObservableGauge, error)
// Float64Counter returns a new Float64Counter instrument identified by
// name and configured with options. The instrument is used to
// synchronously record increasing float64 measurements during a
// computational operation.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Float64Counter(name string, options ...Float64CounterOption) (Float64Counter, error)
// Float64UpDownCounter returns a new Float64UpDownCounter instrument
// identified by name and configured with options. The instrument is used
// to synchronously record float64 measurements during a computational
// operation.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Float64UpDownCounter(name string, options ...Float64UpDownCounterOption) (Float64UpDownCounter, error)
// Float64Histogram returns a new Float64Histogram instrument identified by
// name and configured with options. The instrument is used to
// synchronously record the distribution of float64 measurements during a
// computational operation.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Float64Histogram(name string, options ...Float64HistogramOption) (Float64Histogram, error)
// Float64Gauge returns a new Float64Gauge instrument identified by name and
// configured with options. The instrument is used to synchronously record
// instantaneous float64 measurements during a computational operation.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Float64Gauge(name string, options ...Float64GaugeOption) (Float64Gauge, error)
// Float64ObservableCounter returns a new Float64ObservableCounter
// instrument identified by name and configured with options. The
// instrument is used to asynchronously record increasing float64
@@ -113,7 +175,12 @@ type Meter interface {
// the WithFloat64Callback option to register the callback here, or use the
// RegisterCallback method of this Meter to register one later. See the
// Measurements section of the package documentation for more information.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Float64ObservableCounter(name string, options ...Float64ObservableCounterOption) (Float64ObservableCounter, error)
// Float64ObservableUpDownCounter returns a new
// Float64ObservableUpDownCounter instrument identified by name and
// configured with options. The instrument is used to asynchronously record
@@ -123,7 +190,12 @@ type Meter interface {
// the WithFloat64Callback option to register the callback here, or use the
// RegisterCallback method of this Meter to register one later. See the
// Measurements section of the package documentation for more information.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Float64ObservableUpDownCounter(name string, options ...Float64ObservableUpDownCounterOption) (Float64ObservableUpDownCounter, error)
// Float64ObservableGauge returns a new Float64ObservableGauge instrument
// identified by name and configured with options. The instrument is used
// to asynchronously record instantaneous float64 measurements once per a
@@ -133,6 +205,10 @@ type Meter interface {
// the WithFloat64Callback option to register the callback here, or use the
// RegisterCallback method of this Meter to register one later. See the
// Measurements section of the package documentation for more information.
//
// The name needs to conform to the OpenTelemetry instrument name syntax.
// See the Instrument Name section of the package documentation for more
// information.
Float64ObservableGauge(name string, options ...Float64ObservableGaugeOption) (Float64ObservableGauge, error)
// RegisterCallback registers f to be called during the collection of a
@@ -178,6 +254,7 @@ type Observer interface {
// ObserveFloat64 records the float64 value for obsrv.
ObserveFloat64(obsrv Float64Observable, value float64, opts ...ObserveOption)
// ObserveInt64 records the int64 value for obsrv.
ObserveInt64(obsrv Int64Observable, value int64, opts ...ObserveOption)
}

View File

@@ -28,7 +28,7 @@ type Float64Counter interface {
}
// Float64CounterConfig contains options for synchronous counter instruments that
// record int64 values.
// record float64 values.
type Float64CounterConfig struct {
description string
unit string
@@ -81,7 +81,7 @@ type Float64UpDownCounter interface {
}
// Float64UpDownCounterConfig contains options for synchronous counter
// instruments that record int64 values.
// instruments that record float64 values.
type Float64UpDownCounterConfig struct {
description string
unit string
@@ -133,8 +133,8 @@ type Float64Histogram interface {
Record(ctx context.Context, incr float64, options ...RecordOption)
}
// Float64HistogramConfig contains options for synchronous counter instruments
// that record int64 values.
// Float64HistogramConfig contains options for synchronous histogram
// instruments that record float64 values.
type Float64HistogramConfig struct {
description string
unit string
@@ -172,3 +172,55 @@ func (c Float64HistogramConfig) ExplicitBucketBoundaries() []float64 {
type Float64HistogramOption interface {
applyFloat64Histogram(Float64HistogramConfig) Float64HistogramConfig
}
// Float64Gauge is an instrument that records instantaneous float64 values.
//
// Warning: Methods may be added to this interface in minor releases. See
// package documentation on API implementation for information on how to set
// default behavior for unimplemented methods.
type Float64Gauge interface {
// Users of the interface can ignore this. This embedded type is only used
// by implementations of this interface. See the "API Implementations"
// section of the package documentation for more information.
embedded.Float64Gauge
// Record records the instantaneous value.
//
// Use the WithAttributeSet (or, if performance is not a concern,
// the WithAttributes) option to include measurement attributes.
Record(ctx context.Context, value float64, options ...RecordOption)
}
// Float64GaugeConfig contains options for synchronous gauge instruments that
// record float64 values.
type Float64GaugeConfig struct {
description string
unit string
}
// NewFloat64GaugeConfig returns a new [Float64GaugeConfig] with all opts
// applied.
func NewFloat64GaugeConfig(opts ...Float64GaugeOption) Float64GaugeConfig {
var config Float64GaugeConfig
for _, o := range opts {
config = o.applyFloat64Gauge(config)
}
return config
}
// Description returns the configured description.
func (c Float64GaugeConfig) Description() string {
return c.description
}
// Unit returns the configured unit.
func (c Float64GaugeConfig) Unit() string {
return c.unit
}
// Float64GaugeOption applies options to a [Float64GaugeConfig]. See
// [InstrumentOption] for other options that can be used as a
// Float64GaugeOption.
type Float64GaugeOption interface {
applyFloat64Gauge(Float64GaugeConfig) Float64GaugeConfig
}

View File

@@ -133,7 +133,7 @@ type Int64Histogram interface {
Record(ctx context.Context, incr int64, options ...RecordOption)
}
// Int64HistogramConfig contains options for synchronous counter instruments
// Int64HistogramConfig contains options for synchronous histogram instruments
// that record int64 values.
type Int64HistogramConfig struct {
description string
@@ -172,3 +172,55 @@ func (c Int64HistogramConfig) ExplicitBucketBoundaries() []float64 {
type Int64HistogramOption interface {
applyInt64Histogram(Int64HistogramConfig) Int64HistogramConfig
}
// Int64Gauge is an instrument that records instantaneous int64 values.
//
// Warning: Methods may be added to this interface in minor releases. See
// package documentation on API implementation for information on how to set
// default behavior for unimplemented methods.
type Int64Gauge interface {
// Users of the interface can ignore this. This embedded type is only used
// by implementations of this interface. See the "API Implementations"
// section of the package documentation for more information.
embedded.Int64Gauge
// Record records the instantaneous value.
//
// Use the WithAttributeSet (or, if performance is not a concern,
// the WithAttributes) option to include measurement attributes.
Record(ctx context.Context, value int64, options ...RecordOption)
}
// Int64GaugeConfig contains options for synchronous gauge instruments that
// record int64 values.
type Int64GaugeConfig struct {
description string
unit string
}
// NewInt64GaugeConfig returns a new [Int64GaugeConfig] with all opts
// applied.
func NewInt64GaugeConfig(opts ...Int64GaugeOption) Int64GaugeConfig {
var config Int64GaugeConfig
for _, o := range opts {
config = o.applyInt64Gauge(config)
}
return config
}
// Description returns the configured description.
func (c Int64GaugeConfig) Description() string {
return c.description
}
// Unit returns the configured unit.
func (c Int64GaugeConfig) Unit() string {
return c.unit
}
// Int64GaugeOption applies options to a [Int64GaugeConfig]. See
// [InstrumentOption] for other options that can be used as a
// Int64GaugeOption.
type Int64GaugeOption interface {
applyInt64Gauge(Int64GaugeConfig) Int64GaugeConfig
}

30
vendor/go.opentelemetry.io/otel/renovate.json generated vendored Normal file
View File

@@ -0,0 +1,30 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:best-practices"
],
"ignorePaths": [],
"labels": ["Skip Changelog", "dependencies"],
"postUpdateOptions" : [
"gomodTidy"
],
"packageRules": [
{
"matchManagers": ["gomod"],
"matchDepTypes": ["indirect"],
"enabled": true
},
{
"matchPackageNames": ["go.opentelemetry.io/build-tools/**"],
"groupName": "build-tools"
},
{
"matchPackageNames": ["google.golang.org/genproto/googleapis/**"],
"groupName": "googleapis"
},
{
"matchPackageNames": ["golang.org/x/**"],
"groupName": "golang.org/x"
}
]
}

View File

@@ -1 +1 @@
codespell==2.2.6
codespell==2.4.1

View File

@@ -4,5 +4,6 @@
package instrumentation // import "go.opentelemetry.io/otel/sdk/instrumentation"
// Library represents the instrumentation library.
// Deprecated: please use Scope instead.
//
// Deprecated: use [Scope] instead.
type Library = Scope

View File

@@ -3,6 +3,8 @@
package instrumentation // import "go.opentelemetry.io/otel/sdk/instrumentation"
import "go.opentelemetry.io/otel/attribute"
// Scope represents the instrumentation scope.
type Scope struct {
// Name is the name of the instrumentation scope. This should be the
@@ -12,4 +14,6 @@ type Scope struct {
Version string
// SchemaURL of the telemetry emitted by the scope.
SchemaURL string
// Attributes of the telemetry emitted by the scope.
Attributes attribute.Set
}

View File

@@ -22,7 +22,7 @@ const (
BatchSpanProcessorMaxQueueSizeKey = "OTEL_BSP_MAX_QUEUE_SIZE"
// BatchSpanProcessorMaxExportBatchSizeKey is the maximum batch size (i.e.
// 512). Note: it must be less than or equal to
// EnvBatchSpanProcessorMaxQueueSize.
// BatchSpanProcessorMaxQueueSize.
BatchSpanProcessorMaxExportBatchSizeKey = "OTEL_BSP_MAX_EXPORT_BATCH_SIZE"
// AttributeValueLengthKey is the maximum allowed attribute value size.

View File

@@ -1,18 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package internal // import "go.opentelemetry.io/otel/sdk/internal"
//go:generate gotmpl --body=../../internal/shared/matchers/expectation.go.tmpl "--data={}" --out=matchers/expectation.go
//go:generate gotmpl --body=../../internal/shared/matchers/expecter.go.tmpl "--data={}" --out=matchers/expecter.go
//go:generate gotmpl --body=../../internal/shared/matchers/temporal_matcher.go.tmpl "--data={}" --out=matchers/temporal_matcher.go
//go:generate gotmpl --body=../../internal/shared/internaltest/alignment.go.tmpl "--data={}" --out=internaltest/alignment.go
//go:generate gotmpl --body=../../internal/shared/internaltest/env.go.tmpl "--data={}" --out=internaltest/env.go
//go:generate gotmpl --body=../../internal/shared/internaltest/env_test.go.tmpl "--data={}" --out=internaltest/env_test.go
//go:generate gotmpl --body=../../internal/shared/internaltest/errors.go.tmpl "--data={}" --out=internaltest/errors.go
//go:generate gotmpl --body=../../internal/shared/internaltest/harness.go.tmpl "--data={\"matchersImportPath\": \"go.opentelemetry.io/otel/sdk/internal/matchers\"}" --out=internaltest/harness.go
//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=internaltest/text_map_carrier.go
//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=internaltest/text_map_carrier_test.go
//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=internaltest/text_map_propagator.go
//go:generate gotmpl --body=../../internal/shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=internaltest/text_map_propagator_test.go

View File

@@ -1,17 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package internal // import "go.opentelemetry.io/otel/sdk/internal"
import "time"
// MonotonicEndTime returns the end time at present
// but offset from start, monotonically.
//
// The monotonic clock is used in subtractions hence
// the duration since start added back to start gives
// end as a monotonic time.
// See https://golang.org/pkg/time/#hdr-Monotonic_Clocks
func MonotonicEndTime(start time.Time) time.Time {
return start.Add(time.Since(start))
}

View File

@@ -0,0 +1,46 @@
# Experimental Features
The SDK contains features that have not yet stabilized in the OpenTelemetry specification.
These features are added to the OpenTelemetry Go SDK prior to stabilization in the specification so that users can start experimenting with them and provide feedback.
These feature may change in backwards incompatible ways as feedback is applied.
See the [Compatibility and Stability](#compatibility-and-stability) section for more information.
## Features
- [Resource](#resource)
### Resource
[OpenTelemetry resource semantic conventions] include many attribute definitions that are defined as experimental.
To have experimental semantic conventions be added by [resource detectors] set the `OTEL_GO_X_RESOURCE` environment variable.
The value set must be the case-insensitive string of `"true"` to enable the feature.
All other values are ignored.
<!-- TODO: document what attributes are added by which detector -->
[OpenTelemetry resource semantic conventions]: https://opentelemetry.io/docs/specs/semconv/resource/
[resource detectors]: https://pkg.go.dev/go.opentelemetry.io/otel/sdk/resource#Detector
#### Examples
Enable experimental resource semantic conventions.
```console
export OTEL_GO_X_RESOURCE=true
```
Disable experimental resource semantic conventions.
```console
unset OTEL_GO_X_RESOURCE
```
## Compatibility and Stability
Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../VERSIONING.md).
These features may be removed or modified in successive version releases, including patch versions.
When an experimental feature is promoted to a stable feature, a migration path will be included in the changelog entry of the release.
There is no guarantee that any environment variable feature flags that enabled the experimental feature will be supported by the stable version.
If they are supported, they may be accompanied with a deprecation notice stating a timeline for the removal of that support.

66
vendor/go.opentelemetry.io/otel/sdk/internal/x/x.go generated vendored Normal file
View File

@@ -0,0 +1,66 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package x contains support for OTel SDK experimental features.
//
// This package should only be used for features defined in the specification.
// It should not be used for experiments or new project ideas.
package x // import "go.opentelemetry.io/otel/sdk/internal/x"
import (
"os"
"strings"
)
// Resource is an experimental feature flag that defines if resource detectors
// should be included experimental semantic conventions.
//
// To enable this feature set the OTEL_GO_X_RESOURCE environment variable
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
// will also enable this).
var Resource = newFeature("RESOURCE", func(v string) (string, bool) {
if strings.ToLower(v) == "true" {
return v, true
}
return "", false
})
// Feature is an experimental feature control flag. It provides a uniform way
// to interact with these feature flags and parse their values.
type Feature[T any] struct {
key string
parse func(v string) (T, bool)
}
func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] {
const envKeyRoot = "OTEL_GO_X_"
return Feature[T]{
key: envKeyRoot + suffix,
parse: parse,
}
}
// Key returns the environment variable key that needs to be set to enable the
// feature.
func (f Feature[T]) Key() string { return f.key }
// Lookup returns the user configured value for the feature and true if the
// user has enabled the feature. Otherwise, if the feature is not enabled, a
// zero-value and false are returned.
func (f Feature[T]) Lookup() (v T, ok bool) {
// https://github.com/open-telemetry/opentelemetry-specification/blob/62effed618589a0bec416a87e559c0a9d96289bb/specification/configuration/sdk-environment-variables.md#parsing-empty-value
//
// > The SDK MUST interpret an empty value of an environment variable the
// > same way as when the variable is unset.
vRaw := os.Getenv(f.key)
if vRaw == "" {
return v, ok
}
return f.parse(vRaw)
}
// Enabled returns if the feature is enabled.
func (f Feature[T]) Enabled() bool {
_, ok := f.Lookup()
return ok
}

View File

@@ -7,7 +7,6 @@ import (
"context"
"errors"
"fmt"
"strings"
)
// ErrPartialResource is returned by a detector when complete source
@@ -57,62 +56,37 @@ func Detect(ctx context.Context, detectors ...Detector) (*Resource, error) {
// these errors will be returned. Otherwise, nil is returned.
func detect(ctx context.Context, res *Resource, detectors []Detector) error {
var (
r *Resource
errs detectErrs
err error
r *Resource
err error
e error
)
for _, detector := range detectors {
if detector == nil {
continue
}
r, err = detector.Detect(ctx)
if err != nil {
errs = append(errs, err)
if !errors.Is(err, ErrPartialResource) {
r, e = detector.Detect(ctx)
if e != nil {
err = errors.Join(err, e)
if !errors.Is(e, ErrPartialResource) {
continue
}
}
r, err = Merge(res, r)
if err != nil {
errs = append(errs, err)
r, e = Merge(res, r)
if e != nil {
err = errors.Join(err, e)
}
*res = *r
}
if len(errs) == 0 {
return nil
if err != nil {
if errors.Is(err, ErrSchemaURLConflict) {
// If there has been a merge conflict, ensure the resource has no
// schema URL.
res.schemaURL = ""
}
err = fmt.Errorf("error detecting resource: %w", err)
}
if errors.Is(errs, ErrSchemaURLConflict) {
// If there has been a merge conflict, ensure the resource has no
// schema URL.
res.schemaURL = ""
}
return errs
}
type detectErrs []error
func (e detectErrs) Error() string {
errStr := make([]string, len(e))
for i, err := range e {
errStr[i] = fmt.Sprintf("* %s", err)
}
format := "%d errors occurred detecting resource:\n\t%s"
return fmt.Sprintf(format, len(e), strings.Join(errStr, "\n\t"))
}
func (e detectErrs) Unwrap() error {
switch len(e) {
case 0:
return nil
case 1:
return e[0]
}
return e[1:]
}
func (e detectErrs) Is(target error) bool {
return len(e) != 0 && errors.Is(e[0], target)
return err
}

View File

@@ -9,24 +9,24 @@ import (
"os"
"path/filepath"
"github.com/google/uuid"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
type (
// telemetrySDK is a Detector that provides information about
// the OpenTelemetry SDK used. This Detector is included as a
// builtin. If these resource attributes are not wanted, use
// the WithTelemetrySDK(nil) or WithoutBuiltin() options to
// explicitly disable them.
// resource.New() to explicitly disable them.
telemetrySDK struct{}
// host is a Detector that provides information about the host
// being run on. This Detector is included as a builtin. If
// these resource attributes are not wanted, use the
// WithHost(nil) or WithoutBuiltin() options to explicitly
// disable them.
// resource.New() to explicitly disable them.
host struct{}
stringDetector struct {
@@ -36,6 +36,8 @@ type (
}
defaultServiceNameDetector struct{}
defaultServiceInstanceIDDetector struct{}
)
var (
@@ -43,6 +45,7 @@ var (
_ Detector = host{}
_ Detector = stringDetector{}
_ Detector = defaultServiceNameDetector{}
_ Detector = defaultServiceInstanceIDDetector{}
)
// Detect returns a *Resource that describes the OpenTelemetry SDK used.
@@ -95,3 +98,19 @@ func (defaultServiceNameDetector) Detect(ctx context.Context) (*Resource, error)
},
).Detect(ctx)
}
// Detect implements Detector.
func (defaultServiceInstanceIDDetector) Detect(ctx context.Context) (*Resource, error) {
return StringDetector(
semconv.SchemaURL,
semconv.ServiceInstanceIDKey,
func() (string, error) {
version4Uuid, err := uuid.NewRandom()
if err != nil {
return "", err
}
return version4Uuid.String(), nil
},
).Detect(ctx)
}

View File

@@ -11,7 +11,7 @@ import (
"os"
"regexp"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
type containerIDProvider func() (string, error)

View File

@@ -12,7 +12,7 @@ import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
const (

View File

@@ -8,7 +8,7 @@ import (
"errors"
"strings"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
type hostIDProvider func() (string, error)

View File

@@ -10,17 +10,16 @@ import (
"golang.org/x/sys/windows/registry"
)
// implements hostIDReader
// implements hostIDReader.
type hostIDReaderWindows struct{}
// read reads MachineGuid from the windows registry key:
// SOFTWARE\Microsoft\Cryptography
// read reads MachineGuid from the Windows registry key:
// SOFTWARE\Microsoft\Cryptography.
func (*hostIDReaderWindows) read() (string, error) {
k, err := registry.OpenKey(
registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Cryptography`,
registry.QUERY_VALUE|registry.WOW64_64KEY,
)
if err != nil {
return "", err
}

View File

@@ -8,7 +8,7 @@ import (
"strings"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
type osDescriptionProvider func() (string, error)

View File

@@ -5,6 +5,7 @@ package resource // import "go.opentelemetry.io/otel/sdk/resource"
import (
"encoding/xml"
"errors"
"fmt"
"io"
"os"
@@ -63,7 +64,7 @@ func parsePlistFile(file io.Reader) (map[string]string, error) {
}
if len(v.Dict.Key) != len(v.Dict.String) {
return nil, fmt.Errorf("the number of <key> and <string> elements doesn't match")
return nil, errors.New("the number of <key> and <string> elements doesn't match")
}
properties := make(map[string]string, len(v.Dict.Key))

View File

@@ -17,7 +17,6 @@ import (
func platformOSDescription() (string, error) {
k, err := registry.OpenKey(
registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
if err != nil {
return "", err
}

View File

@@ -11,7 +11,7 @@ import (
"path/filepath"
"runtime"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
type (

View File

@@ -11,6 +11,7 @@ import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/internal/x"
)
// Resource describes an entity about which identifying information
@@ -20,11 +21,22 @@ import (
// Resources should be passed and stored as pointers
// (`*resource.Resource`). The `nil` value is equivalent to an empty
// Resource.
//
// Note that the Go == operator compares not just the resource attributes but
// also all other internals of the Resource type. Therefore, Resource values
// should not be used as map or database keys. In general, the [Resource.Equal]
// method should be used instead of direct comparison with ==, since that
// method ensures the correct comparison of resource attributes, and the
// [attribute.Distinct] returned from [Resource.Equivalent] should be used for
// map and database keys instead.
type Resource struct {
attrs attribute.Set
schemaURL string
}
// Compile-time check that the Resource remains comparable.
var _ map[Resource]struct{} = nil
var (
defaultResource *Resource
defaultResourceOnce sync.Once
@@ -136,15 +148,19 @@ func (r *Resource) Iter() attribute.Iterator {
return r.attrs.Iter()
}
// Equal returns true when a Resource is equivalent to this Resource.
func (r *Resource) Equal(eq *Resource) bool {
// Equal returns whether r and o represent the same resource. Two resources can
// be equal even if they have different schema URLs.
//
// See the documentation on the [Resource] type for the pitfalls of using ==
// with Resource values; most code should use Equal instead.
func (r *Resource) Equal(o *Resource) bool {
if r == nil {
r = Empty()
}
if eq == nil {
eq = Empty()
if o == nil {
o = Empty()
}
return r.Equivalent() == eq.Equivalent()
return r.Equivalent() == o.Equivalent()
}
// Merge creates a new [Resource] by merging a and b.
@@ -218,11 +234,17 @@ func Empty() *Resource {
func Default() *Resource {
defaultResourceOnce.Do(func() {
var err error
defaultResource, err = Detect(
context.Background(),
defaultDetectors := []Detector{
defaultServiceNameDetector{},
fromEnv{},
telemetrySDK{},
}
if x.Resource.Enabled() {
defaultDetectors = append([]Detector{defaultServiceInstanceIDDetector{}}, defaultDetectors...)
}
defaultResource, err = Detect(
context.Background(),
defaultDetectors...,
)
if err != nil {
otel.Handle(err)

View File

@@ -201,10 +201,9 @@ func (bsp *batchSpanProcessor) ForceFlush(ctx context.Context) error {
}
}
wait := make(chan error)
wait := make(chan error, 1)
go func() {
wait <- bsp.exportSpans(ctx)
close(wait)
}()
// Wait until the export is finished or the context is cancelled/timed out
select {
@@ -280,6 +279,7 @@ func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error {
//
// It is up to the exporter to implement any type of retry logic if a batch is failing
// to be exported, since it is specific to the protocol and backend being sent to.
clear(bsp.batch) // Erase elements to let GC collect objects
bsp.batch = bsp.batch[:0]
if err != nil {
@@ -316,7 +316,11 @@ func (bsp *batchSpanProcessor) processQueue() {
bsp.batchMutex.Unlock()
if shouldExport {
if !bsp.timer.Stop() {
<-bsp.timer.C
// Handle both GODEBUG=asynctimerchan=[0|1] properly.
select {
case <-bsp.timer.C:
default:
}
}
if err := bsp.exportSpans(ctx); err != nil {
otel.Handle(err)
@@ -381,7 +385,7 @@ func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd R
}
}
func (bsp *batchSpanProcessor) enqueueDrop(ctx context.Context, sd ReadOnlySpan) bool {
func (bsp *batchSpanProcessor) enqueueDrop(_ context.Context, sd ReadOnlySpan) bool {
if !sd.SpanContext().IsSampled() {
return false
}

View File

@@ -3,23 +3,44 @@
package trace // import "go.opentelemetry.io/otel/sdk/trace"
import (
"slices"
"sync"
"go.opentelemetry.io/otel/internal/global"
)
// evictedQueue is a FIFO queue with a configurable capacity.
type evictedQueue struct {
queue []interface{}
capacity int
droppedCount int
type evictedQueue[T any] struct {
queue []T
capacity int
droppedCount int
logDroppedMsg string
logDroppedOnce sync.Once
}
func newEvictedQueue(capacity int) evictedQueue {
func newEvictedQueueEvent(capacity int) evictedQueue[Event] {
// Do not pre-allocate queue, do this lazily.
return evictedQueue{capacity: capacity}
return evictedQueue[Event]{
capacity: capacity,
logDroppedMsg: "limit reached: dropping trace trace.Event",
}
}
func newEvictedQueueLink(capacity int) evictedQueue[Link] {
// Do not pre-allocate queue, do this lazily.
return evictedQueue[Link]{
capacity: capacity,
logDroppedMsg: "limit reached: dropping trace trace.Link",
}
}
// add adds value to the evictedQueue eq. If eq is at capacity, the oldest
// queued value will be discarded and the drop count incremented.
func (eq *evictedQueue) add(value interface{}) {
func (eq *evictedQueue[T]) add(value T) {
if eq.capacity == 0 {
eq.droppedCount++
eq.logDropped()
return
}
@@ -28,6 +49,16 @@ func (eq *evictedQueue) add(value interface{}) {
copy(eq.queue[:eq.capacity-1], eq.queue[1:])
eq.queue = eq.queue[:eq.capacity-1]
eq.droppedCount++
eq.logDropped()
}
eq.queue = append(eq.queue, value)
}
func (eq *evictedQueue[T]) logDropped() {
eq.logDroppedOnce.Do(func() { global.Warn(eq.logDroppedMsg) })
}
// copy returns a copy of the evictedQueue.
func (eq *evictedQueue[T]) copy() []T {
return slices.Clone(eq.queue)
}

View File

@@ -41,7 +41,12 @@ func (gen *randomIDGenerator) NewSpanID(ctx context.Context, traceID trace.Trace
gen.Lock()
defer gen.Unlock()
sid := trace.SpanID{}
_, _ = gen.randSource.Read(sid[:])
for {
_, _ = gen.randSource.Read(sid[:])
if sid.IsValid() {
break
}
}
return sid
}
@@ -51,9 +56,19 @@ func (gen *randomIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace.
gen.Lock()
defer gen.Unlock()
tid := trace.TraceID{}
_, _ = gen.randSource.Read(tid[:])
sid := trace.SpanID{}
_, _ = gen.randSource.Read(sid[:])
for {
_, _ = gen.randSource.Read(tid[:])
if tid.IsValid() {
break
}
}
for {
_, _ = gen.randSource.Read(sid[:])
if sid.IsValid() {
break
}
}
return tid, sid
}

View File

@@ -139,9 +139,10 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
name = defaultTracerName
}
is := instrumentation.Scope{
Name: name,
Version: c.InstrumentationVersion(),
SchemaURL: c.SchemaURL(),
Name: name,
Version: c.InstrumentationVersion(),
SchemaURL: c.SchemaURL(),
Attributes: c.InstrumentationAttributes(),
}
t, ok := func() (trace.Tracer, bool) {
@@ -168,7 +169,7 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
// slowing down all tracing consumers.
// - Logging code may be instrumented with tracing and deadlock because it could try
// acquiring the same non-reentrant mutex.
global.Info("Tracer created", "name", name, "version", is.Version, "schemaURL", is.SchemaURL)
global.Info("Tracer created", "name", name, "version", is.Version, "schemaURL", is.SchemaURL, "attributes", is.Attributes)
}
return t
}
@@ -291,7 +292,7 @@ func (p *TracerProvider) Shutdown(ctx context.Context) error {
retErr = err
} else {
// Poor man's list of errors
retErr = fmt.Errorf("%v; %v", retErr, err)
retErr = fmt.Errorf("%w; %w", retErr, err)
}
}
}

View File

@@ -5,7 +5,6 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace"
import (
"errors"
"fmt"
"os"
"strconv"
"strings"
@@ -26,7 +25,7 @@ const (
type errUnsupportedSampler string
func (e errUnsupportedSampler) Error() string {
return fmt.Sprintf("unsupported sampler: %s", string(e))
return "unsupported sampler: " + string(e)
}
var (
@@ -39,7 +38,7 @@ type samplerArgParseError struct {
}
func (e samplerArgParseError) Error() string {
return fmt.Sprintf("parsing sampler argument: %s", e.parseErr.Error())
return "parsing sampler argument: " + e.parseErr.Error()
}
func (e samplerArgParseError) Unwrap() error {

View File

@@ -47,12 +47,12 @@ const (
// Drop will not record the span and all attributes/events will be dropped.
Drop SamplingDecision = iota
// Record indicates the span's `IsRecording() == true`, but `Sampled` flag
// *must not* be set.
// RecordOnly indicates the span's IsRecording method returns true, but trace.FlagsSampled flag
// must not be set.
RecordOnly
// RecordAndSample has span's `IsRecording() == true` and `Sampled` flag
// *must* be set.
// RecordAndSample indicates the span's IsRecording method returns true and trace.FlagsSampled flag
// must be set.
RecordAndSample
)

View File

@@ -58,7 +58,7 @@ func (ssp *simpleSpanProcessor) Shutdown(ctx context.Context) error {
var err error
ssp.stopOnce.Do(func() {
stopFunc := func(exp SpanExporter) (<-chan error, func()) {
done := make(chan error)
done := make(chan error, 1)
return done, func() { done <- exp.Shutdown(ctx) }
}

View File

@@ -99,7 +99,7 @@ func (s snapshot) InstrumentationScope() instrumentation.Scope {
// InstrumentationLibrary returns information about the instrumentation
// library that created the span.
func (s snapshot) InstrumentationLibrary() instrumentation.Library {
func (s snapshot) InstrumentationLibrary() instrumentation.Library { //nolint:staticcheck // This method needs to be define for backwards compatibility
return s.instrumentationScope
}

View File

@@ -17,10 +17,10 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/internal/global"
"go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/internal"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/embedded"
)
@@ -62,7 +62,7 @@ type ReadOnlySpan interface {
// InstrumentationLibrary returns information about the instrumentation
// library that created the span.
// Deprecated: please use InstrumentationScope instead.
InstrumentationLibrary() instrumentation.Library
InstrumentationLibrary() instrumentation.Library //nolint:staticcheck // This method needs to be define for backwards compatibility
// Resource returns information about the entity that produced the span.
Resource() *resource.Resource
// DroppedAttributes returns the number of attributes dropped by the span
@@ -137,12 +137,13 @@ type recordingSpan struct {
// ReadOnlySpan exported when the span ends.
attributes []attribute.KeyValue
droppedAttributes int
logDropAttrsOnce sync.Once
// events are stored in FIFO queue capped by configured limit.
events evictedQueue
events evictedQueue[Event]
// links are stored in FIFO queue capped by configured limit.
links evictedQueue
links evictedQueue[Link]
// executionTracerTaskEnd ends the execution tracer span.
executionTracerTaskEnd func()
@@ -173,6 +174,17 @@ func (s *recordingSpan) IsRecording() bool {
s.mu.Lock()
defer s.mu.Unlock()
return s.isRecording()
}
// isRecording returns if this span is being recorded. If this span has ended
// this will return false.
//
// This method assumes s.mu.Lock is held by the caller.
func (s *recordingSpan) isRecording() bool {
if s == nil {
return false
}
return s.endTime.IsZero()
}
@@ -181,11 +193,15 @@ func (s *recordingSpan) IsRecording() bool {
// included in the set status when the code is for an error. If this span is
// not being recorded than this method does nothing.
func (s *recordingSpan) SetStatus(code codes.Code, description string) {
if !s.IsRecording() {
if s == nil {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if !s.isRecording() {
return
}
if s.status.Code > code {
return
}
@@ -209,17 +225,20 @@ func (s *recordingSpan) SetStatus(code codes.Code, description string) {
// attributes the span is configured to have, the last added attributes will
// be dropped.
func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) {
if !s.IsRecording() {
if s == nil || len(attributes) == 0 {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if !s.isRecording() {
return
}
limit := s.tracer.provider.spanLimits.AttributeCountLimit
if limit == 0 {
// No attributes allowed.
s.droppedAttributes += len(attributes)
s.addDroppedAttr(len(attributes))
return
}
@@ -232,11 +251,11 @@ func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) {
// Otherwise, add without deduplication. When attributes are read they
// will be deduplicated, optimizing the operation.
s.attributes = slices.Grow(s.attributes, len(s.attributes)+len(attributes))
s.attributes = slices.Grow(s.attributes, len(attributes))
for _, a := range attributes {
if !a.Valid() {
// Drop all invalid attributes.
s.droppedAttributes++
s.addDroppedAttr(1)
continue
}
a = truncateAttr(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a)
@@ -244,6 +263,22 @@ func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) {
}
}
// Declared as a var so tests can override.
var logDropAttrs = func() {
global.Warn("limit reached: dropping trace Span attributes")
}
// addDroppedAttr adds incr to the count of dropped attributes.
//
// The first, and only the first, time this method is called a warning will be
// logged.
//
// This method assumes s.mu.Lock is held by the caller.
func (s *recordingSpan) addDroppedAttr(incr int) {
s.droppedAttributes += incr
s.logDropAttrsOnce.Do(logDropAttrs)
}
// addOverCapAttrs adds the attributes attrs to the span s while
// de-duplicating the attributes of s and attrs and dropping attributes that
// exceed the limit.
@@ -263,22 +298,27 @@ func (s *recordingSpan) addOverCapAttrs(limit int, attrs []attribute.KeyValue) {
// Do not set a capacity when creating this map. Benchmark testing has
// showed this to only add unused memory allocations in general use.
exists := make(map[attribute.Key]int)
s.dedupeAttrsFromRecord(&exists)
exists := make(map[attribute.Key]int, len(s.attributes))
s.dedupeAttrsFromRecord(exists)
// Now that s.attributes is deduplicated, adding unique attributes up to
// the capacity of s will not over allocate s.attributes.
sum := len(attrs) + len(s.attributes)
s.attributes = slices.Grow(s.attributes, min(sum, limit))
// max size = limit
maxCap := min(len(attrs)+len(s.attributes), limit)
if cap(s.attributes) < maxCap {
s.attributes = slices.Grow(s.attributes, maxCap-cap(s.attributes))
}
for _, a := range attrs {
if !a.Valid() {
// Drop all invalid attributes.
s.droppedAttributes++
s.addDroppedAttr(1)
continue
}
if idx, ok := exists[a.Key]; ok {
// Perform all updates before dropping, even when at capacity.
a = truncateAttr(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a)
s.attributes[idx] = a
continue
}
@@ -286,7 +326,7 @@ func (s *recordingSpan) addOverCapAttrs(limit int, attrs []attribute.KeyValue) {
if len(s.attributes) >= limit {
// Do not just drop all of the remaining attributes, make sure
// updates are checked and performed.
s.droppedAttributes++
s.addDroppedAttr(1)
} else {
a = truncateAttr(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a)
s.attributes = append(s.attributes, a)
@@ -307,54 +347,99 @@ func truncateAttr(limit int, attr attribute.KeyValue) attribute.KeyValue {
}
switch attr.Value.Type() {
case attribute.STRING:
if v := attr.Value.AsString(); len(v) > limit {
return attr.Key.String(safeTruncate(v, limit))
}
v := attr.Value.AsString()
return attr.Key.String(truncate(limit, v))
case attribute.STRINGSLICE:
v := attr.Value.AsStringSlice()
for i := range v {
if len(v[i]) > limit {
v[i] = safeTruncate(v[i], limit)
}
v[i] = truncate(limit, v[i])
}
return attr.Key.StringSlice(v)
}
return attr
}
// safeTruncate truncates the string and guarantees valid UTF-8 is returned.
func safeTruncate(input string, limit int) string {
if trunc, ok := safeTruncateValidUTF8(input, limit); ok {
return trunc
// truncate returns a truncated version of s such that it contains less than
// the limit number of characters. Truncation is applied by returning the limit
// number of valid characters contained in s.
//
// If limit is negative, it returns the original string.
//
// UTF-8 is supported. When truncating, all invalid characters are dropped
// before applying truncation.
//
// If s already contains less than the limit number of bytes, it is returned
// unchanged. No invalid characters are removed.
func truncate(limit int, s string) string {
// This prioritize performance in the following order based on the most
// common expected use-cases.
//
// - Short values less than the default limit (128).
// - Strings with valid encodings that exceed the limit.
// - No limit.
// - Strings with invalid encodings that exceed the limit.
if limit < 0 || len(s) <= limit {
return s
}
trunc, _ := safeTruncateValidUTF8(strings.ToValidUTF8(input, ""), limit)
return trunc
}
// safeTruncateValidUTF8 returns a copy of the input string safely truncated to
// limit. The truncation is ensured to occur at the bounds of complete UTF-8
// characters. If invalid encoding of UTF-8 is encountered, input is returned
// with false, otherwise, the truncated input will be returned with true.
func safeTruncateValidUTF8(input string, limit int) (string, bool) {
for cnt := 0; cnt <= limit; {
r, size := utf8.DecodeRuneInString(input[cnt:])
if r == utf8.RuneError {
return input, false
// Optimistically, assume all valid UTF-8.
var b strings.Builder
count := 0
for i, c := range s {
if c != utf8.RuneError {
count++
if count > limit {
return s[:i]
}
continue
}
if cnt+size > limit {
return input[:cnt], true
_, size := utf8.DecodeRuneInString(s[i:])
if size == 1 {
// Invalid encoding.
b.Grow(len(s) - 1)
_, _ = b.WriteString(s[:i])
s = s[i:]
break
}
cnt += size
}
return input, true
// Fast-path, no invalid input.
if b.Cap() == 0 {
return s
}
// Truncate while validating UTF-8.
for i := 0; i < len(s) && count < limit; {
c := s[i]
if c < utf8.RuneSelf {
// Optimization for single byte runes (common case).
_ = b.WriteByte(c)
i++
count++
continue
}
_, size := utf8.DecodeRuneInString(s[i:])
if size == 1 {
// We checked for all 1-byte runes above, this is a RuneError.
i++
continue
}
_, _ = b.WriteString(s[i : i+size])
i += size
count++
}
return b.String()
}
// End ends the span. This method does nothing if the span is already ended or
// is not being recorded.
//
// The only SpanOption currently supported is WithTimestamp which will set the
// end time for a Span's life-cycle.
// The only SpanEndOption currently supported are [trace.WithTimestamp], and
// [trace.WithStackTrace].
//
// If this method is called while panicking an error event is added to the
// Span before ending it and the panic is continued.
@@ -367,11 +452,12 @@ func (s *recordingSpan) End(options ...trace.SpanEndOption) {
// Store the end time as soon as possible to avoid artificially increasing
// the span's duration in case some operation below takes a while.
et := internal.MonotonicEndTime(s.startTime)
et := monotonicEndTime(s.startTime)
// Do relative expensive check now that we have an end time and see if we
// need to do any more processing.
if !s.IsRecording() {
// Lock the span now that we have an end time and see if we need to do any more processing.
s.mu.Lock()
if !s.isRecording() {
s.mu.Unlock()
return
}
@@ -396,10 +482,11 @@ func (s *recordingSpan) End(options ...trace.SpanEndOption) {
}
if s.executionTracerTaskEnd != nil {
s.mu.Unlock()
s.executionTracerTaskEnd()
s.mu.Lock()
}
s.mu.Lock()
// Setting endTime to non-zero marks the span as ended and not recording.
if config.Timestamp().IsZero() {
s.endTime = et
@@ -418,12 +505,28 @@ func (s *recordingSpan) End(options ...trace.SpanEndOption) {
}
}
// monotonicEndTime returns the end time at present but offset from start,
// monotonically.
//
// The monotonic clock is used in subtractions hence the duration since start
// added back to start gives end as a monotonic time. See
// https://golang.org/pkg/time/#hdr-Monotonic_Clocks
func monotonicEndTime(start time.Time) time.Time {
return start.Add(time.Since(start))
}
// RecordError will record err as a span event for this span. An additional call to
// SetStatus is required if the Status of the Span should be set to Error, this method
// does not change the Span status. If this span is not being recorded or err is nil
// than this method does nothing.
func (s *recordingSpan) RecordError(err error, opts ...trace.EventOption) {
if s == nil || err == nil || !s.IsRecording() {
if s == nil || err == nil {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if !s.isRecording() {
return
}
@@ -459,14 +562,23 @@ func recordStackTrace() string {
}
// AddEvent adds an event with the provided name and options. If this span is
// not being recorded than this method does nothing.
// not being recorded then this method does nothing.
func (s *recordingSpan) AddEvent(name string, o ...trace.EventOption) {
if !s.IsRecording() {
if s == nil {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if !s.isRecording() {
return
}
s.addEvent(name, o...)
}
// addEvent adds an event with the provided name and options.
//
// This method assumes s.mu.Lock is held by the caller.
func (s *recordingSpan) addEvent(name string, o ...trace.EventOption) {
c := trace.NewEventConfig(o...)
e := Event{Name: name, Attributes: c.Attributes(), Time: c.Timestamp()}
@@ -483,20 +595,21 @@ func (s *recordingSpan) addEvent(name string, o ...trace.EventOption) {
e.Attributes = e.Attributes[:limit]
}
s.mu.Lock()
s.events.add(e)
s.mu.Unlock()
}
// SetName sets the name of this span. If this span is not being recorded than
// this method does nothing.
func (s *recordingSpan) SetName(name string) {
if !s.IsRecording() {
if s == nil {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if !s.isRecording() {
return
}
s.name = name
}
@@ -552,29 +665,26 @@ func (s *recordingSpan) Attributes() []attribute.KeyValue {
func (s *recordingSpan) dedupeAttrs() {
// Do not set a capacity when creating this map. Benchmark testing has
// showed this to only add unused memory allocations in general use.
exists := make(map[attribute.Key]int)
s.dedupeAttrsFromRecord(&exists)
exists := make(map[attribute.Key]int, len(s.attributes))
s.dedupeAttrsFromRecord(exists)
}
// dedupeAttrsFromRecord deduplicates the attributes of s to fit capacity
// using record as the record of unique attribute keys to their index.
//
// This method assumes s.mu.Lock is held by the caller.
func (s *recordingSpan) dedupeAttrsFromRecord(record *map[attribute.Key]int) {
func (s *recordingSpan) dedupeAttrsFromRecord(record map[attribute.Key]int) {
// Use the fact that slices share the same backing array.
unique := s.attributes[:0]
for _, a := range s.attributes {
if idx, ok := (*record)[a.Key]; ok {
if idx, ok := record[a.Key]; ok {
unique[idx] = a
} else {
unique = append(unique, a)
(*record)[a.Key] = len(unique) - 1
record[a.Key] = len(unique) - 1
}
}
// s.attributes have element types of attribute.KeyValue. These types are
// not pointers and they themselves do not contain pointer fields,
// therefore the duplicate values do not need to be zeroed for them to be
// garbage collected.
clear(s.attributes[len(unique):]) // Erase unneeded elements to let GC collect objects.
s.attributes = unique
}
@@ -585,7 +695,7 @@ func (s *recordingSpan) Links() []Link {
if len(s.links.queue) == 0 {
return []Link{}
}
return s.interfaceArrayToLinksArray()
return s.links.copy()
}
// Events returns the events of this span.
@@ -595,7 +705,7 @@ func (s *recordingSpan) Events() []Event {
if len(s.events.queue) == 0 {
return []Event{}
}
return s.interfaceArrayToEventArray()
return s.events.copy()
}
// Status returns the status of this span.
@@ -615,7 +725,7 @@ func (s *recordingSpan) InstrumentationScope() instrumentation.Scope {
// InstrumentationLibrary returns the instrumentation.Library associated with
// the Tracer that created this span.
func (s *recordingSpan) InstrumentationLibrary() instrumentation.Library {
func (s *recordingSpan) InstrumentationLibrary() instrumentation.Library { //nolint:staticcheck // This method needs to be define for backwards compatibility
s.mu.Lock()
defer s.mu.Unlock()
return s.tracer.instrumentationScope
@@ -630,7 +740,17 @@ func (s *recordingSpan) Resource() *resource.Resource {
}
func (s *recordingSpan) AddLink(link trace.Link) {
if !s.IsRecording() || !link.SpanContext.IsValid() {
if s == nil {
return
}
if !link.SpanContext.IsValid() && len(link.Attributes) == 0 &&
link.SpanContext.TraceState().Len() == 0 {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if !s.isRecording() {
return
}
@@ -647,9 +767,7 @@ func (s *recordingSpan) AddLink(link trace.Link) {
l.Attributes = l.Attributes[:limit]
}
s.mu.Lock()
s.links.add(l)
s.mu.Unlock()
}
// DroppedAttributes returns the number of attributes dropped by the span
@@ -713,39 +831,27 @@ func (s *recordingSpan) snapshot() ReadOnlySpan {
}
sd.droppedAttributeCount = s.droppedAttributes
if len(s.events.queue) > 0 {
sd.events = s.interfaceArrayToEventArray()
sd.events = s.events.copy()
sd.droppedEventCount = s.events.droppedCount
}
if len(s.links.queue) > 0 {
sd.links = s.interfaceArrayToLinksArray()
sd.links = s.links.copy()
sd.droppedLinkCount = s.links.droppedCount
}
return &sd
}
func (s *recordingSpan) interfaceArrayToLinksArray() []Link {
linkArr := make([]Link, 0)
for _, value := range s.links.queue {
linkArr = append(linkArr, value.(Link))
}
return linkArr
}
func (s *recordingSpan) interfaceArrayToEventArray() []Event {
eventArr := make([]Event, 0)
for _, value := range s.events.queue {
eventArr = append(eventArr, value.(Event))
}
return eventArr
}
func (s *recordingSpan) addChild() {
if !s.IsRecording() {
if s == nil {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if !s.isRecording() {
return
}
s.childSpanCount++
s.mu.Unlock()
}
func (*recordingSpan) private() {}

View File

@@ -132,8 +132,8 @@ func (tr *tracer) newRecordingSpan(psc, sc trace.SpanContext, name string, sr Sa
spanKind: trace.ValidateSpanKind(config.SpanKind()),
name: name,
startTime: startTime,
events: newEvictedQueue(tr.provider.spanLimits.EventCountLimit),
links: newEvictedQueue(tr.provider.spanLimits.LinkCountLimit),
events: newEvictedQueueEvent(tr.provider.spanLimits.EventCountLimit),
links: newEvictedQueueLink(tr.provider.spanLimits.LinkCountLimit),
tracer: tr,
}

View File

@@ -5,5 +5,5 @@ package sdk // import "go.opentelemetry.io/otel/sdk"
// Version is the current release version of the OpenTelemetry SDK in use.
func Version() string {
return "1.26.0"
return "1.35.0"
}

View File

@@ -115,7 +115,7 @@ func hostIPNamePort(hostWithPort string) (ip string, name string, port int) {
name = hostPart
}
if parsedPort, err = strconv.ParseUint(portPart, 10, 16); err == nil {
port = int(parsedPort)
port = int(parsedPort) // nolint: gosec // Bit size of 16 checked above.
}
return
}

View File

@@ -1,3 +0,0 @@
# Semconv v1.24.0
[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/semconv/v1.24.0)](https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.24.0)

File diff suppressed because it is too large Load Diff

View File

@@ -1,200 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Code generated from semantic convention specification. DO NOT EDIT.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.24.0"
import "go.opentelemetry.io/otel/attribute"
// This event represents an occurrence of a lifecycle transition on the iOS
// platform.
const (
// IosStateKey is the attribute Key conforming to the "ios.state" semantic
// conventions. It represents the this attribute represents the state the
// application has transitioned into at the occurrence of the event.
//
// Type: Enum
// RequirementLevel: Required
// Stability: experimental
// Note: The iOS lifecycle states are defined in the [UIApplicationDelegate
// documentation](https://developer.apple.com/documentation/uikit/uiapplicationdelegate#1656902),
// and from which the `OS terminology` column values are derived.
IosStateKey = attribute.Key("ios.state")
)
var (
// The app has become `active`. Associated with UIKit notification `applicationDidBecomeActive`
IosStateActive = IosStateKey.String("active")
// The app is now `inactive`. Associated with UIKit notification `applicationWillResignActive`
IosStateInactive = IosStateKey.String("inactive")
// The app is now in the background. This value is associated with UIKit notification `applicationDidEnterBackground`
IosStateBackground = IosStateKey.String("background")
// The app is now in the foreground. This value is associated with UIKit notification `applicationWillEnterForeground`
IosStateForeground = IosStateKey.String("foreground")
// The app is about to terminate. Associated with UIKit notification `applicationWillTerminate`
IosStateTerminate = IosStateKey.String("terminate")
)
// This event represents an occurrence of a lifecycle transition on the Android
// platform.
const (
// AndroidStateKey is the attribute Key conforming to the "android.state"
// semantic conventions. It represents the this attribute represents the
// state the application has transitioned into at the occurrence of the
// event.
//
// Type: Enum
// RequirementLevel: Required
// Stability: experimental
// Note: The Android lifecycle states are defined in [Activity lifecycle
// callbacks](https://developer.android.com/guide/components/activities/activity-lifecycle#lc),
// and from which the `OS identifiers` are derived.
AndroidStateKey = attribute.Key("android.state")
)
var (
// Any time before Activity.onResume() or, if the app has no Activity, Context.startService() has been called in the app for the first time
AndroidStateCreated = AndroidStateKey.String("created")
// Any time after Activity.onPause() or, if the app has no Activity, Context.stopService() has been called when the app was in the foreground state
AndroidStateBackground = AndroidStateKey.String("background")
// Any time after Activity.onResume() or, if the app has no Activity, Context.startService() has been called when the app was in either the created or background states
AndroidStateForeground = AndroidStateKey.String("foreground")
)
// This semantic convention defines the attributes used to represent a feature
// flag evaluation as an event.
const (
// FeatureFlagKeyKey is the attribute Key conforming to the
// "feature_flag.key" semantic conventions. It represents the unique
// identifier of the feature flag.
//
// Type: string
// RequirementLevel: Required
// Stability: experimental
// Examples: 'logo-color'
FeatureFlagKeyKey = attribute.Key("feature_flag.key")
// FeatureFlagProviderNameKey is the attribute Key conforming to the
// "feature_flag.provider_name" semantic conventions. It represents the
// name of the service provider that performs the flag evaluation.
//
// Type: string
// RequirementLevel: Recommended
// Stability: experimental
// Examples: 'Flag Manager'
FeatureFlagProviderNameKey = attribute.Key("feature_flag.provider_name")
// FeatureFlagVariantKey is the attribute Key conforming to the
// "feature_flag.variant" semantic conventions. It represents the sHOULD be
// a semantic identifier for a value. If one is unavailable, a stringified
// version of the value can be used.
//
// Type: string
// RequirementLevel: Recommended
// Stability: experimental
// Examples: 'red', 'true', 'on'
// Note: A semantic identifier, commonly referred to as a variant, provides
// a means
// for referring to a value without including the value itself. This can
// provide additional context for understanding the meaning behind a value.
// For example, the variant `red` maybe be used for the value `#c05543`.
//
// A stringified version of the value can be used in situations where a
// semantic identifier is unavailable. String representation of the value
// should be determined by the implementer.
FeatureFlagVariantKey = attribute.Key("feature_flag.variant")
)
// FeatureFlagKey returns an attribute KeyValue conforming to the
// "feature_flag.key" semantic conventions. It represents the unique identifier
// of the feature flag.
func FeatureFlagKey(val string) attribute.KeyValue {
return FeatureFlagKeyKey.String(val)
}
// FeatureFlagProviderName returns an attribute KeyValue conforming to the
// "feature_flag.provider_name" semantic conventions. It represents the name of
// the service provider that performs the flag evaluation.
func FeatureFlagProviderName(val string) attribute.KeyValue {
return FeatureFlagProviderNameKey.String(val)
}
// FeatureFlagVariant returns an attribute KeyValue conforming to the
// "feature_flag.variant" semantic conventions. It represents the sHOULD be a
// semantic identifier for a value. If one is unavailable, a stringified
// version of the value can be used.
func FeatureFlagVariant(val string) attribute.KeyValue {
return FeatureFlagVariantKey.String(val)
}
// RPC received/sent message.
const (
// MessageCompressedSizeKey is the attribute Key conforming to the
// "message.compressed_size" semantic conventions. It represents the
// compressed size of the message in bytes.
//
// Type: int
// RequirementLevel: Optional
// Stability: experimental
MessageCompressedSizeKey = attribute.Key("message.compressed_size")
// MessageIDKey is the attribute Key conforming to the "message.id"
// semantic conventions. It represents the mUST be calculated as two
// different counters starting from `1` one for sent messages and one for
// received message.
//
// Type: int
// RequirementLevel: Optional
// Stability: experimental
// Note: This way we guarantee that the values will be consistent between
// different implementations.
MessageIDKey = attribute.Key("message.id")
// MessageTypeKey is the attribute Key conforming to the "message.type"
// semantic conventions. It represents the whether this is a received or
// sent message.
//
// Type: Enum
// RequirementLevel: Optional
// Stability: experimental
MessageTypeKey = attribute.Key("message.type")
// MessageUncompressedSizeKey is the attribute Key conforming to the
// "message.uncompressed_size" semantic conventions. It represents the
// uncompressed size of the message in bytes.
//
// Type: int
// RequirementLevel: Optional
// Stability: experimental
MessageUncompressedSizeKey = attribute.Key("message.uncompressed_size")
)
var (
// sent
MessageTypeSent = MessageTypeKey.String("SENT")
// received
MessageTypeReceived = MessageTypeKey.String("RECEIVED")
)
// MessageCompressedSize returns an attribute KeyValue conforming to the
// "message.compressed_size" semantic conventions. It represents the compressed
// size of the message in bytes.
func MessageCompressedSize(val int) attribute.KeyValue {
return MessageCompressedSizeKey.Int(val)
}
// MessageID returns an attribute KeyValue conforming to the "message.id"
// semantic conventions. It represents the mUST be calculated as two different
// counters starting from `1` one for sent messages and one for received
// message.
func MessageID(val int) attribute.KeyValue {
return MessageIDKey.Int(val)
}
// MessageUncompressedSize returns an attribute KeyValue conforming to the
// "message.uncompressed_size" semantic conventions. It represents the
// uncompressed size of the message in bytes.
func MessageUncompressedSize(val int) attribute.KeyValue {
return MessageUncompressedSizeKey.Int(val)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
# Semconv v1.26.0
[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/semconv/v1.26.0)](https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.26.0)

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,6 @@
// Package semconv implements OpenTelemetry semantic conventions.
//
// OpenTelemetry semantic conventions are agreed standardized naming
// patterns for OpenTelemetry things. This package represents the v1.24.0
// patterns for OpenTelemetry things. This package represents the v1.26.0
// version of the OpenTelemetry semantic conventions.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.24.0"
package semconv // import "go.opentelemetry.io/otel/semconv/v1.26.0"

View File

@@ -1,7 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package semconv // import "go.opentelemetry.io/otel/semconv/v1.24.0"
package semconv // import "go.opentelemetry.io/otel/semconv/v1.26.0"
const (
// ExceptionEventName is the name of the Span event representing an exception.

View File

@@ -3,176 +3,242 @@
// Code generated from semantic convention specification. DO NOT EDIT.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.24.0"
package semconv // import "go.opentelemetry.io/otel/semconv/v1.26.0"
const (
// DBClientConnectionsUsage is the metric conforming to the
// "db.client.connections.usage" semantic conventions. It represents the number
// ContainerCPUTime is the metric conforming to the "container.cpu.time"
// semantic conventions. It represents the total CPU time consumed.
// Instrument: counter
// Unit: s
// Stability: Experimental
ContainerCPUTimeName = "container.cpu.time"
ContainerCPUTimeUnit = "s"
ContainerCPUTimeDescription = "Total CPU time consumed"
// ContainerMemoryUsage is the metric conforming to the
// "container.memory.usage" semantic conventions. It represents the memory
// usage of the container.
// Instrument: counter
// Unit: By
// Stability: Experimental
ContainerMemoryUsageName = "container.memory.usage"
ContainerMemoryUsageUnit = "By"
ContainerMemoryUsageDescription = "Memory usage of the container."
// ContainerDiskIo is the metric conforming to the "container.disk.io" semantic
// conventions. It represents the disk bytes for the container.
// Instrument: counter
// Unit: By
// Stability: Experimental
ContainerDiskIoName = "container.disk.io"
ContainerDiskIoUnit = "By"
ContainerDiskIoDescription = "Disk bytes for the container."
// ContainerNetworkIo is the metric conforming to the "container.network.io"
// semantic conventions. It represents the network bytes for the container.
// Instrument: counter
// Unit: By
// Stability: Experimental
ContainerNetworkIoName = "container.network.io"
ContainerNetworkIoUnit = "By"
ContainerNetworkIoDescription = "Network bytes for the container."
// DBClientOperationDuration is the metric conforming to the
// "db.client.operation.duration" semantic conventions. It represents the
// duration of database client operations.
// Instrument: histogram
// Unit: s
// Stability: Experimental
DBClientOperationDurationName = "db.client.operation.duration"
DBClientOperationDurationUnit = "s"
DBClientOperationDurationDescription = "Duration of database client operations."
// DBClientConnectionCount is the metric conforming to the
// "db.client.connection.count" semantic conventions. It represents the number
// of connections that are currently in state described by the `state`
// attribute.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
DBClientConnectionsUsageName = "db.client.connections.usage"
DBClientConnectionsUsageUnit = "{connection}"
DBClientConnectionsUsageDescription = "The number of connections that are currently in state described by the `state` attribute"
DBClientConnectionCountName = "db.client.connection.count"
DBClientConnectionCountUnit = "{connection}"
DBClientConnectionCountDescription = "The number of connections that are currently in state described by the `state` attribute"
// DBClientConnectionsIdleMax is the metric conforming to the
// "db.client.connections.idle.max" semantic conventions. It represents the
// DBClientConnectionIdleMax is the metric conforming to the
// "db.client.connection.idle.max" semantic conventions. It represents the
// maximum number of idle open connections allowed.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
DBClientConnectionsIdleMaxName = "db.client.connections.idle.max"
DBClientConnectionsIdleMaxUnit = "{connection}"
DBClientConnectionsIdleMaxDescription = "The maximum number of idle open connections allowed"
DBClientConnectionIdleMaxName = "db.client.connection.idle.max"
DBClientConnectionIdleMaxUnit = "{connection}"
DBClientConnectionIdleMaxDescription = "The maximum number of idle open connections allowed"
// DBClientConnectionsIdleMin is the metric conforming to the
// "db.client.connections.idle.min" semantic conventions. It represents the
// DBClientConnectionIdleMin is the metric conforming to the
// "db.client.connection.idle.min" semantic conventions. It represents the
// minimum number of idle open connections allowed.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
DBClientConnectionsIdleMinName = "db.client.connections.idle.min"
DBClientConnectionsIdleMinUnit = "{connection}"
DBClientConnectionsIdleMinDescription = "The minimum number of idle open connections allowed"
DBClientConnectionIdleMinName = "db.client.connection.idle.min"
DBClientConnectionIdleMinUnit = "{connection}"
DBClientConnectionIdleMinDescription = "The minimum number of idle open connections allowed"
// DBClientConnectionsMax is the metric conforming to the
// "db.client.connections.max" semantic conventions. It represents the maximum
// DBClientConnectionMax is the metric conforming to the
// "db.client.connection.max" semantic conventions. It represents the maximum
// number of open connections allowed.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
DBClientConnectionsMaxName = "db.client.connections.max"
DBClientConnectionsMaxUnit = "{connection}"
DBClientConnectionsMaxDescription = "The maximum number of open connections allowed"
DBClientConnectionMaxName = "db.client.connection.max"
DBClientConnectionMaxUnit = "{connection}"
DBClientConnectionMaxDescription = "The maximum number of open connections allowed"
// DBClientConnectionsPendingRequests is the metric conforming to the
// "db.client.connections.pending_requests" semantic conventions. It represents
// DBClientConnectionPendingRequests is the metric conforming to the
// "db.client.connection.pending_requests" semantic conventions. It represents
// the number of pending requests for an open connection, cumulative for the
// entire pool.
// Instrument: updowncounter
// Unit: {request}
// Stability: Experimental
DBClientConnectionsPendingRequestsName = "db.client.connections.pending_requests"
DBClientConnectionsPendingRequestsUnit = "{request}"
DBClientConnectionsPendingRequestsDescription = "The number of pending requests for an open connection, cumulative for the entire pool"
DBClientConnectionPendingRequestsName = "db.client.connection.pending_requests"
DBClientConnectionPendingRequestsUnit = "{request}"
DBClientConnectionPendingRequestsDescription = "The number of pending requests for an open connection, cumulative for the entire pool"
// DBClientConnectionsTimeouts is the metric conforming to the
// "db.client.connections.timeouts" semantic conventions. It represents the
// DBClientConnectionTimeouts is the metric conforming to the
// "db.client.connection.timeouts" semantic conventions. It represents the
// number of connection timeouts that have occurred trying to obtain a
// connection from the pool.
// Instrument: counter
// Unit: {timeout}
// Stability: Experimental
DBClientConnectionTimeoutsName = "db.client.connection.timeouts"
DBClientConnectionTimeoutsUnit = "{timeout}"
DBClientConnectionTimeoutsDescription = "The number of connection timeouts that have occurred trying to obtain a connection from the pool"
// DBClientConnectionCreateTime is the metric conforming to the
// "db.client.connection.create_time" semantic conventions. It represents the
// time it took to create a new connection.
// Instrument: histogram
// Unit: s
// Stability: Experimental
DBClientConnectionCreateTimeName = "db.client.connection.create_time"
DBClientConnectionCreateTimeUnit = "s"
DBClientConnectionCreateTimeDescription = "The time it took to create a new connection"
// DBClientConnectionWaitTime is the metric conforming to the
// "db.client.connection.wait_time" semantic conventions. It represents the
// time it took to obtain an open connection from the pool.
// Instrument: histogram
// Unit: s
// Stability: Experimental
DBClientConnectionWaitTimeName = "db.client.connection.wait_time"
DBClientConnectionWaitTimeUnit = "s"
DBClientConnectionWaitTimeDescription = "The time it took to obtain an open connection from the pool"
// DBClientConnectionUseTime is the metric conforming to the
// "db.client.connection.use_time" semantic conventions. It represents the time
// between borrowing a connection and returning it to the pool.
// Instrument: histogram
// Unit: s
// Stability: Experimental
DBClientConnectionUseTimeName = "db.client.connection.use_time"
DBClientConnectionUseTimeUnit = "s"
DBClientConnectionUseTimeDescription = "The time between borrowing a connection and returning it to the pool"
// DBClientConnectionsUsage is the metric conforming to the
// "db.client.connections.usage" semantic conventions. It represents the
// deprecated, use `db.client.connection.count` instead.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
DBClientConnectionsUsageName = "db.client.connections.usage"
DBClientConnectionsUsageUnit = "{connection}"
DBClientConnectionsUsageDescription = "Deprecated, use `db.client.connection.count` instead."
// DBClientConnectionsIdleMax is the metric conforming to the
// "db.client.connections.idle.max" semantic conventions. It represents the
// deprecated, use `db.client.connection.idle.max` instead.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
DBClientConnectionsIdleMaxName = "db.client.connections.idle.max"
DBClientConnectionsIdleMaxUnit = "{connection}"
DBClientConnectionsIdleMaxDescription = "Deprecated, use `db.client.connection.idle.max` instead."
// DBClientConnectionsIdleMin is the metric conforming to the
// "db.client.connections.idle.min" semantic conventions. It represents the
// deprecated, use `db.client.connection.idle.min` instead.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
DBClientConnectionsIdleMinName = "db.client.connections.idle.min"
DBClientConnectionsIdleMinUnit = "{connection}"
DBClientConnectionsIdleMinDescription = "Deprecated, use `db.client.connection.idle.min` instead."
// DBClientConnectionsMax is the metric conforming to the
// "db.client.connections.max" semantic conventions. It represents the
// deprecated, use `db.client.connection.max` instead.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
DBClientConnectionsMaxName = "db.client.connections.max"
DBClientConnectionsMaxUnit = "{connection}"
DBClientConnectionsMaxDescription = "Deprecated, use `db.client.connection.max` instead."
// DBClientConnectionsPendingRequests is the metric conforming to the
// "db.client.connections.pending_requests" semantic conventions. It represents
// the deprecated, use `db.client.connection.pending_requests` instead.
// Instrument: updowncounter
// Unit: {request}
// Stability: Experimental
DBClientConnectionsPendingRequestsName = "db.client.connections.pending_requests"
DBClientConnectionsPendingRequestsUnit = "{request}"
DBClientConnectionsPendingRequestsDescription = "Deprecated, use `db.client.connection.pending_requests` instead."
// DBClientConnectionsTimeouts is the metric conforming to the
// "db.client.connections.timeouts" semantic conventions. It represents the
// deprecated, use `db.client.connection.timeouts` instead.
// Instrument: counter
// Unit: {timeout}
// Stability: Experimental
DBClientConnectionsTimeoutsName = "db.client.connections.timeouts"
DBClientConnectionsTimeoutsUnit = "{timeout}"
DBClientConnectionsTimeoutsDescription = "The number of connection timeouts that have occurred trying to obtain a connection from the pool"
DBClientConnectionsTimeoutsDescription = "Deprecated, use `db.client.connection.timeouts` instead."
// DBClientConnectionsCreateTime is the metric conforming to the
// "db.client.connections.create_time" semantic conventions. It represents the
// time it took to create a new connection.
// deprecated, use `db.client.connection.create_time` instead. Note: the unit
// also changed from `ms` to `s`.
// Instrument: histogram
// Unit: ms
// Stability: Experimental
DBClientConnectionsCreateTimeName = "db.client.connections.create_time"
DBClientConnectionsCreateTimeUnit = "ms"
DBClientConnectionsCreateTimeDescription = "The time it took to create a new connection"
DBClientConnectionsCreateTimeDescription = "Deprecated, use `db.client.connection.create_time` instead. Note: the unit also changed from `ms` to `s`."
// DBClientConnectionsWaitTime is the metric conforming to the
// "db.client.connections.wait_time" semantic conventions. It represents the
// time it took to obtain an open connection from the pool.
// deprecated, use `db.client.connection.wait_time` instead. Note: the unit
// also changed from `ms` to `s`.
// Instrument: histogram
// Unit: ms
// Stability: Experimental
DBClientConnectionsWaitTimeName = "db.client.connections.wait_time"
DBClientConnectionsWaitTimeUnit = "ms"
DBClientConnectionsWaitTimeDescription = "The time it took to obtain an open connection from the pool"
DBClientConnectionsWaitTimeDescription = "Deprecated, use `db.client.connection.wait_time` instead. Note: the unit also changed from `ms` to `s`."
// DBClientConnectionsUseTime is the metric conforming to the
// "db.client.connections.use_time" semantic conventions. It represents the
// time between borrowing a connection and returning it to the pool.
// deprecated, use `db.client.connection.use_time` instead. Note: the unit also
// changed from `ms` to `s`.
// Instrument: histogram
// Unit: ms
// Stability: Experimental
DBClientConnectionsUseTimeName = "db.client.connections.use_time"
DBClientConnectionsUseTimeUnit = "ms"
DBClientConnectionsUseTimeDescription = "The time between borrowing a connection and returning it to the pool"
// AspnetcoreRoutingMatchAttempts is the metric conforming to the
// "aspnetcore.routing.match_attempts" semantic conventions. It represents the
// number of requests that were attempted to be matched to an endpoint.
// Instrument: counter
// Unit: {match_attempt}
// Stability: Experimental
AspnetcoreRoutingMatchAttemptsName = "aspnetcore.routing.match_attempts"
AspnetcoreRoutingMatchAttemptsUnit = "{match_attempt}"
AspnetcoreRoutingMatchAttemptsDescription = "Number of requests that were attempted to be matched to an endpoint."
// AspnetcoreDiagnosticsExceptions is the metric conforming to the
// "aspnetcore.diagnostics.exceptions" semantic conventions. It represents the
// number of exceptions caught by exception handling middleware.
// Instrument: counter
// Unit: {exception}
// Stability: Experimental
AspnetcoreDiagnosticsExceptionsName = "aspnetcore.diagnostics.exceptions"
AspnetcoreDiagnosticsExceptionsUnit = "{exception}"
AspnetcoreDiagnosticsExceptionsDescription = "Number of exceptions caught by exception handling middleware."
// AspnetcoreRateLimitingActiveRequestLeases is the metric conforming to the
// "aspnetcore.rate_limiting.active_request_leases" semantic conventions. It
// represents the number of requests that are currently active on the server
// that hold a rate limiting lease.
// Instrument: updowncounter
// Unit: {request}
// Stability: Experimental
AspnetcoreRateLimitingActiveRequestLeasesName = "aspnetcore.rate_limiting.active_request_leases"
AspnetcoreRateLimitingActiveRequestLeasesUnit = "{request}"
AspnetcoreRateLimitingActiveRequestLeasesDescription = "Number of requests that are currently active on the server that hold a rate limiting lease."
// AspnetcoreRateLimitingRequestLeaseDuration is the metric conforming to the
// "aspnetcore.rate_limiting.request_lease.duration" semantic conventions. It
// represents the duration of rate limiting lease held by requests on the
// server.
// Instrument: histogram
// Unit: s
// Stability: Experimental
AspnetcoreRateLimitingRequestLeaseDurationName = "aspnetcore.rate_limiting.request_lease.duration"
AspnetcoreRateLimitingRequestLeaseDurationUnit = "s"
AspnetcoreRateLimitingRequestLeaseDurationDescription = "The duration of rate limiting lease held by requests on the server."
// AspnetcoreRateLimitingRequestTimeInQueue is the metric conforming to the
// "aspnetcore.rate_limiting.request.time_in_queue" semantic conventions. It
// represents the time the request spent in a queue waiting to acquire a rate
// limiting lease.
// Instrument: histogram
// Unit: s
// Stability: Experimental
AspnetcoreRateLimitingRequestTimeInQueueName = "aspnetcore.rate_limiting.request.time_in_queue"
AspnetcoreRateLimitingRequestTimeInQueueUnit = "s"
AspnetcoreRateLimitingRequestTimeInQueueDescription = "The time the request spent in a queue waiting to acquire a rate limiting lease."
// AspnetcoreRateLimitingQueuedRequests is the metric conforming to the
// "aspnetcore.rate_limiting.queued_requests" semantic conventions. It
// represents the number of requests that are currently queued, waiting to
// acquire a rate limiting lease.
// Instrument: updowncounter
// Unit: {request}
// Stability: Experimental
AspnetcoreRateLimitingQueuedRequestsName = "aspnetcore.rate_limiting.queued_requests"
AspnetcoreRateLimitingQueuedRequestsUnit = "{request}"
AspnetcoreRateLimitingQueuedRequestsDescription = "Number of requests that are currently queued, waiting to acquire a rate limiting lease."
// AspnetcoreRateLimitingRequests is the metric conforming to the
// "aspnetcore.rate_limiting.requests" semantic conventions. It represents the
// number of requests that tried to acquire a rate limiting lease.
// Instrument: counter
// Unit: {request}
// Stability: Experimental
AspnetcoreRateLimitingRequestsName = "aspnetcore.rate_limiting.requests"
AspnetcoreRateLimitingRequestsUnit = "{request}"
AspnetcoreRateLimitingRequestsDescription = "Number of requests that tried to acquire a rate limiting lease."
DBClientConnectionsUseTimeDescription = "Deprecated, use `db.client.connection.use_time` instead. Note: the unit also changed from `ms` to `s`."
// DNSLookupDuration is the metric conforming to the "dns.lookup.duration"
// semantic conventions. It represents the measures the time taken to perform a
@@ -184,54 +250,86 @@ const (
DNSLookupDurationUnit = "s"
DNSLookupDurationDescription = "Measures the time taken to perform a DNS lookup."
// HTTPClientOpenConnections is the metric conforming to the
// "http.client.open_connections" semantic conventions. It represents the
// number of outbound HTTP connections that are currently active or idle on the
// client.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
HTTPClientOpenConnectionsName = "http.client.open_connections"
HTTPClientOpenConnectionsUnit = "{connection}"
HTTPClientOpenConnectionsDescription = "Number of outbound HTTP connections that are currently active or idle on the client."
// AspnetcoreRoutingMatchAttempts is the metric conforming to the
// "aspnetcore.routing.match_attempts" semantic conventions. It represents the
// number of requests that were attempted to be matched to an endpoint.
// Instrument: counter
// Unit: {match_attempt}
// Stability: Stable
AspnetcoreRoutingMatchAttemptsName = "aspnetcore.routing.match_attempts"
AspnetcoreRoutingMatchAttemptsUnit = "{match_attempt}"
AspnetcoreRoutingMatchAttemptsDescription = "Number of requests that were attempted to be matched to an endpoint."
// HTTPClientConnectionDuration is the metric conforming to the
// "http.client.connection.duration" semantic conventions. It represents the
// duration of the successfully established outbound HTTP connections.
// Instrument: histogram
// Unit: s
// Stability: Experimental
HTTPClientConnectionDurationName = "http.client.connection.duration"
HTTPClientConnectionDurationUnit = "s"
HTTPClientConnectionDurationDescription = "The duration of the successfully established outbound HTTP connections."
// AspnetcoreDiagnosticsExceptions is the metric conforming to the
// "aspnetcore.diagnostics.exceptions" semantic conventions. It represents the
// number of exceptions caught by exception handling middleware.
// Instrument: counter
// Unit: {exception}
// Stability: Stable
AspnetcoreDiagnosticsExceptionsName = "aspnetcore.diagnostics.exceptions"
AspnetcoreDiagnosticsExceptionsUnit = "{exception}"
AspnetcoreDiagnosticsExceptionsDescription = "Number of exceptions caught by exception handling middleware."
// HTTPClientActiveRequests is the metric conforming to the
// "http.client.active_requests" semantic conventions. It represents the number
// of active HTTP requests.
// AspnetcoreRateLimitingActiveRequestLeases is the metric conforming to the
// "aspnetcore.rate_limiting.active_request_leases" semantic conventions. It
// represents the number of requests that are currently active on the server
// that hold a rate limiting lease.
// Instrument: updowncounter
// Unit: {request}
// Stability: Experimental
HTTPClientActiveRequestsName = "http.client.active_requests"
HTTPClientActiveRequestsUnit = "{request}"
HTTPClientActiveRequestsDescription = "Number of active HTTP requests."
// Stability: Stable
AspnetcoreRateLimitingActiveRequestLeasesName = "aspnetcore.rate_limiting.active_request_leases"
AspnetcoreRateLimitingActiveRequestLeasesUnit = "{request}"
AspnetcoreRateLimitingActiveRequestLeasesDescription = "Number of requests that are currently active on the server that hold a rate limiting lease."
// HTTPClientRequestTimeInQueue is the metric conforming to the
// "http.client.request.time_in_queue" semantic conventions. It represents the
// amount of time requests spent on a queue waiting for an available
// connection.
// AspnetcoreRateLimitingRequestLeaseDuration is the metric conforming to the
// "aspnetcore.rate_limiting.request_lease.duration" semantic conventions. It
// represents the duration of rate limiting lease held by requests on the
// server.
// Instrument: histogram
// Unit: s
// Stability: Experimental
HTTPClientRequestTimeInQueueName = "http.client.request.time_in_queue"
HTTPClientRequestTimeInQueueUnit = "s"
HTTPClientRequestTimeInQueueDescription = "The amount of time requests spent on a queue waiting for an available connection."
// Stability: Stable
AspnetcoreRateLimitingRequestLeaseDurationName = "aspnetcore.rate_limiting.request_lease.duration"
AspnetcoreRateLimitingRequestLeaseDurationUnit = "s"
AspnetcoreRateLimitingRequestLeaseDurationDescription = "The duration of rate limiting lease held by requests on the server."
// AspnetcoreRateLimitingRequestTimeInQueue is the metric conforming to the
// "aspnetcore.rate_limiting.request.time_in_queue" semantic conventions. It
// represents the time the request spent in a queue waiting to acquire a rate
// limiting lease.
// Instrument: histogram
// Unit: s
// Stability: Stable
AspnetcoreRateLimitingRequestTimeInQueueName = "aspnetcore.rate_limiting.request.time_in_queue"
AspnetcoreRateLimitingRequestTimeInQueueUnit = "s"
AspnetcoreRateLimitingRequestTimeInQueueDescription = "The time the request spent in a queue waiting to acquire a rate limiting lease."
// AspnetcoreRateLimitingQueuedRequests is the metric conforming to the
// "aspnetcore.rate_limiting.queued_requests" semantic conventions. It
// represents the number of requests that are currently queued, waiting to
// acquire a rate limiting lease.
// Instrument: updowncounter
// Unit: {request}
// Stability: Stable
AspnetcoreRateLimitingQueuedRequestsName = "aspnetcore.rate_limiting.queued_requests"
AspnetcoreRateLimitingQueuedRequestsUnit = "{request}"
AspnetcoreRateLimitingQueuedRequestsDescription = "Number of requests that are currently queued, waiting to acquire a rate limiting lease."
// AspnetcoreRateLimitingRequests is the metric conforming to the
// "aspnetcore.rate_limiting.requests" semantic conventions. It represents the
// number of requests that tried to acquire a rate limiting lease.
// Instrument: counter
// Unit: {request}
// Stability: Stable
AspnetcoreRateLimitingRequestsName = "aspnetcore.rate_limiting.requests"
AspnetcoreRateLimitingRequestsUnit = "{request}"
AspnetcoreRateLimitingRequestsDescription = "Number of requests that tried to acquire a rate limiting lease."
// KestrelActiveConnections is the metric conforming to the
// "kestrel.active_connections" semantic conventions. It represents the number
// of connections that are currently active on the server.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
// Stability: Stable
KestrelActiveConnectionsName = "kestrel.active_connections"
KestrelActiveConnectionsUnit = "{connection}"
KestrelActiveConnectionsDescription = "Number of connections that are currently active on the server."
@@ -241,7 +339,7 @@ const (
// duration of connections on the server.
// Instrument: histogram
// Unit: s
// Stability: Experimental
// Stability: Stable
KestrelConnectionDurationName = "kestrel.connection.duration"
KestrelConnectionDurationUnit = "s"
KestrelConnectionDurationDescription = "The duration of connections on the server."
@@ -251,7 +349,7 @@ const (
// number of connections rejected by the server.
// Instrument: counter
// Unit: {connection}
// Stability: Experimental
// Stability: Stable
KestrelRejectedConnectionsName = "kestrel.rejected_connections"
KestrelRejectedConnectionsUnit = "{connection}"
KestrelRejectedConnectionsDescription = "Number of connections rejected by the server."
@@ -261,7 +359,7 @@ const (
// of connections that are currently queued and are waiting to start.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
// Stability: Stable
KestrelQueuedConnectionsName = "kestrel.queued_connections"
KestrelQueuedConnectionsUnit = "{connection}"
KestrelQueuedConnectionsDescription = "Number of connections that are currently queued and are waiting to start."
@@ -272,7 +370,7 @@ const (
// currently queued and are waiting to start.
// Instrument: updowncounter
// Unit: {request}
// Stability: Experimental
// Stability: Stable
KestrelQueuedRequestsName = "kestrel.queued_requests"
KestrelQueuedRequestsUnit = "{request}"
KestrelQueuedRequestsDescription = "Number of HTTP requests on multiplexed connections (HTTP/2 and HTTP/3) that are currently queued and are waiting to start."
@@ -282,7 +380,7 @@ const (
// number of connections that are currently upgraded (WebSockets). .
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
// Stability: Stable
KestrelUpgradedConnectionsName = "kestrel.upgraded_connections"
KestrelUpgradedConnectionsUnit = "{connection}"
KestrelUpgradedConnectionsDescription = "Number of connections that are currently upgraded (WebSockets). ."
@@ -292,7 +390,7 @@ const (
// duration of TLS handshakes on the server.
// Instrument: histogram
// Unit: s
// Stability: Experimental
// Stability: Stable
KestrelTLSHandshakeDurationName = "kestrel.tls_handshake.duration"
KestrelTLSHandshakeDurationUnit = "s"
KestrelTLSHandshakeDurationDescription = "The duration of TLS handshakes on the server."
@@ -302,7 +400,7 @@ const (
// number of TLS handshakes that are currently in progress on the server.
// Instrument: updowncounter
// Unit: {handshake}
// Stability: Experimental
// Stability: Stable
KestrelActiveTLSHandshakesName = "kestrel.active_tls_handshakes"
KestrelActiveTLSHandshakesUnit = "{handshake}"
KestrelActiveTLSHandshakesDescription = "Number of TLS handshakes that are currently in progress on the server."
@@ -312,7 +410,7 @@ const (
// duration of connections on the server.
// Instrument: histogram
// Unit: s
// Stability: Experimental
// Stability: Stable
SignalrServerConnectionDurationName = "signalr.server.connection.duration"
SignalrServerConnectionDurationUnit = "s"
SignalrServerConnectionDurationDescription = "The duration of connections on the server."
@@ -322,7 +420,7 @@ const (
// number of connections that are currently active on the server.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
// Stability: Stable
SignalrServerActiveConnectionsName = "signalr.server.active_connections"
SignalrServerActiveConnectionsUnit = "{connection}"
SignalrServerActiveConnectionsDescription = "Number of connections that are currently active on the server."
@@ -481,6 +579,37 @@ const (
HTTPClientResponseBodySizeUnit = "By"
HTTPClientResponseBodySizeDescription = "Size of HTTP client response bodies."
// HTTPClientOpenConnections is the metric conforming to the
// "http.client.open_connections" semantic conventions. It represents the
// number of outbound HTTP connections that are currently active or idle on the
// client.
// Instrument: updowncounter
// Unit: {connection}
// Stability: Experimental
HTTPClientOpenConnectionsName = "http.client.open_connections"
HTTPClientOpenConnectionsUnit = "{connection}"
HTTPClientOpenConnectionsDescription = "Number of outbound HTTP connections that are currently active or idle on the client."
// HTTPClientConnectionDuration is the metric conforming to the
// "http.client.connection.duration" semantic conventions. It represents the
// duration of the successfully established outbound HTTP connections.
// Instrument: histogram
// Unit: s
// Stability: Experimental
HTTPClientConnectionDurationName = "http.client.connection.duration"
HTTPClientConnectionDurationUnit = "s"
HTTPClientConnectionDurationDescription = "The duration of the successfully established outbound HTTP connections."
// HTTPClientActiveRequests is the metric conforming to the
// "http.client.active_requests" semantic conventions. It represents the number
// of active HTTP requests.
// Instrument: updowncounter
// Unit: {request}
// Stability: Experimental
HTTPClientActiveRequestsName = "http.client.active_requests"
HTTPClientActiveRequestsUnit = "{request}"
HTTPClientActiveRequestsDescription = "Number of active HTTP requests."
// JvmMemoryInit is the metric conforming to the "jvm.memory.init" semantic
// conventions. It represents the measure of initial memory requested.
// Instrument: updowncounter
@@ -673,15 +802,15 @@ const (
MessagingReceiveDurationUnit = "s"
MessagingReceiveDurationDescription = "Measures the duration of receive operation."
// MessagingDeliverDuration is the metric conforming to the
// "messaging.deliver.duration" semantic conventions. It represents the
// measures the duration of deliver operation.
// MessagingProcessDuration is the metric conforming to the
// "messaging.process.duration" semantic conventions. It represents the
// measures the duration of process operation.
// Instrument: histogram
// Unit: s
// Stability: Experimental
MessagingDeliverDurationName = "messaging.deliver.duration"
MessagingDeliverDurationUnit = "s"
MessagingDeliverDurationDescription = "Measures the duration of deliver operation."
MessagingProcessDurationName = "messaging.process.duration"
MessagingProcessDurationUnit = "s"
MessagingProcessDurationDescription = "Measures the duration of process operation."
// MessagingPublishMessages is the metric conforming to the
// "messaging.publish.messages" semantic conventions. It represents the
@@ -703,15 +832,112 @@ const (
MessagingReceiveMessagesUnit = "{message}"
MessagingReceiveMessagesDescription = "Measures the number of received messages."
// MessagingDeliverMessages is the metric conforming to the
// "messaging.deliver.messages" semantic conventions. It represents the
// measures the number of delivered messages.
// MessagingProcessMessages is the metric conforming to the
// "messaging.process.messages" semantic conventions. It represents the
// measures the number of processed messages.
// Instrument: counter
// Unit: {message}
// Stability: Experimental
MessagingDeliverMessagesName = "messaging.deliver.messages"
MessagingDeliverMessagesUnit = "{message}"
MessagingDeliverMessagesDescription = "Measures the number of delivered messages."
MessagingProcessMessagesName = "messaging.process.messages"
MessagingProcessMessagesUnit = "{message}"
MessagingProcessMessagesDescription = "Measures the number of processed messages."
// ProcessCPUTime is the metric conforming to the "process.cpu.time" semantic
// conventions. It represents the total CPU seconds broken down by different
// states.
// Instrument: counter
// Unit: s
// Stability: Experimental
ProcessCPUTimeName = "process.cpu.time"
ProcessCPUTimeUnit = "s"
ProcessCPUTimeDescription = "Total CPU seconds broken down by different states."
// ProcessCPUUtilization is the metric conforming to the
// "process.cpu.utilization" semantic conventions. It represents the difference
// in process.cpu.time since the last measurement, divided by the elapsed time
// and number of CPUs available to the process.
// Instrument: gauge
// Unit: 1
// Stability: Experimental
ProcessCPUUtilizationName = "process.cpu.utilization"
ProcessCPUUtilizationUnit = "1"
ProcessCPUUtilizationDescription = "Difference in process.cpu.time since the last measurement, divided by the elapsed time and number of CPUs available to the process."
// ProcessMemoryUsage is the metric conforming to the "process.memory.usage"
// semantic conventions. It represents the amount of physical memory in use.
// Instrument: updowncounter
// Unit: By
// Stability: Experimental
ProcessMemoryUsageName = "process.memory.usage"
ProcessMemoryUsageUnit = "By"
ProcessMemoryUsageDescription = "The amount of physical memory in use."
// ProcessMemoryVirtual is the metric conforming to the
// "process.memory.virtual" semantic conventions. It represents the amount of
// committed virtual memory.
// Instrument: updowncounter
// Unit: By
// Stability: Experimental
ProcessMemoryVirtualName = "process.memory.virtual"
ProcessMemoryVirtualUnit = "By"
ProcessMemoryVirtualDescription = "The amount of committed virtual memory."
// ProcessDiskIo is the metric conforming to the "process.disk.io" semantic
// conventions. It represents the disk bytes transferred.
// Instrument: counter
// Unit: By
// Stability: Experimental
ProcessDiskIoName = "process.disk.io"
ProcessDiskIoUnit = "By"
ProcessDiskIoDescription = "Disk bytes transferred."
// ProcessNetworkIo is the metric conforming to the "process.network.io"
// semantic conventions. It represents the network bytes transferred.
// Instrument: counter
// Unit: By
// Stability: Experimental
ProcessNetworkIoName = "process.network.io"
ProcessNetworkIoUnit = "By"
ProcessNetworkIoDescription = "Network bytes transferred."
// ProcessThreadCount is the metric conforming to the "process.thread.count"
// semantic conventions. It represents the process threads count.
// Instrument: updowncounter
// Unit: {thread}
// Stability: Experimental
ProcessThreadCountName = "process.thread.count"
ProcessThreadCountUnit = "{thread}"
ProcessThreadCountDescription = "Process threads count."
// ProcessOpenFileDescriptorCount is the metric conforming to the
// "process.open_file_descriptor.count" semantic conventions. It represents the
// number of file descriptors in use by the process.
// Instrument: updowncounter
// Unit: {count}
// Stability: Experimental
ProcessOpenFileDescriptorCountName = "process.open_file_descriptor.count"
ProcessOpenFileDescriptorCountUnit = "{count}"
ProcessOpenFileDescriptorCountDescription = "Number of file descriptors in use by the process."
// ProcessContextSwitches is the metric conforming to the
// "process.context_switches" semantic conventions. It represents the number of
// times the process has been context switched.
// Instrument: counter
// Unit: {count}
// Stability: Experimental
ProcessContextSwitchesName = "process.context_switches"
ProcessContextSwitchesUnit = "{count}"
ProcessContextSwitchesDescription = "Number of times the process has been context switched."
// ProcessPagingFaults is the metric conforming to the "process.paging.faults"
// semantic conventions. It represents the number of page faults the process
// has made.
// Instrument: counter
// Unit: {fault}
// Stability: Experimental
ProcessPagingFaultsName = "process.paging.faults"
ProcessPagingFaultsUnit = "{fault}"
ProcessPagingFaultsDescription = "Number of page faults the process has made."
// RPCServerDuration is the metric conforming to the "rpc.server.duration"
// semantic conventions. It represents the measures the duration of inbound
@@ -883,6 +1109,16 @@ const (
SystemMemoryLimitUnit = "By"
SystemMemoryLimitDescription = "Total memory available in the system."
// SystemMemoryShared is the metric conforming to the "system.memory.shared"
// semantic conventions. It represents the shared memory used (mostly by
// tmpfs).
// Instrument: updowncounter
// Unit: By
// Stability: Experimental
SystemMemorySharedName = "system.memory.shared"
SystemMemorySharedUnit = "By"
SystemMemorySharedDescription = "Shared memory used (mostly by tmpfs)."
// SystemMemoryUtilization is the metric conforming to the
// "system.memory.utilization" semantic conventions.
// Instrument: gauge
@@ -1038,25 +1274,25 @@ const (
SystemNetworkConnectionsName = "system.network.connections"
SystemNetworkConnectionsUnit = "{connection}"
// SystemProcessesCount is the metric conforming to the
// "system.processes.count" semantic conventions. It represents the total
// number of processes in each state.
// SystemProcessCount is the metric conforming to the "system.process.count"
// semantic conventions. It represents the total number of processes in each
// state.
// Instrument: updowncounter
// Unit: {process}
// Stability: Experimental
SystemProcessesCountName = "system.processes.count"
SystemProcessesCountUnit = "{process}"
SystemProcessesCountDescription = "Total number of processes in each state"
SystemProcessCountName = "system.process.count"
SystemProcessCountUnit = "{process}"
SystemProcessCountDescription = "Total number of processes in each state"
// SystemProcessesCreated is the metric conforming to the
// "system.processes.created" semantic conventions. It represents the total
// SystemProcessCreated is the metric conforming to the
// "system.process.created" semantic conventions. It represents the total
// number of processes created over uptime of the host.
// Instrument: counter
// Unit: {process}
// Stability: Experimental
SystemProcessesCreatedName = "system.processes.created"
SystemProcessesCreatedUnit = "{process}"
SystemProcessesCreatedDescription = "Total number of processes created over uptime of the host"
SystemProcessCreatedName = "system.process.created"
SystemProcessCreatedUnit = "{process}"
SystemProcessCreatedDescription = "Total number of processes created over uptime of the host"
// SystemLinuxMemoryAvailable is the metric conforming to the
// "system.linux.memory.available" semantic conventions. It represents an

View File

@@ -1,9 +1,9 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package semconv // import "go.opentelemetry.io/otel/semconv/v1.24.0"
package semconv // import "go.opentelemetry.io/otel/semconv/v1.26.0"
// SchemaURL is the schema URL that matches the version of the semantic conventions
// that this package defines. Semconv packages starting from v1.4.0 must declare
// non-empty schema URL in the form https://opentelemetry.io/schemas/<version>
const SchemaURL = "https://opentelemetry.io/schemas/1.24.0"
const SchemaURL = "https://opentelemetry.io/schemas/1.26.0"

661
vendor/go.opentelemetry.io/otel/trace/auto.go generated vendored Normal file
View File

@@ -0,0 +1,661 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package trace // import "go.opentelemetry.io/otel/trace"
import (
"context"
"encoding/json"
"fmt"
"math"
"os"
"reflect"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"unicode/utf8"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/otel/trace/embedded"
"go.opentelemetry.io/otel/trace/internal/telemetry"
)
// newAutoTracerProvider returns an auto-instrumentable [trace.TracerProvider].
// If an [go.opentelemetry.io/auto.Instrumentation] is configured to instrument
// the process using the returned TracerProvider, all of the telemetry it
// produces will be processed and handled by that Instrumentation. By default,
// if no Instrumentation instruments the TracerProvider it will not generate
// any trace telemetry.
func newAutoTracerProvider() TracerProvider { return tracerProviderInstance }
var tracerProviderInstance = new(autoTracerProvider)
type autoTracerProvider struct{ embedded.TracerProvider }
var _ TracerProvider = autoTracerProvider{}
func (p autoTracerProvider) Tracer(name string, opts ...TracerOption) Tracer {
cfg := NewTracerConfig(opts...)
return autoTracer{
name: name,
version: cfg.InstrumentationVersion(),
schemaURL: cfg.SchemaURL(),
}
}
type autoTracer struct {
embedded.Tracer
name, schemaURL, version string
}
var _ Tracer = autoTracer{}
func (t autoTracer) Start(ctx context.Context, name string, opts ...SpanStartOption) (context.Context, Span) {
var psc SpanContext
sampled := true
span := new(autoSpan)
// Ask eBPF for sampling decision and span context info.
t.start(ctx, span, &psc, &sampled, &span.spanContext)
span.sampled.Store(sampled)
ctx = ContextWithSpan(ctx, span)
if sampled {
// Only build traces if sampled.
cfg := NewSpanStartConfig(opts...)
span.traces, span.span = t.traces(name, cfg, span.spanContext, psc)
}
return ctx, span
}
// Expected to be implemented in eBPF.
//
//go:noinline
func (t *autoTracer) start(
ctx context.Context,
spanPtr *autoSpan,
psc *SpanContext,
sampled *bool,
sc *SpanContext,
) {
start(ctx, spanPtr, psc, sampled, sc)
}
// start is used for testing.
var start = func(context.Context, *autoSpan, *SpanContext, *bool, *SpanContext) {}
func (t autoTracer) traces(name string, cfg SpanConfig, sc, psc SpanContext) (*telemetry.Traces, *telemetry.Span) {
span := &telemetry.Span{
TraceID: telemetry.TraceID(sc.TraceID()),
SpanID: telemetry.SpanID(sc.SpanID()),
Flags: uint32(sc.TraceFlags()),
TraceState: sc.TraceState().String(),
ParentSpanID: telemetry.SpanID(psc.SpanID()),
Name: name,
Kind: spanKind(cfg.SpanKind()),
}
span.Attrs, span.DroppedAttrs = convCappedAttrs(maxSpan.Attrs, cfg.Attributes())
links := cfg.Links()
if limit := maxSpan.Links; limit == 0 {
n := int64(len(links))
if n > 0 {
span.DroppedLinks = uint32(min(n, math.MaxUint32)) // nolint: gosec // Bounds checked.
}
} else {
if limit > 0 {
n := int64(max(len(links)-limit, 0))
span.DroppedLinks = uint32(min(n, math.MaxUint32)) // nolint: gosec // Bounds checked.
links = links[n:]
}
span.Links = convLinks(links)
}
if t := cfg.Timestamp(); !t.IsZero() {
span.StartTime = cfg.Timestamp()
} else {
span.StartTime = time.Now()
}
return &telemetry.Traces{
ResourceSpans: []*telemetry.ResourceSpans{
{
ScopeSpans: []*telemetry.ScopeSpans{
{
Scope: &telemetry.Scope{
Name: t.name,
Version: t.version,
},
Spans: []*telemetry.Span{span},
SchemaURL: t.schemaURL,
},
},
},
},
}, span
}
func spanKind(kind SpanKind) telemetry.SpanKind {
switch kind {
case SpanKindInternal:
return telemetry.SpanKindInternal
case SpanKindServer:
return telemetry.SpanKindServer
case SpanKindClient:
return telemetry.SpanKindClient
case SpanKindProducer:
return telemetry.SpanKindProducer
case SpanKindConsumer:
return telemetry.SpanKindConsumer
}
return telemetry.SpanKind(0) // undefined.
}
type autoSpan struct {
embedded.Span
spanContext SpanContext
sampled atomic.Bool
mu sync.Mutex
traces *telemetry.Traces
span *telemetry.Span
}
func (s *autoSpan) SpanContext() SpanContext {
if s == nil {
return SpanContext{}
}
// s.spanContext is immutable, do not acquire lock s.mu.
return s.spanContext
}
func (s *autoSpan) IsRecording() bool {
if s == nil {
return false
}
return s.sampled.Load()
}
func (s *autoSpan) SetStatus(c codes.Code, msg string) {
if s == nil || !s.sampled.Load() {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if s.span.Status == nil {
s.span.Status = new(telemetry.Status)
}
s.span.Status.Message = msg
switch c {
case codes.Unset:
s.span.Status.Code = telemetry.StatusCodeUnset
case codes.Error:
s.span.Status.Code = telemetry.StatusCodeError
case codes.Ok:
s.span.Status.Code = telemetry.StatusCodeOK
}
}
func (s *autoSpan) SetAttributes(attrs ...attribute.KeyValue) {
if s == nil || !s.sampled.Load() {
return
}
s.mu.Lock()
defer s.mu.Unlock()
limit := maxSpan.Attrs
if limit == 0 {
// No attributes allowed.
n := int64(len(attrs))
if n > 0 {
s.span.DroppedAttrs += uint32(min(n, math.MaxUint32)) // nolint: gosec // Bounds checked.
}
return
}
m := make(map[string]int)
for i, a := range s.span.Attrs {
m[a.Key] = i
}
for _, a := range attrs {
val := convAttrValue(a.Value)
if val.Empty() {
s.span.DroppedAttrs++
continue
}
if idx, ok := m[string(a.Key)]; ok {
s.span.Attrs[idx] = telemetry.Attr{
Key: string(a.Key),
Value: val,
}
} else if limit < 0 || len(s.span.Attrs) < limit {
s.span.Attrs = append(s.span.Attrs, telemetry.Attr{
Key: string(a.Key),
Value: val,
})
m[string(a.Key)] = len(s.span.Attrs) - 1
} else {
s.span.DroppedAttrs++
}
}
}
// convCappedAttrs converts up to limit attrs into a []telemetry.Attr. The
// number of dropped attributes is also returned.
func convCappedAttrs(limit int, attrs []attribute.KeyValue) ([]telemetry.Attr, uint32) {
n := len(attrs)
if limit == 0 {
var out uint32
if n > 0 {
out = uint32(min(int64(n), math.MaxUint32)) // nolint: gosec // Bounds checked.
}
return nil, out
}
if limit < 0 {
// Unlimited.
return convAttrs(attrs), 0
}
if n < 0 {
n = 0
}
limit = min(n, limit)
return convAttrs(attrs[:limit]), uint32(n - limit) // nolint: gosec // Bounds checked.
}
func convAttrs(attrs []attribute.KeyValue) []telemetry.Attr {
if len(attrs) == 0 {
// Avoid allocations if not necessary.
return nil
}
out := make([]telemetry.Attr, 0, len(attrs))
for _, attr := range attrs {
key := string(attr.Key)
val := convAttrValue(attr.Value)
if val.Empty() {
continue
}
out = append(out, telemetry.Attr{Key: key, Value: val})
}
return out
}
func convAttrValue(value attribute.Value) telemetry.Value {
switch value.Type() {
case attribute.BOOL:
return telemetry.BoolValue(value.AsBool())
case attribute.INT64:
return telemetry.Int64Value(value.AsInt64())
case attribute.FLOAT64:
return telemetry.Float64Value(value.AsFloat64())
case attribute.STRING:
v := truncate(maxSpan.AttrValueLen, value.AsString())
return telemetry.StringValue(v)
case attribute.BOOLSLICE:
slice := value.AsBoolSlice()
out := make([]telemetry.Value, 0, len(slice))
for _, v := range slice {
out = append(out, telemetry.BoolValue(v))
}
return telemetry.SliceValue(out...)
case attribute.INT64SLICE:
slice := value.AsInt64Slice()
out := make([]telemetry.Value, 0, len(slice))
for _, v := range slice {
out = append(out, telemetry.Int64Value(v))
}
return telemetry.SliceValue(out...)
case attribute.FLOAT64SLICE:
slice := value.AsFloat64Slice()
out := make([]telemetry.Value, 0, len(slice))
for _, v := range slice {
out = append(out, telemetry.Float64Value(v))
}
return telemetry.SliceValue(out...)
case attribute.STRINGSLICE:
slice := value.AsStringSlice()
out := make([]telemetry.Value, 0, len(slice))
for _, v := range slice {
v = truncate(maxSpan.AttrValueLen, v)
out = append(out, telemetry.StringValue(v))
}
return telemetry.SliceValue(out...)
}
return telemetry.Value{}
}
// truncate returns a truncated version of s such that it contains less than
// the limit number of characters. Truncation is applied by returning the limit
// number of valid characters contained in s.
//
// If limit is negative, it returns the original string.
//
// UTF-8 is supported. When truncating, all invalid characters are dropped
// before applying truncation.
//
// If s already contains less than the limit number of bytes, it is returned
// unchanged. No invalid characters are removed.
func truncate(limit int, s string) string {
// This prioritize performance in the following order based on the most
// common expected use-cases.
//
// - Short values less than the default limit (128).
// - Strings with valid encodings that exceed the limit.
// - No limit.
// - Strings with invalid encodings that exceed the limit.
if limit < 0 || len(s) <= limit {
return s
}
// Optimistically, assume all valid UTF-8.
var b strings.Builder
count := 0
for i, c := range s {
if c != utf8.RuneError {
count++
if count > limit {
return s[:i]
}
continue
}
_, size := utf8.DecodeRuneInString(s[i:])
if size == 1 {
// Invalid encoding.
b.Grow(len(s) - 1)
_, _ = b.WriteString(s[:i])
s = s[i:]
break
}
}
// Fast-path, no invalid input.
if b.Cap() == 0 {
return s
}
// Truncate while validating UTF-8.
for i := 0; i < len(s) && count < limit; {
c := s[i]
if c < utf8.RuneSelf {
// Optimization for single byte runes (common case).
_ = b.WriteByte(c)
i++
count++
continue
}
_, size := utf8.DecodeRuneInString(s[i:])
if size == 1 {
// We checked for all 1-byte runes above, this is a RuneError.
i++
continue
}
_, _ = b.WriteString(s[i : i+size])
i += size
count++
}
return b.String()
}
func (s *autoSpan) End(opts ...SpanEndOption) {
if s == nil || !s.sampled.Swap(false) {
return
}
// s.end exists so the lock (s.mu) is not held while s.ended is called.
s.ended(s.end(opts))
}
func (s *autoSpan) end(opts []SpanEndOption) []byte {
s.mu.Lock()
defer s.mu.Unlock()
cfg := NewSpanEndConfig(opts...)
if t := cfg.Timestamp(); !t.IsZero() {
s.span.EndTime = cfg.Timestamp()
} else {
s.span.EndTime = time.Now()
}
b, _ := json.Marshal(s.traces) // TODO: do not ignore this error.
return b
}
// Expected to be implemented in eBPF.
//
//go:noinline
func (*autoSpan) ended(buf []byte) { ended(buf) }
// ended is used for testing.
var ended = func([]byte) {}
func (s *autoSpan) RecordError(err error, opts ...EventOption) {
if s == nil || err == nil || !s.sampled.Load() {
return
}
cfg := NewEventConfig(opts...)
attrs := cfg.Attributes()
attrs = append(attrs,
semconv.ExceptionType(typeStr(err)),
semconv.ExceptionMessage(err.Error()),
)
if cfg.StackTrace() {
buf := make([]byte, 2048)
n := runtime.Stack(buf, false)
attrs = append(attrs, semconv.ExceptionStacktrace(string(buf[0:n])))
}
s.mu.Lock()
defer s.mu.Unlock()
s.addEvent(semconv.ExceptionEventName, cfg.Timestamp(), attrs)
}
func typeStr(i any) string {
t := reflect.TypeOf(i)
if t.PkgPath() == "" && t.Name() == "" {
// Likely a builtin type.
return t.String()
}
return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
}
func (s *autoSpan) AddEvent(name string, opts ...EventOption) {
if s == nil || !s.sampled.Load() {
return
}
cfg := NewEventConfig(opts...)
s.mu.Lock()
defer s.mu.Unlock()
s.addEvent(name, cfg.Timestamp(), cfg.Attributes())
}
// addEvent adds an event with name and attrs at tStamp to the span. The span
// lock (s.mu) needs to be held by the caller.
func (s *autoSpan) addEvent(name string, tStamp time.Time, attrs []attribute.KeyValue) {
limit := maxSpan.Events
if limit == 0 {
s.span.DroppedEvents++
return
}
if limit > 0 && len(s.span.Events) == limit {
// Drop head while avoiding allocation of more capacity.
copy(s.span.Events[:limit-1], s.span.Events[1:])
s.span.Events = s.span.Events[:limit-1]
s.span.DroppedEvents++
}
e := &telemetry.SpanEvent{Time: tStamp, Name: name}
e.Attrs, e.DroppedAttrs = convCappedAttrs(maxSpan.EventAttrs, attrs)
s.span.Events = append(s.span.Events, e)
}
func (s *autoSpan) AddLink(link Link) {
if s == nil || !s.sampled.Load() {
return
}
l := maxSpan.Links
s.mu.Lock()
defer s.mu.Unlock()
if l == 0 {
s.span.DroppedLinks++
return
}
if l > 0 && len(s.span.Links) == l {
// Drop head while avoiding allocation of more capacity.
copy(s.span.Links[:l-1], s.span.Links[1:])
s.span.Links = s.span.Links[:l-1]
s.span.DroppedLinks++
}
s.span.Links = append(s.span.Links, convLink(link))
}
func convLinks(links []Link) []*telemetry.SpanLink {
out := make([]*telemetry.SpanLink, 0, len(links))
for _, link := range links {
out = append(out, convLink(link))
}
return out
}
func convLink(link Link) *telemetry.SpanLink {
l := &telemetry.SpanLink{
TraceID: telemetry.TraceID(link.SpanContext.TraceID()),
SpanID: telemetry.SpanID(link.SpanContext.SpanID()),
TraceState: link.SpanContext.TraceState().String(),
Flags: uint32(link.SpanContext.TraceFlags()),
}
l.Attrs, l.DroppedAttrs = convCappedAttrs(maxSpan.LinkAttrs, link.Attributes)
return l
}
func (s *autoSpan) SetName(name string) {
if s == nil || !s.sampled.Load() {
return
}
s.mu.Lock()
defer s.mu.Unlock()
s.span.Name = name
}
func (*autoSpan) TracerProvider() TracerProvider { return newAutoTracerProvider() }
// maxSpan are the span limits resolved during startup.
var maxSpan = newSpanLimits()
type spanLimits struct {
// Attrs is the number of allowed attributes for a span.
//
// This is resolved from the environment variable value for the
// OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT key if it exists. Otherwise, the
// environment variable value for OTEL_ATTRIBUTE_COUNT_LIMIT, or 128 if
// that is not set, is used.
Attrs int
// AttrValueLen is the maximum attribute value length allowed for a span.
//
// This is resolved from the environment variable value for the
// OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT key if it exists. Otherwise, the
// environment variable value for OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, or -1
// if that is not set, is used.
AttrValueLen int
// Events is the number of allowed events for a span.
//
// This is resolved from the environment variable value for the
// OTEL_SPAN_EVENT_COUNT_LIMIT key, or 128 is used if that is not set.
Events int
// EventAttrs is the number of allowed attributes for a span event.
//
// The is resolved from the environment variable value for the
// OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT key, or 128 is used if that is not set.
EventAttrs int
// Links is the number of allowed Links for a span.
//
// This is resolved from the environment variable value for the
// OTEL_SPAN_LINK_COUNT_LIMIT, or 128 is used if that is not set.
Links int
// LinkAttrs is the number of allowed attributes for a span link.
//
// This is resolved from the environment variable value for the
// OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, or 128 is used if that is not set.
LinkAttrs int
}
func newSpanLimits() spanLimits {
return spanLimits{
Attrs: firstEnv(
128,
"OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT",
"OTEL_ATTRIBUTE_COUNT_LIMIT",
),
AttrValueLen: firstEnv(
-1, // Unlimited.
"OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT",
"OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT",
),
Events: firstEnv(128, "OTEL_SPAN_EVENT_COUNT_LIMIT"),
EventAttrs: firstEnv(128, "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT"),
Links: firstEnv(128, "OTEL_SPAN_LINK_COUNT_LIMIT"),
LinkAttrs: firstEnv(128, "OTEL_LINK_ATTRIBUTE_COUNT_LIMIT"),
}
}
// firstEnv returns the parsed integer value of the first matching environment
// variable from keys. The defaultVal is returned if the value is not an
// integer or no match is found.
func firstEnv(defaultVal int, keys ...string) int {
for _, key := range keys {
strV := os.Getenv(key)
if strV == "" {
continue
}
v, err := strconv.Atoi(strV)
if err == nil {
return v
}
// Ignore invalid environment variable.
}
return defaultVal
}

View File

@@ -213,7 +213,7 @@ var _ SpanStartEventOption = attributeOption{}
// WithAttributes adds the attributes related to a span life-cycle event.
// These attributes are used to describe the work a Span represents when this
// option is provided to a Span's start or end events. Otherwise, these
// option is provided to a Span's start event. Otherwise, these
// attributes provide additional information about the event being recorded
// (e.g. error, state change, processing progress, system event).
//

View File

@@ -22,7 +22,7 @@ func ContextWithSpanContext(parent context.Context, sc SpanContext) context.Cont
return ContextWithSpan(parent, nonRecordingSpan{sc: sc})
}
// ContextWithRemoteSpanContext returns a copy of parent with rsc set explicly
// ContextWithRemoteSpanContext returns a copy of parent with rsc set explicitly
// as a remote SpanContext and as the current Span. The Span implementation
// that wraps rsc is non-recording and performs no operations other than to
// return rsc as the SpanContext from the SpanContext method.

View File

@@ -96,7 +96,7 @@ can embed the API interface directly.
This option is not recommended. It will lead to publishing packages that
contain runtime panics when users update to newer versions of
[go.opentelemetry.io/otel/trace], which may be done with a trasitive
[go.opentelemetry.io/otel/trace], which may be done with a transitive
dependency.
Finally, an author can embed another implementation in theirs. The embedded

View 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 returns if a is equal to b.
func (a Attr) Equal(b Attr) bool {
return a.Key == b.Key && a.Value.Equal(b.Value)
}

View 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"

View 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 returns false if id contains at least one non-zero byte.
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 returns true if the span ID contains at least one non-zero byte.
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 []byte, 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
}

View 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
}

Some files were not shown because too many files have changed in this diff Show More