You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-08-10 22:31:50 +02:00
This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [github.com/golangci/golangci-lint](https://redirect.github.com/golangci/golangci-lint) | `v1.64.8` -> `v2.0.2` | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>golangci/golangci-lint (github.com/golangci/golangci-lint)</summary> ### [`v2.0.2`](https://redirect.github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v202) [Compare Source](https://redirect.github.com/golangci/golangci-lint/compare/v2.0.1...v2.0.2) 1. Misc. - Fixes flags parsing for formatters - Fixes the filepath used by the exclusion `source` option 2. Documentation - Adds a section about flags migration - Cleaning pages with v1 options ### [`v2.0.1`](https://redirect.github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v201) [Compare Source](https://redirect.github.com/golangci/golangci-lint/compare/v2.0.0...v2.0.1) 1. Linters/formatters bug fixes - `golines`: fix settings during linter load 2. Misc. - Validates the `version` field before the configuration - `forbidigo`: fix migration ### [`v2.0.0`](https://redirect.github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v200) [Compare Source](https://redirect.github.com/golangci/golangci-lint/compare/v1.64.8...v2.0.0) 1. Enhancements - 🌟 New `golangci-lint fmt` command with dedicated formatter configuration (https://golangci-lint.run/welcome/quick-start/#formatting) - ♻️ New `golangci-lint migrate` command to help migration from v1 to v2 (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#command-migrate)) - ⚠️ New default values (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/)) - ⚠️ No exclusions by default (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#issuesexclude-use-default)) - ⚠️ New default sort order (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#outputsort-order)) - 🌟 New option `run.relative-path-mode` (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#runrelative-path-mode)) - 🌟 New linters configuration (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#linters)) - 🌟 New output format configuration (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#output)) - 🌟 New `--fast-only` flag (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#lintersfast)) - 🌟 New option `linters.exclusions.warn-unused` to log a warning if an exclusion rule is unused. 2. New linters/formatters - Add `golines` formatter https://github.com/segmentio/golines 3. Linters new features - ⚠️ Merge `staticcheck`, `stylecheck`, `gosimple` into one linter (`staticcheck`) (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#lintersenablestylecheckgosimplestaticcheck)) - `go-critic`: from 0.12.0 to 0.13.0 - `gomodguard`: from 1.3.5 to 1.4.1 (block explicit indirect dependencies) - `nilnil`: from 1.0.1 to 1.1.0 (new option: `only-two`) - `perfsprint`: from 0.8.2 to 0.9.1 (checker name in the diagnostic message) - `staticcheck`: new `quickfix` set of rules - `testifylint`: from 1.5.2 to 1.6.0 (new options: `equal-values`, `suite-method-signature`, `require-string-msg`) - `wsl`: from 4.5.0 to 4.6.0 (new option: `allow-cuddle-used-in-block`) 4. Linters bug fixes - `bidichk`: from 0.3.2 to 0.3.3 - `errchkjson`: from 0.4.0 to 0.4.1 - `errname`: from 1.0.0 to 1.1.0 - `funlen`: fix `ignore-comments` option - `gci`: from 0.13.5 to 0.13.6 - `gosmopolitan`: from 1.2.2 to 1.3.0 - `inamedparam`: from 0.1.3 to 0.2.0 - `intrange`: from 0.3.0 to 0.3.1 - `protogetter`: from 0.3.9 to 0.3.12 - `unparam`: from [`8a5130c`](https://redirect.github.com/golangci/golangci-lint/commit/8a5130ca722f) to [`0df0534`](https://redirect.github.com/golangci/golangci-lint/commit/0df0534333a4) 5. Misc. - 🧹 Configuration options renaming (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/)) - 🧹 Remove options (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/)) - 🧹 Remove flags (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/)) - 🧹 Remove alternative names (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/#alternative-linter-names)) - 🧹 Remove or replace deprecated elements (cf. [Migration guide](https://golangci-lint.run/product/migration-guide/)) - Adds an option to display some commands as JSON: - `golangci-lint config path --json` - `golangci-lint help linters --json` - `golangci-lint help formatters --json` - `golangci-lint linters --json` - `golangci-lint formatters --json` - `golangci-lint version --json` 6. Documentation - [Migration guide](https://golangci-lint.run/product/migration-guide/) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/open-telemetry/opentelemetry-go). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJTa2lwIENoYW5nZWxvZyIsImRlcGVuZGVuY2llcyJdfQ==--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Tyler Yahn <codingalias@gmail.com>
444 lines
12 KiB
Go
444 lines
12 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
//go:generate stringer -type=Kind -trimprefix=Kind
|
|
|
|
package log // import "go.opentelemetry.io/otel/log"
|
|
|
|
import (
|
|
"bytes"
|
|
"cmp"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"slices"
|
|
"strconv"
|
|
"unsafe"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/internal/global"
|
|
)
|
|
|
|
// errKind is logged when a Value is decoded to an incompatible type.
|
|
var errKind = errors.New("invalid Kind")
|
|
|
|
// Kind is the kind of a [Value].
|
|
type Kind int
|
|
|
|
// Kind values.
|
|
const (
|
|
KindEmpty Kind = iota
|
|
KindBool
|
|
KindFloat64
|
|
KindInt64
|
|
KindString
|
|
KindBytes
|
|
KindSlice
|
|
KindMap
|
|
)
|
|
|
|
// A Value represents a structured log 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 *KeyValue
|
|
)
|
|
|
|
// 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 {
|
|
// This can be later converted back to int64 (overflow not checked).
|
|
return Value{num: uint64(v), any: KindInt64} // nolint:gosec
|
|
}
|
|
|
|
// Float64Value returns a [Value] for a float64.
|
|
func Float64Value(v float64) Value {
|
|
return Value{num: math.Float64bits(v), any: KindFloat64}
|
|
}
|
|
|
|
// 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: KindBool}
|
|
}
|
|
|
|
// 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 ...KeyValue) 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)
|
|
}
|
|
global.Error(errKind, "AsString", "Kind", v.Kind())
|
|
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() != KindInt64 {
|
|
global.Error(errKind, "AsInt64", "Kind", v.Kind())
|
|
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() != KindBool {
|
|
global.Error(errKind, "AsBool", "Kind", v.Kind())
|
|
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() != KindFloat64 {
|
|
global.Error(errKind, "AsFloat64", "Kind", v.Kind())
|
|
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)
|
|
}
|
|
global.Error(errKind, "AsBytes", "Kind", v.Kind())
|
|
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)
|
|
}
|
|
global.Error(errKind, "AsSlice", "Kind", v.Kind())
|
|
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 []KeyValue.
|
|
func (v Value) AsMap() []KeyValue {
|
|
if sp, ok := v.any.(mapptr); ok {
|
|
return unsafe.Slice((*KeyValue)(sp), v.num)
|
|
}
|
|
global.Error(errKind, "AsMap", "Kind", v.Kind())
|
|
return nil
|
|
}
|
|
|
|
// asMap returns the value held by v as a []KeyValue. It will panic if the
|
|
// Value is not KindMap.
|
|
func (v Value) asMap() []KeyValue {
|
|
return unsafe.Slice((*KeyValue)(v.any.(mapptr)), v.num)
|
|
}
|
|
|
|
// Kind returns the Kind of v.
|
|
func (v Value) Kind() Kind {
|
|
switch x := v.any.(type) {
|
|
case Kind:
|
|
return x
|
|
case stringptr:
|
|
return KindString
|
|
case bytesptr:
|
|
return KindBytes
|
|
case sliceptr:
|
|
return KindSlice
|
|
case mapptr:
|
|
return KindMap
|
|
default:
|
|
return KindEmpty
|
|
}
|
|
}
|
|
|
|
// Empty returns if v does not hold any value.
|
|
func (v Value) Empty() bool { return v.Kind() == KindEmpty }
|
|
|
|
// 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 KindInt64, KindBool:
|
|
return v.num == w.num
|
|
case KindString:
|
|
return v.asString() == w.asString()
|
|
case KindFloat64:
|
|
return v.asFloat64() == w.asFloat64()
|
|
case KindSlice:
|
|
return slices.EqualFunc(v.asSlice(), w.asSlice(), Value.Equal)
|
|
case KindMap:
|
|
sv := sortMap(v.asMap())
|
|
sw := sortMap(w.asMap())
|
|
return slices.EqualFunc(sv, sw, KeyValue.Equal)
|
|
case KindBytes:
|
|
return bytes.Equal(v.asBytes(), w.asBytes())
|
|
case KindEmpty:
|
|
return true
|
|
default:
|
|
global.Error(errKind, "Equal", "Kind", k1)
|
|
return false
|
|
}
|
|
}
|
|
|
|
func sortMap(m []KeyValue) []KeyValue {
|
|
sm := make([]KeyValue, len(m))
|
|
copy(sm, m)
|
|
slices.SortFunc(sm, func(a, b KeyValue) 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 KindString:
|
|
return v.asString()
|
|
case KindInt64:
|
|
// Assumes v.num was a valid int64 (overflow not checked).
|
|
return strconv.FormatInt(int64(v.num), 10) // nolint: gosec
|
|
case KindFloat64:
|
|
return strconv.FormatFloat(v.asFloat64(), 'g', -1, 64)
|
|
case KindBool:
|
|
return strconv.FormatBool(v.asBool())
|
|
case KindBytes:
|
|
return fmt.Sprint(v.asBytes()) // nolint:staticcheck // Use fmt.Sprint to encode as slice.
|
|
case KindMap:
|
|
return fmt.Sprint(v.asMap())
|
|
case KindSlice:
|
|
return fmt.Sprint(v.asSlice())
|
|
case KindEmpty:
|
|
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 log.Kind: %s>", v.Kind())
|
|
}
|
|
}
|
|
|
|
// A KeyValue is a key-value pair used to represent a log attribute (a
|
|
// superset of [go.opentelemetry.io/otel/attribute.KeyValue]) and map item.
|
|
type KeyValue struct {
|
|
Key string
|
|
Value Value
|
|
}
|
|
|
|
// Equal returns if a is equal to b.
|
|
func (a KeyValue) Equal(b KeyValue) bool {
|
|
return a.Key == b.Key && a.Value.Equal(b.Value)
|
|
}
|
|
|
|
// String returns a KeyValue for a string value.
|
|
func String(key, value string) KeyValue {
|
|
return KeyValue{key, StringValue(value)}
|
|
}
|
|
|
|
// Int64 returns a KeyValue for an int64 value.
|
|
func Int64(key string, value int64) KeyValue {
|
|
return KeyValue{key, Int64Value(value)}
|
|
}
|
|
|
|
// Int returns a KeyValue for an int value.
|
|
func Int(key string, value int) KeyValue {
|
|
return KeyValue{key, IntValue(value)}
|
|
}
|
|
|
|
// Float64 returns a KeyValue for a float64 value.
|
|
func Float64(key string, value float64) KeyValue {
|
|
return KeyValue{key, Float64Value(value)}
|
|
}
|
|
|
|
// Bool returns a KeyValue for a bool value.
|
|
func Bool(key string, value bool) KeyValue {
|
|
return KeyValue{key, BoolValue(value)}
|
|
}
|
|
|
|
// Bytes returns a KeyValue for a []byte value.
|
|
// The passed slice must not be changed after it is passed.
|
|
func Bytes(key string, value []byte) KeyValue {
|
|
return KeyValue{key, BytesValue(value)}
|
|
}
|
|
|
|
// Slice returns a KeyValue for a []Value value.
|
|
// The passed slice must not be changed after it is passed.
|
|
func Slice(key string, value ...Value) KeyValue {
|
|
return KeyValue{key, SliceValue(value...)}
|
|
}
|
|
|
|
// Map returns a KeyValue for a map value.
|
|
// The passed slice must not be changed after it is passed.
|
|
func Map(key string, value ...KeyValue) KeyValue {
|
|
return KeyValue{key, MapValue(value...)}
|
|
}
|
|
|
|
// Empty returns a KeyValue with an empty value.
|
|
func Empty(key string) KeyValue {
|
|
return KeyValue{key, Value{}}
|
|
}
|
|
|
|
// String returns key-value pair as a string, formatted like "key:value".
|
|
//
|
|
// The returned string is meant for debugging;
|
|
// the string representation is not stable.
|
|
func (a KeyValue) String() string {
|
|
return fmt.Sprintf("%s:%s", a.Key, a.Value)
|
|
}
|
|
|
|
// ValueFromAttribute converts [attribute.Value] to [Value].
|
|
func ValueFromAttribute(value attribute.Value) Value {
|
|
switch value.Type() {
|
|
case attribute.INVALID:
|
|
return Value{}
|
|
case attribute.BOOL:
|
|
return BoolValue(value.AsBool())
|
|
case attribute.BOOLSLICE:
|
|
val := value.AsBoolSlice()
|
|
res := make([]Value, 0, len(val))
|
|
for _, v := range val {
|
|
res = append(res, BoolValue(v))
|
|
}
|
|
return SliceValue(res...)
|
|
case attribute.INT64:
|
|
return Int64Value(value.AsInt64())
|
|
case attribute.INT64SLICE:
|
|
val := value.AsInt64Slice()
|
|
res := make([]Value, 0, len(val))
|
|
for _, v := range val {
|
|
res = append(res, Int64Value(v))
|
|
}
|
|
return SliceValue(res...)
|
|
case attribute.FLOAT64:
|
|
return Float64Value(value.AsFloat64())
|
|
case attribute.FLOAT64SLICE:
|
|
val := value.AsFloat64Slice()
|
|
res := make([]Value, 0, len(val))
|
|
for _, v := range val {
|
|
res = append(res, Float64Value(v))
|
|
}
|
|
return SliceValue(res...)
|
|
case attribute.STRING:
|
|
return StringValue(value.AsString())
|
|
case attribute.STRINGSLICE:
|
|
val := value.AsStringSlice()
|
|
res := make([]Value, 0, len(val))
|
|
for _, v := range val {
|
|
res = append(res, StringValue(v))
|
|
}
|
|
return SliceValue(res...)
|
|
}
|
|
// This code should never be reached
|
|
// as log attributes are a superset of standard attributes.
|
|
panic("unknown attribute type")
|
|
}
|
|
|
|
// KeyValueFromAttribute converts [attribute.KeyValue] to [KeyValue].
|
|
func KeyValueFromAttribute(kv attribute.KeyValue) KeyValue {
|
|
return KeyValue{
|
|
Key: string(kv.Key),
|
|
Value: ValueFromAttribute(kv.Value),
|
|
}
|
|
}
|