// Copyright The OpenTelemetry Authors// SPDX-License-Identifier: Apache-2.0package trace // import "go.opentelemetry.io/otel/trace"import ()const ( maxListMembers = 32 listDelimiters = "," memberDelimiter = "=" errInvalidKey errorConst = "invalid tracestate key" errInvalidValue errorConst = "invalid tracestate value" errInvalidMember errorConst = "invalid tracestate list-member" errMemberNumber errorConst = "too many list-members in tracestate" errDuplicate errorConst = "duplicate list-member in tracestate")type member struct { Key string Value string}// according to (chr = %x20 / (nblk-char = %x21-2B / %x2D-3C / %x3E-7E) )// means (chr = %x20-2B / %x2D-3C / %x3E-7E) .func checkValueChar( byte) bool {return >= '\x20' && <= '\x7e' && != '\x2c' && != '\x3d'}// according to (nblk-chr = %x21-2B / %x2D-3C / %x3E-7E) .func checkValueLast( byte) bool {return >= '\x21' && <= '\x7e' && != '\x2c' && != '\x3d'}// based on the W3C Trace Context specification//// value = (0*255(chr)) nblk-chr// nblk-chr = %x21-2B / %x2D-3C / %x3E-7E// chr = %x20 / nblk-chr//// see https://www.w3.org/TR/trace-context-1/#valuefunc checkValue( string) bool { := len()if == 0 || > 256 {returnfalse }for := 0; < -1; ++ {if !checkValueChar([]) {returnfalse } }returncheckValueLast([-1])}func checkKeyRemain( string) bool {// ( lcalpha / DIGIT / "_" / "-"/ "*" / "/" )for , := range {ifisAlphaNum(byte()) {continue }switch {case'_', '-', '*', '/':continue }returnfalse }returntrue}// according to//// simple-key = lcalpha (0*255( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ))// system-id = lcalpha (0*13( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ))//// param n is remain part length, should be 255 in simple-key or 13 in system-id.func checkKeyPart( string, int) bool {if == "" {returnfalse } := [0] // key's first char := len([1:]) <= = && >= 'a' && <= 'z'return && checkKeyRemain([1:])}func isAlphaNum( byte) bool {if >= 'a' && <= 'z' {returntrue }return >= '0' && <= '9'}// according to//// tenant-id = ( lcalpha / DIGIT ) 0*240( lcalpha / DIGIT / "_" / "-"/ "*" / "/" )//// param n is remain part length, should be 240 exactly.func checkKeyTenant( string, int) bool {if == "" {returnfalse }returnisAlphaNum([0]) && len([1:]) <= && checkKeyRemain([1:])}// based on the W3C Trace Context specification//// key = simple-key / multi-tenant-key// simple-key = lcalpha (0*255( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ))// multi-tenant-key = tenant-id "@" system-id// tenant-id = ( lcalpha / DIGIT ) (0*240( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ))// system-id = lcalpha (0*13( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ))// lcalpha = %x61-7A ; a-z//// see https://www.w3.org/TR/trace-context-1/#tracestate-header.func checkKey( string) bool { , , := strings.Cut(, "@")if ! {returncheckKeyPart(, 255) }returncheckKeyTenant(, 240) && checkKeyPart(, 13)}func newMember(, string) (member, error) {if !checkKey() {returnmember{}, errInvalidKey }if !checkValue() {returnmember{}, errInvalidValue }returnmember{Key: , Value: }, nil}func parseMember( string) (member, error) { , , := strings.Cut(, memberDelimiter)if ! {returnmember{}, fmt.Errorf("%w: %s", errInvalidMember, ) } = strings.TrimLeft(, " \t") = strings.TrimRight(, " \t") , := newMember(, )if != nil {returnmember{}, fmt.Errorf("%w: %s", errInvalidMember, ) }return , nil}// String encodes member into a string compliant with the W3C Trace Context// specification.func ( member) () string {return .Key + "=" + .Value}// TraceState provides additional vendor-specific trace identification// information across different distributed tracing systems. It represents an// immutable list consisting of key/value pairs, each pair is referred to as a// list-member.//// TraceState conforms to the W3C Trace Context specification// (https://www.w3.org/TR/trace-context-1). All operations that create or copy// a TraceState do so by validating all input and will only produce TraceState// that conform to the specification. Specifically, this means that all// list-member's key/value pairs are valid, no duplicate list-members exist,// and the maximum number of list-members (32) is not exceeded.typeTraceStatestruct { //nolint:revive // revive complains about stutter of `trace.TraceState`// list is the members in order. list []member}var _ json.Marshaler = TraceState{}// ParseTraceState attempts to decode a TraceState from the passed// string. It returns an error if the input is invalid according to the W3C// Trace Context specification.func ( string) (TraceState, error) {if == "" {returnTraceState{}, nil } := func( error) error {returnfmt.Errorf("failed to parse tracestate: %w", ) }var []member := make(map[string]struct{})for != "" {varstring , , _ = strings.Cut(, listDelimiters)if == "" {continue } , := parseMember()if != nil {returnTraceState{}, () }if , := [.Key]; {returnTraceState{}, (errDuplicate) } [.Key] = struct{}{} = append(, )if := len(); > maxListMembers {returnTraceState{}, (errMemberNumber) } }returnTraceState{list: }, nil}// MarshalJSON marshals the TraceState into JSON.func ( TraceState) () ([]byte, error) {returnjson.Marshal(.String())}// String encodes the TraceState into a string compliant with the W3C// Trace Context specification. The returned string will be invalid if the// TraceState contains any invalid members.func ( TraceState) () string {iflen(.list) == 0 {return"" }varint += len(.list) // member delimiters: '=' += len(.list) - 1// list delimiters: ','for , := range .list { += len(.Key) += len(.Value) }varstrings.Builder .Grow() _, _ = .WriteString(.list[0].Key) _ = .WriteByte('=') _, _ = .WriteString(.list[0].Value)for := 1; < len(.list); ++ { _ = .WriteByte(listDelimiters[0]) _, _ = .WriteString(.list[].Key) _ = .WriteByte('=') _, _ = .WriteString(.list[].Value) }return .String()}// Get returns the value paired with key from the corresponding TraceState// list-member if it exists, otherwise an empty string is returned.func ( TraceState) ( string) string {for , := range .list {if .Key == {return .Value } }return""}// Walk walks all key value pairs in the TraceState by calling f// Iteration stops if f returns false.func ( TraceState) ( func(, string) bool) {for , := range .list {if !(.Key, .Value) {break } }}// Insert adds a new list-member defined by the key/value pair to the// TraceState. If a list-member already exists for the given key, that// list-member's value is updated. The new or updated list-member is always// moved to the beginning of the TraceState as specified by the W3C Trace// Context specification.//// If key or value are invalid according to the W3C Trace Context// specification an error is returned with the original TraceState.//// If adding a new list-member means the TraceState would have more members// then is allowed, the new list-member will be inserted and the right-most// list-member will be dropped in the returned TraceState.func ( TraceState) (, string) (TraceState, error) { , := newMember(, )if != nil {return , } := len(.list) := for := range .list {if .list[].Key == { = } } := TraceState{}if == && < maxListMembers { .list = make([]member, +1) } else { .list = make([]member, ) } .list[0] = // When the number of members exceeds capacity, drop the "right-most".copy(.list[1:], .list[0:])if < {copy(.list[1+:], .list[+1:]) }return , nil}// Delete returns a copy of the TraceState with the list-member identified by// key removed.func ( TraceState) ( string) TraceState { := make([]member, .Len())copy(, .list)for , := range .list {if .Key == { = append([:], [+1:]...)// TraceState should contain no duplicate members.break } }returnTraceState{list: }}// Len returns the number of list-members in the TraceState.func ( TraceState) () int {returnlen(.list)}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.