1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2026-06-03 18:35:08 +02:00
Files
opentelemetry-go/log/record.go
T
Israel Blancas c9d20155fc log: add error field to Record and make SDK to emit exception attributes (#7924)
Fixes #7923


```sh
$ go test -run=^$ -bench=BenchmarkLoggerEmitExceptionAttributes -benchmem -count=5 -benchtime=500ms -cpu=1
goos: darwin
goarch: arm64
pkg: go.opentelemetry.io/otel/sdk/log
cpu: Apple M4 Pro
BenchmarkLoggerSetErrAndEmit                 	  628162	      1023 ns/op	    5371 B/op	       1 allocs/op
BenchmarkLoggerSetErrAndEmit                 	  663955	       863.3 ns/op	    5105 B/op	       1 allocs/op
BenchmarkLoggerSetErrAndEmit                 	  653888	      1067 ns/op	    5177 B/op	       1 allocs/op
BenchmarkLoggerSetErrAndEmit                 	  716438	       824.8 ns/op	    4764 B/op	       1 allocs/op
BenchmarkLoggerSetErrAndEmit                 	  746902	       999.2 ns/op	    5630 B/op	       1 allocs/op
BenchmarkLoggerSetExceptionAttributesAndEmit 	  650696	      1042 ns/op	    5200 B/op	       1 allocs/op
BenchmarkLoggerSetExceptionAttributesAndEmit 	  574962	       980.7 ns/op	    4743 B/op	       1 allocs/op
BenchmarkLoggerSetExceptionAttributesAndEmit 	  536736	       989.2 ns/op	    5049 B/op	       1 allocs/op
BenchmarkLoggerSetExceptionAttributesAndEmit 	  558511	      1190 ns/op	    4870 B/op	       1 allocs/op
BenchmarkLoggerSetExceptionAttributesAndEmit 	  669452	       978.8 ns/op	    5067 B/op	       1 allocs/op
PASS
ok  	go.opentelemetry.io/otel/sdk/log	6.994s
```

TODO after merged:
-
https://github.com/open-telemetry/opentelemetry-go/pull/7924#discussion_r2832906451

---------

Signed-off-by: Israel Blancas <iblancasa@gmail.com>
Co-authored-by: Robert Pająk <pellared@hotmail.com>
Co-authored-by: Damien Mathieu <42@dmathieu.com>
2026-03-05 10:24:05 +01:00

164 lines
4.6 KiB
Go

// 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.
// A log record with non-empty event name is interpreted as an event record.
type Record struct {
// Ensure forward compatibility by explicitly making this not comparable.
noCmp [0]func() //nolint: unused // This is indeed used.
eventName string
timestamp time.Time
observedTimestamp time.Time
severity Severity
severityText string
body Value
err error
// 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
}
// EventName returns the event name.
// A log record with non-empty event name is interpreted as an event record.
func (r *Record) EventName() string {
return r.eventName
}
// SetEventName sets the event name.
// A log record with non-empty event name is interpreted as an event record.
func (r *Record) SetEventName(s string) {
r.eventName = s
}
// 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
}
// Err returns the associated error if one has been set.
func (r *Record) Err() error {
return r.err
}
// SetErr sets the associated error. Passing nil clears the error.
func (r *Record) SetErr(err error) {
r.err = err
}
// 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)
}
// Clone returns a copy of the record with no shared state.
// The original record and the clone can both be modified without interfering with each other.
func (r *Record) Clone() Record {
res := *r
res.back = slices.Clone(r.back)
return res
}