Skip to content

Commit

Permalink
Implement the providerConfig (#5074)
Browse files Browse the repository at this point in the history
* Implement the providerConfig

* Add test for NewLoggerProvider configuration

* Add TestLimitValueFailsOpen

* Fix merge
  • Loading branch information
MrAlias authored Mar 15, 2024
1 parent da047e7 commit 3a72c5e
Show file tree
Hide file tree
Showing 2 changed files with 223 additions and 9 deletions.
106 changes: 97 additions & 9 deletions sdk/log/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,105 @@ package log // import "go.opentelemetry.io/otel/sdk/log"

import (
"context"
"fmt"
"os"
"strconv"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/log"
"go.opentelemetry.io/otel/log/embedded"
"go.opentelemetry.io/otel/sdk/resource"
)

// Compile-time check LoggerProvider implements log.LoggerProvider.
var _ log.LoggerProvider = (*LoggerProvider)(nil)
const (
defaultAttrCntLim = 128
defaultAttrValLenLim = -1

envarAttrCntLim = "OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT"
envarAttrValLenLim = "OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT"
)

type providerConfig struct {
resource *resource.Resource
processors []Processor
attrCntLim limit
attrValLenLim limit
}

func newProviderConfig(opts []LoggerProviderOption) providerConfig {
var c providerConfig
for _, opt := range opts {
c = opt.apply(c)
}

if c.resource == nil {
c.resource = resource.Default()
}

c.attrCntLim = c.attrCntLim.Resolve(
envarAttrCntLim,
defaultAttrCntLim,
)

c.attrValLenLim = c.attrValLenLim.Resolve(
envarAttrValLenLim,
defaultAttrValLenLim,
)

return c
}

type limit struct {
value int
set bool
}

func newLimit(value int) limit {
return limit{value: value, set: true}
}

// Resolve returns the resolved form of the limit l. If l's value is set, it
// will return l. If the l's value is not set, a new limit based on the
// environment variable envar will be returned if that environment variable is
// set. Otherwise, fallback is used to construct a new limit that is returned.
func (l limit) Resolve(envar string, fallback int) limit {
if l.set {
return l
}

if v := os.Getenv(envar); v != "" {
n, err := strconv.Atoi(v)
if err == nil {
return newLimit(n)
}
otel.Handle(fmt.Errorf("invalid %s value %s: %w", envar, v, err))
}

return newLimit(fallback)
}

// Value returns the limit value if set. Otherwise, it returns -1.
func (l limit) Value() int {
if l.set {
return l.value
}
// Fail open, not closed (-1 == unlimited).
return -1
}

// LoggerProvider handles the creation and coordination of Loggers. All Loggers
// created by a LoggerProvider will be associated with the same Resource.
type LoggerProvider struct {
embedded.LoggerProvider

resource *resource.Resource
processors []Processor
attributeCountLimit int
attributeValueLengthLimit int
}

type providerConfig struct{}
// Compile-time check LoggerProvider implements log.LoggerProvider.
var _ log.LoggerProvider = (*LoggerProvider)(nil)

// NewLoggerProvider returns a new and configured LoggerProvider.
//
Expand All @@ -29,8 +112,13 @@ type providerConfig struct{}
// created. This means the returned LoggerProvider, one created with no
// Processors, will perform no operations.
func NewLoggerProvider(opts ...LoggerProviderOption) *LoggerProvider {
// TODO (#5060): Implement.
return nil
cfg := newProviderConfig(opts)
return &LoggerProvider{
resource: cfg.resource,
processors: cfg.processors,
attributeCountLimit: cfg.attrCntLim.Value(),
attributeValueLengthLimit: cfg.attrValLenLim.Value(),
}
}

// Logger returns a new [log.Logger] with the provided name and configuration.
Expand Down Expand Up @@ -76,7 +164,7 @@ func (fn loggerProviderOptionFunc) apply(c providerConfig) providerConfig {
// go.opentelemetry.io/otel/sdk/resource package will be used.
func WithResource(res *resource.Resource) LoggerProviderOption {
return loggerProviderOptionFunc(func(cfg providerConfig) providerConfig {
// TODO (#5060): Implement.
cfg.resource = res
return cfg
})
}
Expand All @@ -93,7 +181,7 @@ func WithResource(res *resource.Resource) LoggerProviderOption {
// For testing and debugging, use [NewSimpleProcessor] to synchronously export log records.
func WithProcessor(processor Processor) LoggerProviderOption {
return loggerProviderOptionFunc(func(cfg providerConfig) providerConfig {
// TODO (#5060): Implement.
cfg.processors = append(cfg.processors, processor)
return cfg
})
}
Expand All @@ -112,7 +200,7 @@ func WithProcessor(processor Processor) LoggerProviderOption {
// passed, 128 will be used.
func WithAttributeCountLimit(limit int) LoggerProviderOption {
return loggerProviderOptionFunc(func(cfg providerConfig) providerConfig {
// TODO (#5060): Implement.
cfg.attrCntLim = newLimit(limit)
return cfg
})
}
Expand All @@ -131,7 +219,7 @@ func WithAttributeCountLimit(limit int) LoggerProviderOption {
// passed, no limit (-1) will be used.
func WithAttributeValueLengthLimit(limit int) LoggerProviderOption {
return loggerProviderOptionFunc(func(cfg providerConfig) providerConfig {
// TODO (#5060): Implement.
cfg.attrValLenLim = newLimit(limit)
return cfg
})
}
126 changes: 126 additions & 0 deletions sdk/log/provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package log // import "go.opentelemetry.io/otel/sdk/log"

import (
"context"
"strconv"
"testing"

"github.com/stretchr/testify/assert"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/resource"
)

type processor struct {
name string
}

func (processor) OnEmit(context.Context, Record) error { return nil }
func (processor) Enabled(context.Context, Record) bool { return true }
func (processor) Shutdown(context.Context) error { return nil }
func (processor) ForceFlush(context.Context) error { return nil }

func TestNewLoggerProviderConfiguration(t *testing.T) {
t.Cleanup(func(orig otel.ErrorHandler) func() {
otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {
t.Log(err)
}))
return func() { otel.SetErrorHandler(orig) }
}(otel.GetErrorHandler()))

res := resource.NewSchemaless(attribute.String("key", "value"))
p0, p1 := processor{name: "0"}, processor{name: "1"}
attrCntLim := 12
attrValLenLim := 21

testcases := []struct {
name string
envars map[string]string
options []LoggerProviderOption
want *LoggerProvider
}{
{
name: "Defaults",
want: &LoggerProvider{
resource: resource.Default(),
attributeCountLimit: defaultAttrCntLim,
attributeValueLengthLimit: defaultAttrValLenLim,
},
},
{
name: "Options",
options: []LoggerProviderOption{
WithResource(res),
WithProcessor(p0),
WithProcessor(p1),
WithAttributeCountLimit(attrCntLim),
WithAttributeValueLengthLimit(attrValLenLim),
},
want: &LoggerProvider{
resource: res,
processors: []Processor{p0, p1},
attributeCountLimit: attrCntLim,
attributeValueLengthLimit: attrValLenLim,
},
},
{
name: "Environment",
envars: map[string]string{
envarAttrCntLim: strconv.Itoa(attrCntLim),
envarAttrValLenLim: strconv.Itoa(attrValLenLim),
},
want: &LoggerProvider{
resource: resource.Default(),
attributeCountLimit: attrCntLim,
attributeValueLengthLimit: attrValLenLim,
},
},
{
name: "InvalidEnvironment",
envars: map[string]string{
envarAttrCntLim: "invalid attributeCountLimit",
envarAttrValLenLim: "invalid attributeValueLengthLimit",
},
want: &LoggerProvider{
resource: resource.Default(),
attributeCountLimit: defaultAttrCntLim,
attributeValueLengthLimit: defaultAttrValLenLim,
},
},
{
name: "Precedence",
envars: map[string]string{
envarAttrCntLim: strconv.Itoa(100),
envarAttrValLenLim: strconv.Itoa(101),
},
options: []LoggerProviderOption{
// These override the environment variables.
WithAttributeCountLimit(attrCntLim),
WithAttributeValueLengthLimit(attrValLenLim),
},
want: &LoggerProvider{
resource: resource.Default(),
attributeCountLimit: attrCntLim,
attributeValueLengthLimit: attrValLenLim,
},
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
for key, value := range tc.envars {
t.Setenv(key, value)
}
assert.Equal(t, tc.want, NewLoggerProvider(tc.options...))
})
}
}

func TestLimitValueFailsOpen(t *testing.T) {
var l limit
assert.Equal(t, -1, l.Value(), "limit value should default to unlimited")
}

0 comments on commit 3a72c5e

Please sign in to comment.