You've already forked opentelemetry-go
							
							
				mirror of
				https://github.com/open-telemetry/opentelemetry-go.git
				synced 2025-10-31 00:07:40 +02:00 
			
		
		
		
	Use https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/modernize to update code to new style. --------- Co-authored-by: Flc゛ <four_leaf_clover@foxmail.com> Co-authored-by: Damien Mathieu <42@dmathieu.com>
		
			
				
	
	
		
			420 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			420 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright The OpenTelemetry Authors
 | |
| // SPDX-License-Identifier: Apache-2.0
 | |
| 
 | |
| package log // import "go.opentelemetry.io/otel/sdk/log"
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"strconv"
 | |
| 	"sync"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/go-logr/logr"
 | |
| 	"github.com/go-logr/logr/testr"
 | |
| 	"github.com/google/go-cmp/cmp"
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| 
 | |
| 	"go.opentelemetry.io/otel"
 | |
| 	"go.opentelemetry.io/otel/attribute"
 | |
| 	"go.opentelemetry.io/otel/internal/global"
 | |
| 	"go.opentelemetry.io/otel/log"
 | |
| 	"go.opentelemetry.io/otel/log/noop"
 | |
| 	"go.opentelemetry.io/otel/sdk/resource"
 | |
| )
 | |
| 
 | |
| const envVarResourceAttributes = "OTEL_RESOURCE_ATTRIBUTES"
 | |
| 
 | |
| type processor struct {
 | |
| 	Name string
 | |
| 	Err  error
 | |
| 
 | |
| 	shutdownCalls   int
 | |
| 	forceFlushCalls int
 | |
| 
 | |
| 	records []Record
 | |
| }
 | |
| 
 | |
| func newProcessor(name string) *processor {
 | |
| 	return &processor{Name: name}
 | |
| }
 | |
| 
 | |
| 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 (p *processor) Shutdown(context.Context) error {
 | |
| 	p.shutdownCalls++
 | |
| 	return p.Err
 | |
| }
 | |
| 
 | |
| func (p *processor) ForceFlush(context.Context) error {
 | |
| 	p.forceFlushCalls++
 | |
| 	return p.Err
 | |
| }
 | |
| 
 | |
| type fltrProcessor struct {
 | |
| 	*processor
 | |
| 
 | |
| 	enabled bool
 | |
| 	params  []EnabledParameters
 | |
| }
 | |
| 
 | |
| var _ FilterProcessor = (*fltrProcessor)(nil)
 | |
| 
 | |
| func newFltrProcessor(name string, enabled bool) *fltrProcessor {
 | |
| 	return &fltrProcessor{
 | |
| 		processor: newProcessor(name),
 | |
| 		enabled:   enabled,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (p *fltrProcessor) Enabled(_ context.Context, params EnabledParameters) bool {
 | |
| 	p.params = append(p.params, params)
 | |
| 	return p.enabled
 | |
| }
 | |
| 
 | |
| 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 := newProcessor("0"), newProcessor("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),
 | |
| 				WithAllowKeyDuplication(),
 | |
| 			},
 | |
| 			want: &LoggerProvider{
 | |
| 				resource:                  res,
 | |
| 				processors:                []Processor{p0, p1},
 | |
| 				attributeCountLimit:       attrCntLim,
 | |
| 				attributeValueLengthLimit: attrValLenLim,
 | |
| 				allowDupKeys:              true,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			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 mergeResource(t *testing.T, r1, r2 *resource.Resource) *resource.Resource {
 | |
| 	r, err := resource.Merge(r1, r2)
 | |
| 	assert.NoError(t, err)
 | |
| 	return r
 | |
| }
 | |
| 
 | |
| func TestWithResource(t *testing.T) {
 | |
| 	t.Setenv(envVarResourceAttributes, "key=value,rk5=7")
 | |
| 
 | |
| 	cases := []struct {
 | |
| 		name    string
 | |
| 		options []LoggerProviderOption
 | |
| 		want    *resource.Resource
 | |
| 		msg     string
 | |
| 	}{
 | |
| 		{
 | |
| 			name:    "explicitly empty resource",
 | |
| 			options: []LoggerProviderOption{WithResource(resource.Empty())},
 | |
| 			want:    resource.Environment(),
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "uses default if no resource option",
 | |
| 			options: []LoggerProviderOption{},
 | |
| 			want:    resource.Default(),
 | |
| 		},
 | |
| 		{
 | |
| 			name: "explicit resource",
 | |
| 			options: []LoggerProviderOption{
 | |
| 				WithResource(resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5))),
 | |
| 			},
 | |
| 			want: mergeResource(
 | |
| 				t,
 | |
| 				resource.Environment(),
 | |
| 				resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5)),
 | |
| 			),
 | |
| 		},
 | |
| 		{
 | |
| 			name: "last resource wins",
 | |
| 			options: []LoggerProviderOption{
 | |
| 				WithResource(resource.NewSchemaless(attribute.String("rk1", "vk1"), attribute.Int64("rk2", 5))),
 | |
| 				WithResource(resource.NewSchemaless(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10))),
 | |
| 			},
 | |
| 			want: mergeResource(
 | |
| 				t,
 | |
| 				resource.Environment(),
 | |
| 				resource.NewSchemaless(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10)),
 | |
| 			),
 | |
| 		},
 | |
| 		{
 | |
| 			name: "overlapping attributes with environment resource",
 | |
| 			options: []LoggerProviderOption{
 | |
| 				WithResource(resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10))),
 | |
| 			},
 | |
| 			want: mergeResource(
 | |
| 				t,
 | |
| 				resource.Environment(),
 | |
| 				resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10)),
 | |
| 			),
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tc := range cases {
 | |
| 		t.Run(tc.name, func(t *testing.T) {
 | |
| 			got := newProviderConfig(tc.options).resource
 | |
| 			if diff := cmp.Diff(got, tc.want); diff != "" {
 | |
| 				t.Errorf("WithResource:\n  -got +want %s", diff)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestLoggerProviderConcurrentSafe(t *testing.T) {
 | |
| 	const goRoutineN = 10
 | |
| 
 | |
| 	var wg sync.WaitGroup
 | |
| 	wg.Add(goRoutineN)
 | |
| 
 | |
| 	p := NewLoggerProvider(WithProcessor(newProcessor("0")))
 | |
| 	const name = "testLogger"
 | |
| 	ctx := context.Background()
 | |
| 	for range goRoutineN {
 | |
| 		go func() {
 | |
| 			defer wg.Done()
 | |
| 
 | |
| 			_ = p.Logger(name)
 | |
| 			_ = p.Shutdown(ctx)
 | |
| 			_ = p.ForceFlush(ctx)
 | |
| 		}()
 | |
| 	}
 | |
| 
 | |
| 	wg.Wait()
 | |
| }
 | |
| 
 | |
| type logSink struct {
 | |
| 	logr.LogSink
 | |
| 
 | |
| 	level         int
 | |
| 	msg           string
 | |
| 	keysAndValues []any
 | |
| }
 | |
| 
 | |
| func (l *logSink) Enabled(int) bool { return true }
 | |
| 
 | |
| func (l *logSink) Info(level int, msg string, keysAndValues ...any) {
 | |
| 	l.level, l.msg, l.keysAndValues = level, msg, keysAndValues
 | |
| 	l.LogSink.Info(level, msg, keysAndValues...)
 | |
| }
 | |
| 
 | |
| func TestLoggerProviderLogger(t *testing.T) {
 | |
| 	t.Run("InvalidName", func(t *testing.T) {
 | |
| 		l := &logSink{LogSink: testr.New(t).GetSink()}
 | |
| 		t.Cleanup(func(orig logr.Logger) func() {
 | |
| 			global.SetLogger(logr.New(l))
 | |
| 			return func() { global.SetLogger(orig) }
 | |
| 		}(global.GetLogger()))
 | |
| 
 | |
| 		_ = NewLoggerProvider().Logger("")
 | |
| 		assert.Equal(t, 1, l.level, "logged level")
 | |
| 		assert.Equal(t, "Invalid Logger name.", l.msg, "logged message")
 | |
| 		require.Len(t, l.keysAndValues, 2, "logged key values")
 | |
| 		assert.Empty(t, l.keysAndValues[1], "logged name")
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Stopped", func(t *testing.T) {
 | |
| 		ctx := context.Background()
 | |
| 		p := NewLoggerProvider()
 | |
| 		_ = p.Shutdown(ctx)
 | |
| 		l := p.Logger("testing")
 | |
| 
 | |
| 		assert.NotNil(t, l)
 | |
| 		assert.IsType(t, noop.Logger{}, l)
 | |
| 	})
 | |
| 
 | |
| 	t.Run("SameLoggers", func(t *testing.T) {
 | |
| 		p := NewLoggerProvider()
 | |
| 
 | |
| 		l0, l1, l2 := p.Logger(
 | |
| 			"l0",
 | |
| 		), p.Logger(
 | |
| 			"l1",
 | |
| 		), p.Logger(
 | |
| 			"l0",
 | |
| 			log.WithInstrumentationAttributes(attribute.String("foo", "bar")),
 | |
| 		)
 | |
| 		assert.NotSame(t, l0, l1)
 | |
| 		assert.NotSame(t, l0, l2)
 | |
| 		assert.NotSame(t, l1, l2)
 | |
| 
 | |
| 		l3, l4, l5 := p.Logger(
 | |
| 			"l0",
 | |
| 		), p.Logger(
 | |
| 			"l1",
 | |
| 		), p.Logger(
 | |
| 			"l0",
 | |
| 			log.WithInstrumentationAttributes(attribute.String("foo", "bar")),
 | |
| 		)
 | |
| 		assert.Same(t, l0, l3)
 | |
| 		assert.Same(t, l1, l4)
 | |
| 		assert.Same(t, l2, l5)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestLoggerProviderShutdown(t *testing.T) {
 | |
| 	t.Run("Once", func(t *testing.T) {
 | |
| 		proc := newProcessor("")
 | |
| 		p := NewLoggerProvider(WithProcessor(proc))
 | |
| 
 | |
| 		ctx := context.Background()
 | |
| 		require.NoError(t, p.Shutdown(ctx))
 | |
| 		require.Equal(t, 1, proc.shutdownCalls, "processor Shutdown not called")
 | |
| 
 | |
| 		require.NoError(t, p.Shutdown(ctx))
 | |
| 		assert.Equal(t, 1, proc.shutdownCalls, "processor Shutdown called multiple times")
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Error", func(t *testing.T) {
 | |
| 		proc := newProcessor("")
 | |
| 		proc.Err = assert.AnError
 | |
| 		p := NewLoggerProvider(WithProcessor(proc))
 | |
| 
 | |
| 		ctx := context.Background()
 | |
| 		assert.ErrorIs(t, p.Shutdown(ctx), assert.AnError, "processor error not returned")
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestLoggerProviderForceFlush(t *testing.T) {
 | |
| 	t.Run("Stopped", func(t *testing.T) {
 | |
| 		proc := newProcessor("")
 | |
| 		p := NewLoggerProvider(WithProcessor(proc))
 | |
| 
 | |
| 		ctx := context.Background()
 | |
| 		require.NoError(t, p.ForceFlush(ctx))
 | |
| 		require.Equal(t, 1, proc.forceFlushCalls, "processor ForceFlush not called")
 | |
| 
 | |
| 		require.NoError(t, p.Shutdown(ctx))
 | |
| 
 | |
| 		require.NoError(t, p.ForceFlush(ctx))
 | |
| 		assert.Equal(t, 1, proc.forceFlushCalls, "processor ForceFlush called after Shutdown")
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Multi", func(t *testing.T) {
 | |
| 		proc := newProcessor("")
 | |
| 		p := NewLoggerProvider(WithProcessor(proc))
 | |
| 
 | |
| 		ctx := context.Background()
 | |
| 		require.NoError(t, p.ForceFlush(ctx))
 | |
| 		require.Equal(t, 1, proc.forceFlushCalls, "processor ForceFlush not called")
 | |
| 
 | |
| 		require.NoError(t, p.ForceFlush(ctx))
 | |
| 		assert.Equal(t, 2, proc.forceFlushCalls, "processor ForceFlush not called multiple times")
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Error", func(t *testing.T) {
 | |
| 		proc := newProcessor("")
 | |
| 		proc.Err = assert.AnError
 | |
| 		p := NewLoggerProvider(WithProcessor(proc))
 | |
| 
 | |
| 		ctx := context.Background()
 | |
| 		assert.ErrorIs(t, p.ForceFlush(ctx), assert.AnError, "processor error not returned")
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func BenchmarkLoggerProviderLogger(b *testing.B) {
 | |
| 	p := NewLoggerProvider()
 | |
| 	names := make([]string, b.N)
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		names[i] = fmt.Sprintf("%d logger", i)
 | |
| 	}
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 	b.ReportAllocs()
 | |
| 
 | |
| 	loggers := make([]log.Logger, b.N)
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		loggers[i] = p.Logger(names[i])
 | |
| 	}
 | |
| 
 | |
| 	b.StopTimer()
 | |
| 	loggers[0].Enabled(context.Background(), log.EnabledParameters{})
 | |
| }
 |