// Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 package log // import "go.opentelemetry.io/otel/log" import ( "slices" "time" ) // attributesInlineCount is the number of attributes that are efficiently // stored in an array within a Record. This value is borrowed from slog which // performed a quantitative survey of log library use and found this value to // cover 95% of all use-cases (https://go.dev/blog/slog#performance). const attributesInlineCount = 5 // Record represents a log record. type Record struct { // Ensure forward compatibility by explicitly making this not comparable. noCmp [0]func() //nolint: unused // This is indeed used. timestamp time.Time observedTimestamp time.Time severity Severity severityText string body Value // The fields below are for optimizing the implementation of Attributes and // AddAttributes. This design is borrowed from the slog Record type: // https://cs.opensource.google/go/go/+/refs/tags/go1.22.0:src/log/slog/record.go;l=20 // Allocation optimization: an inline array sized to hold // the majority of log calls (based on examination of open-source // code). It holds the start of the list of attributes. front [attributesInlineCount]KeyValue // The number of attributes in front. nFront int // The list of attributes except for those in front. // Invariants: // - len(back) > 0 if nFront == len(front) // - Unused array elements are zero-ed. Used to detect mistakes. back []KeyValue } // Timestamp returns the time when the log record occurred. func (r *Record) Timestamp() time.Time { return r.timestamp } // SetTimestamp sets the time when the log record occurred. func (r *Record) SetTimestamp(t time.Time) { r.timestamp = t } // ObservedTimestamp returns the time when the log record was observed. func (r *Record) ObservedTimestamp() time.Time { return r.observedTimestamp } // SetObservedTimestamp sets the time when the log record was observed. func (r *Record) SetObservedTimestamp(t time.Time) { r.observedTimestamp = t } // Severity returns the [Severity] of the log record. func (r *Record) Severity() Severity { return r.severity } // SetSeverity sets the [Severity] level of the log record. func (r *Record) SetSeverity(level Severity) { r.severity = level } // SeverityText returns severity (also known as log level) text. This is the // original string representation of the severity as it is known at the source. func (r *Record) SeverityText() string { return r.severityText } // SetSeverityText sets severity (also known as log level) text. This is the // original string representation of the severity as it is known at the source. func (r *Record) SetSeverityText(text string) { r.severityText = text } // Body returns the body of the log record. func (r *Record) Body() Value { return r.body } // SetBody sets the body of the log record. func (r *Record) SetBody(v Value) { r.body = v } // WalkAttributes walks all attributes the log record holds by calling f for // each on each [KeyValue] in the [Record]. Iteration stops if f returns false. func (r *Record) WalkAttributes(f func(KeyValue) bool) { for i := 0; i < r.nFront; i++ { if !f(r.front[i]) { return } } for _, a := range r.back { if !f(a) { return } } } // AddAttributes adds attributes to the log record. func (r *Record) AddAttributes(attrs ...KeyValue) { var i int for i = 0; i < len(attrs) && r.nFront < len(r.front); i++ { a := attrs[i] r.front[r.nFront] = a r.nFront++ } r.back = slices.Grow(r.back, len(attrs[i:])) r.back = append(r.back, attrs[i:]...) } // AttributesLen returns the number of attributes in the log record. func (r *Record) AttributesLen() int { return r.nFront + len(r.back) }