Skip to content

Commit

Permalink
Implement logger (#5081)
Browse files Browse the repository at this point in the history
* Implement logger

* Remove todo comment

* Add tests

* Fix

* Check processors in Enabled method
  • Loading branch information
XSAM committed Mar 18, 2024
1 parent 4580e06 commit 77d9192
Show file tree
Hide file tree
Showing 3 changed files with 293 additions and 10 deletions.
54 changes: 49 additions & 5 deletions sdk/log/logger.go
Expand Up @@ -6,28 +6,72 @@ package log // import "go.opentelemetry.io/otel/sdk/log"
import (
"context"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/log"
"go.opentelemetry.io/otel/log/embedded"
"go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/trace"
)

// Compile-time check logger implements log.Logger.
var _ log.Logger = (*logger)(nil)

type logger struct {
embedded.Logger

provider *LoggerProvider
instrumentationScope instrumentation.Scope
}

func newLogger(p *LoggerProvider, scope instrumentation.Scope) *logger {
// TODO (#5061): Implement.
return &logger{}
return &logger{
provider: p,
instrumentationScope: scope,
}
}

func (l *logger) Emit(ctx context.Context, r log.Record) {
// TODO (#5061): Implement.
newRecord := l.newRecord(ctx, r)
for _, p := range l.provider.processors {
if err := p.OnEmit(ctx, newRecord); err != nil {
otel.Handle(err)
}
}
}

func (l *logger) Enabled(ctx context.Context, r log.Record) bool {
// TODO (#5061): Implement.
return true
newRecord := l.newRecord(ctx, r)
for _, p := range l.provider.processors {
if enabled := p.Enabled(ctx, newRecord); enabled {
return true
}
}
return false
}

func (l *logger) newRecord(ctx context.Context, r log.Record) Record {
sc := trace.SpanContextFromContext(ctx)

newRecord := Record{
timestamp: r.Timestamp(),
observedTimestamp: r.ObservedTimestamp(),
severity: r.Severity(),
severityText: r.SeverityText(),
body: r.Body(),

traceID: sc.TraceID(),
spanID: sc.SpanID(),
traceFlags: sc.TraceFlags(),

resource: l.provider.resource,
scope: &l.instrumentationScope,
attributeValueLengthLimit: l.provider.attributeValueLengthLimit,
attributeCountLimit: l.provider.attributeCountLimit,
}
r.WalkAttributes(func(kv log.KeyValue) bool {
newRecord.AddAttributes(kv)
return true
})

return newRecord
}
231 changes: 231 additions & 0 deletions sdk/log/logger_test.go
@@ -0,0 +1,231 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package log // import "go.opentelemetry.io/otel/sdk/log"
import (
"context"
"errors"
"testing"
"time"

"github.com/stretchr/testify/assert"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/log"
"go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/trace"
)

func TestLoggerEmit(t *testing.T) {
p0, p1, p2WithError := newProcessor("0"), newProcessor("1"), newProcessor("2")
p2WithError.Err = errors.New("error")

r := log.Record{}
r.SetTimestamp(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC))
r.SetBody(log.StringValue("testing body value"))
r.SetSeverity(log.SeverityInfo)
r.SetSeverityText("testing text")
r.AddAttributes(
log.String("k1", "str"),
log.Float64("k2", 1.0),
)
r.SetObservedTimestamp(time.Date(2001, time.January, 1, 0, 0, 0, 0, time.UTC))

contextWithSpanContext := trace.ContextWithSpanContext(context.Background(), trace.NewSpanContext(trace.SpanContextConfig{
TraceID: trace.TraceID{0o1},
SpanID: trace.SpanID{0o2},
TraceFlags: 0x1,
}))

testCases := []struct {
name string
logger *logger
ctx context.Context
record log.Record
expectedRecords []Record
}{
{
name: "NoProcessors",
logger: newLogger(NewLoggerProvider(), instrumentation.Scope{}),
ctx: context.Background(),
record: r,
},
{
name: "WithProcessors",
logger: newLogger(NewLoggerProvider(
WithProcessor(p0),
WithProcessor(p1),
WithAttributeValueLengthLimit(3),
WithAttributeCountLimit(2),
WithResource(resource.NewSchemaless(attribute.String("key", "value"))),
), instrumentation.Scope{Name: "scope"}),
ctx: context.Background(),
record: r,
expectedRecords: []Record{
{
timestamp: r.Timestamp(),
body: r.Body(),
severity: r.Severity(),
severityText: r.SeverityText(),
observedTimestamp: r.ObservedTimestamp(),
resource: resource.NewSchemaless(attribute.String("key", "value")),
attributeValueLengthLimit: 3,
attributeCountLimit: 2,
scope: &instrumentation.Scope{Name: "scope"},
front: [attributesInlineCount]log.KeyValue{
log.String("k1", "str"),
log.Float64("k2", 1.0),
},
nFront: 2,
},
},
},
{
name: "WithProcessorsWithError",
logger: newLogger(NewLoggerProvider(
WithProcessor(p2WithError),
WithAttributeValueLengthLimit(3),
WithAttributeCountLimit(2),
WithResource(resource.NewSchemaless(attribute.String("key", "value"))),
), instrumentation.Scope{Name: "scope"}),
ctx: context.Background(),
},
{
name: "WithTraceSpanInContext",
logger: newLogger(NewLoggerProvider(
WithProcessor(p0),
WithProcessor(p1),
WithAttributeValueLengthLimit(3),
WithAttributeCountLimit(2),
WithResource(resource.NewSchemaless(attribute.String("key", "value"))),
), instrumentation.Scope{Name: "scope"}),
ctx: contextWithSpanContext,
record: r,
expectedRecords: []Record{
{
timestamp: r.Timestamp(),
body: r.Body(),
severity: r.Severity(),
severityText: r.SeverityText(),
observedTimestamp: r.ObservedTimestamp(),
resource: resource.NewSchemaless(attribute.String("key", "value")),
attributeValueLengthLimit: 3,
attributeCountLimit: 2,
scope: &instrumentation.Scope{Name: "scope"},
front: [attributesInlineCount]log.KeyValue{
log.String("k1", "str"),
log.Float64("k2", 1.0),
},
nFront: 2,
traceID: trace.TraceID{0o1},
spanID: trace.SpanID{0o2},
traceFlags: 0x1,
},
},
},
{
name: "WithNilContext",
logger: newLogger(NewLoggerProvider(
WithProcessor(p0),
WithProcessor(p1),
WithAttributeValueLengthLimit(3),
WithAttributeCountLimit(2),
WithResource(resource.NewSchemaless(attribute.String("key", "value"))),
), instrumentation.Scope{Name: "scope"}),
ctx: context.Background(),
record: r,
expectedRecords: []Record{
{
timestamp: r.Timestamp(),
body: r.Body(),
severity: r.Severity(),
severityText: r.SeverityText(),
observedTimestamp: r.ObservedTimestamp(),
resource: resource.NewSchemaless(attribute.String("key", "value")),
attributeValueLengthLimit: 3,
attributeCountLimit: 2,
scope: &instrumentation.Scope{Name: "scope"},
front: [attributesInlineCount]log.KeyValue{
log.String("k1", "str"),
log.Float64("k2", 1.0),
},
nFront: 2,
},
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Clean up the records before the test.
p0.records = nil
p1.records = nil

tc.logger.Emit(tc.ctx, tc.record)

assert.Equal(t, tc.expectedRecords, p0.records)
assert.Equal(t, tc.expectedRecords, p1.records)
})
}
}

func TestLoggerEnabled(t *testing.T) {
p0, p1, p2WithDisabled := newProcessor("0"), newProcessor("1"), newProcessor("2")
p2WithDisabled.enabled = false

testCases := []struct {
name string
logger *logger
ctx context.Context
expected bool
}{
{
name: "NoProcessors",
logger: newLogger(NewLoggerProvider(), instrumentation.Scope{}),
ctx: context.Background(),
expected: false,
},
{
name: "WithProcessors",
logger: newLogger(NewLoggerProvider(
WithProcessor(p0),
WithProcessor(p1),
), instrumentation.Scope{}),
ctx: context.Background(),
expected: true,
},
{
name: "WithDisabledProcessors",
logger: newLogger(NewLoggerProvider(
WithProcessor(p2WithDisabled),
), instrumentation.Scope{}),
ctx: context.Background(),
expected: false,
},
{
name: "ContainsDisabledProcessor",
logger: newLogger(NewLoggerProvider(
WithProcessor(p2WithDisabled),
WithProcessor(p0),
), instrumentation.Scope{}),
ctx: context.Background(),
expected: true,
},
{
name: "WithNilContext",
logger: newLogger(NewLoggerProvider(
WithProcessor(p0),
WithProcessor(p1),
), instrumentation.Scope{}),
ctx: nil,
expected: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.expected, tc.logger.Enabled(tc.ctx, log.Record{}))
})
}
}
18 changes: 13 additions & 5 deletions sdk/log/provider_test.go
Expand Up @@ -27,18 +27,26 @@ type processor struct {

shutdownCalls int
forceFlushCalls int

records []Record
enabled bool
}

func newProcessor(name string) *processor {
return &processor{Name: name}
return &processor{Name: name, enabled: true}
}

func (p *processor) OnEmit(context.Context, Record) error {
return p.Err
func (p *processor) OnEmit(ctx context.Context, r Record) error {
if p.Err != nil {
return p.Err
}

p.records = append(p.records, r)
return nil
}

func (*processor) Enabled(context.Context, Record) bool {
return true
func (p *processor) Enabled(context.Context, Record) bool {
return p.enabled
}

func (p *processor) Shutdown(context.Context) error {
Expand Down

0 comments on commit 77d9192

Please sign in to comment.