Skip to content

Commit

Permalink
sdk/log: Implement Record (#5073)
Browse files Browse the repository at this point in the history
  • Loading branch information
MrAlias authored Mar 14, 2024
1 parent 54b6ee4 commit ca35244
Show file tree
Hide file tree
Showing 4 changed files with 319 additions and 40 deletions.
6 changes: 5 additions & 1 deletion sdk/log/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ module go.opentelemetry.io/otel/sdk/log
go 1.21

require (
github.com/stretchr/testify v1.9.0
go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/log v0.0.1-alpha
go.opentelemetry.io/otel/sdk v1.24.0
go.opentelemetry.io/otel/trace v1.24.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
golang.org/x/sys v0.18.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace go.opentelemetry.io/otel/metric => ../../metric
Expand Down
2 changes: 2 additions & 0 deletions sdk/log/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
145 changes: 106 additions & 39 deletions sdk/log/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package log // import "go.opentelemetry.io/otel/sdk/log"

import (
"slices"
"time"

"go.opentelemetry.io/otel/log"
Expand All @@ -12,130 +13,197 @@ import (
"go.opentelemetry.io/otel/trace"
)

// 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 is a log record emitted by the Logger.
type Record struct{}
type Record struct {
// Do not embed the log.Record. Attributes need to be overwrite-able and
// deep-copying needs to be possible.

timestamp time.Time
observedTimestamp time.Time
severity log.Severity
severityText string
body log.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]log.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 []log.KeyValue

traceID trace.TraceID
spanID trace.SpanID
traceFlags trace.TraceFlags

// resource represents the entity that collected the log.
resource *resource.Resource

// scope is the Scope that the Logger was created with.
scope *instrumentation.Scope

attributeValueLengthLimit int
attributeCountLimit int
}

// Timestamp returns the time when the log record occurred.
func (r *Record) Timestamp() time.Time {
// TODO (#5064): Implement.
return time.Time{}
return r.timestamp
}

// SetTimestamp sets the time when the log record occurred.
func (r *Record) SetTimestamp(t time.Time) {
// TODO (#5064): Implement.
r.timestamp = t
}

// ObservedTimestamp returns the time when the log record was observed.
func (r *Record) ObservedTimestamp() time.Time {
// TODO (#5064): Implement.
return time.Time{}
return r.observedTimestamp
}

// SetObservedTimestamp sets the time when the log record was observed.
func (r *Record) SetObservedTimestamp(t time.Time) {
// TODO (#5064): Implement.
r.observedTimestamp = t
}

// Severity returns the severity of the log record.
func (r *Record) Severity() log.Severity {
// TODO (#5064): Implement.
return log.Severity(0)
return r.severity
}

// SetSeverity sets the severity level of the log record.
func (r *Record) SetSeverity(level log.Severity) {
// TODO (#5064): Implement.
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 {
// TODO (#5064): Implement.
return ""
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) {
// TODO (#5064): Implement.
r.severityText = text
}

// Body returns the body of the log record.
func (r *Record) Body() log.Value {
// TODO (#5064): Implement.
return log.Value{}
return r.body
}

// SetBody sets the body of the log record.
func (r *Record) SetBody(v log.Value) {
// TODO (#5064): Implement.
r.body = v
}

// WalkAttributes walks all attributes the log record holds by calling f for
// each on each [log.KeyValue] in the [Record]. Iteration stops if f returns false.
func (r *Record) WalkAttributes(f func(log.KeyValue) bool) {
// TODO (#5064): Implement.
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 ...log.KeyValue) {
// TODO (#5064): Implement.
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:]...)
}

// SetAttributes sets (and overrides) attributes to the log record.
func (r *Record) SetAttributes(attrs ...log.KeyValue) {
// TODO (#5064): Implement.
r.nFront = 0
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.Clone(attrs[i:])
}

// AttributesLen returns the number of attributes in the log record.
func (r *Record) AttributesLen() int {
// TODO (#5064): Implement.
return 0
return r.nFront + len(r.back)
}

// TraceID returns the trace ID or empty array.
func (r *Record) TraceID() trace.TraceID {
// TODO (#5064): Implement.
return trace.TraceID{}
return r.traceID
}

// SetTraceID sets the trace ID.
func (r *Record) SetTraceID(id trace.TraceID) {
// TODO (#5064): Implement.
r.traceID = id
}

// SpanID returns the span ID or empty array.
func (r *Record) SpanID() trace.SpanID {
// TODO (#5064): Implement.
return trace.SpanID{}
return r.spanID
}

// SetSpanID sets the span ID.
func (r *Record) SetSpanID(id trace.SpanID) {
// TODO (#5064): Implement.
r.spanID = id
}

// TraceFlags returns the trace flags.
func (r *Record) TraceFlags() trace.TraceFlags {
return 0
return r.traceFlags
}

// SetTraceFlags sets the trace flags.
func (r *Record) SetTraceFlags(flags trace.TraceFlags) {
// TODO (#5064): Implement.
r.traceFlags = flags
}

// Resource returns the entity that collected the log.
func (r *Record) Resource() resource.Resource {
// TODO (#5064): Implement.
return resource.Resource{}
if r.resource == nil {
return *resource.Empty()
}
return *r.resource
}

// InstrumentationScope returns the scope that the Logger was created with.
func (r *Record) InstrumentationScope() instrumentation.Scope {
// TODO (#5064): Implement.
return instrumentation.Scope{}
if r.scope == nil {
return instrumentation.Scope{}
}
return *r.scope
}

// AttributeValueLengthLimit is the maximum allowed attribute value length.
Expand All @@ -145,8 +213,7 @@ func (r *Record) InstrumentationScope() instrumentation.Scope {
//
// Negative value means no limit should be applied.
func (r *Record) AttributeValueLengthLimit() int {
// TODO (#5064): Implement.
return 0
return r.attributeValueLengthLimit
}

// AttributeCountLimit is the maximum allowed log record attribute count. Any
Expand All @@ -156,13 +223,13 @@ func (r *Record) AttributeValueLengthLimit() int {
//
// Negative value means no limit should be applied.
func (r *Record) AttributeCountLimit() int {
// TODO (#5064): Implement.
return 0
return r.attributeCountLimit
}

// 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 {
// TODO (#5064): Implement.
return *r
res := *r
res.back = slices.Clone(r.back)
return res
}
Loading

0 comments on commit ca35244

Please sign in to comment.