1
0
Fork 0

fix: otel not working without USER

This commit is contained in:
Michael 2025-10-03 14:48:04 +02:00 committed by GitHub
parent ad566ee9ef
commit c5ed376d5f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
127 changed files with 347 additions and 305 deletions

View file

@ -0,0 +1,16 @@
package logs
import (
"github.com/aws/smithy-go/logging"
"github.com/rs/zerolog"
)
func NewAWSWrapper(logger zerolog.Logger) logging.LoggerFunc {
if logger.GetLevel() > zerolog.DebugLevel {
return func(classification logging.Classification, format string, args ...interface{}) {}
}
return func(classification logging.Classification, format string, args ...interface{}) {
logger.Debug().CallerSkipFrame(2).MsgFunc(msgFunc(args...))
}
}

View file

@ -0,0 +1,25 @@
package logs
import (
"bytes"
"os"
"testing"
"time"
"github.com/aws/smithy-go/logging"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func TestNewAWSWrapper(t *testing.T) {
buf := bytes.NewBuffer(nil)
cwb := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.RFC3339, NoColor: true}
out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}, cwb)
logger := NewAWSWrapper(zerolog.New(out).With().Caller().Logger())
logger.Logf(logging.Debug, "%s", "foo")
assert.Equal(t, "<nil> DBG aws_test.go:22 > foo\n", buf.String())
}

View file

@ -0,0 +1,15 @@
package logs
import "github.com/rs/zerolog"
type DatadogLogger struct {
logger zerolog.Logger
}
func NewDatadogLogger(logger zerolog.Logger) *DatadogLogger {
return &DatadogLogger{logger: logger}
}
func (d DatadogLogger) Log(msg string) {
d.logger.Debug().CallerSkipFrame(1).Msg(msg)
}

View file

@ -0,0 +1,24 @@
package logs
import (
"bytes"
"os"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func TestNewDatadogLogger(t *testing.T) {
buf := bytes.NewBuffer(nil)
cwb := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.RFC3339, NoColor: true}
out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}, cwb)
logger := NewDatadogLogger(zerolog.New(out).With().Caller().Logger())
logger.Log("foo")
assert.Equal(t, "<nil> DBG datadog_test.go:21 > foo\n", buf.String())
}

View file

@ -0,0 +1,19 @@
package logs
import "github.com/rs/zerolog"
type ElasticLogger struct {
logger zerolog.Logger
}
func NewElasticLogger(logger zerolog.Logger) *ElasticLogger {
return &ElasticLogger{logger: logger}
}
func (l ElasticLogger) Debugf(format string, args ...interface{}) {
l.logger.Debug().CallerSkipFrame(1).Msgf(format, args...)
}
func (l ElasticLogger) Errorf(format string, args ...interface{}) {
l.logger.Error().CallerSkipFrame(1).Msgf(format, args...)
}

View file

@ -0,0 +1,24 @@
package logs
import (
"bytes"
"os"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func TestNewElasticLogger(t *testing.T) {
buf := bytes.NewBuffer(nil)
cwb := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.RFC3339, NoColor: true}
out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}, cwb)
logger := NewElasticLogger(zerolog.New(out).With().Caller().Logger())
logger.Errorf("foo")
assert.Equal(t, "<nil> ERR elastic_test.go:21 > foo\n", buf.String())
}

View file

@ -0,0 +1,17 @@
package logs
// Log entry names.
const (
EntryPointName = "entryPointName"
RouterName = "routerName"
Rule = "rule"
MiddlewareName = "middlewareName"
MiddlewareType = "middlewareType"
ProviderName = "providerName"
ServiceName = "serviceName"
MetricsProviderName = "metricsProviderName"
TracingProviderName = "tracingProviderName"
ServerIndex = "serverIndex"
TLSStoreName = "tlsStoreName"
ServersTransportName = "serversTransport"
)

View file

@ -0,0 +1,17 @@
package logs
import (
kitlog "github.com/go-kit/log"
"github.com/rs/zerolog"
)
func NewGoKitWrapper(logger zerolog.Logger) kitlog.LoggerFunc {
if logger.GetLevel() > zerolog.DebugLevel {
return func(args ...interface{}) error { return nil }
}
return func(args ...interface{}) error {
logger.Debug().CallerSkipFrame(2).MsgFunc(msgFunc(args...))
return nil
}
}

View file

@ -0,0 +1,24 @@
package logs
import (
"bytes"
"os"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func TestNewGoKitWrapper(t *testing.T) {
buf := bytes.NewBuffer(nil)
cwb := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.RFC3339, NoColor: true}
out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}, cwb)
logger := NewGoKitWrapper(zerolog.New(out).With().Caller().Logger())
_ = logger.Log("foo")
assert.Equal(t, "<nil> DBG gokit_test.go:21 > foo\n", buf.String())
}

View file

@ -0,0 +1,75 @@
package logs
import (
"fmt"
"strings"
"unicode"
"github.com/rs/zerolog"
)
// RetryableHTTPLogger wraps our logger and implements retryablehttp.LeveledLogger.
// The retry library sends fields as pairs of keys and values as structured logging,
// so we need to adapt them to our logger.
type RetryableHTTPLogger struct {
logger zerolog.Logger
}
// NewRetryableHTTPLogger creates an implementation of the retryablehttp.LeveledLogger.
func NewRetryableHTTPLogger(logger zerolog.Logger) *RetryableHTTPLogger {
return &RetryableHTTPLogger{logger: logger}
}
// Error starts a new message with error level.
func (l RetryableHTTPLogger) Error(msg string, keysAndValues ...interface{}) {
logWithLevel(l.logger.Error().CallerSkipFrame(2), msg, keysAndValues...)
}
// Info starts a new message with info level.
func (l RetryableHTTPLogger) Info(msg string, keysAndValues ...interface{}) {
logWithLevel(l.logger.Info().CallerSkipFrame(2), msg, keysAndValues...)
}
// Debug starts a new message with debug level.
func (l RetryableHTTPLogger) Debug(msg string, keysAndValues ...interface{}) {
logWithLevel(l.logger.Debug().CallerSkipFrame(2), msg, keysAndValues...)
}
// Warn starts a new message with warn level.
func (l RetryableHTTPLogger) Warn(msg string, keysAndValues ...interface{}) {
logWithLevel(l.logger.Warn().CallerSkipFrame(2), msg, keysAndValues...)
}
func logWithLevel(ev *zerolog.Event, msg string, kvs ...interface{}) {
if len(kvs)%2 == 0 {
for i := 0; i < len(kvs)-1; i += 2 {
// The first item of the pair (the key) is supposed to be a string.
key, ok := kvs[i].(string)
if !ok {
continue
}
val := kvs[i+1]
var s fmt.Stringer
if s, ok = val.(fmt.Stringer); ok {
ev.Str(key, s.String())
} else {
ev.Interface(key, val)
}
}
}
// Capitalize first character.
first := true
msg = strings.Map(func(r rune) rune {
if first {
first = false
return unicode.ToTitle(r)
}
return r
}, msg)
ev.Msg(msg)
}

View file

@ -0,0 +1,24 @@
package logs
import (
"bytes"
"os"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func TestNewRetryableHTTPLogger(t *testing.T) {
buf := bytes.NewBuffer(nil)
cwb := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.RFC3339, NoColor: true}
out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}, cwb)
logger := NewRetryableHTTPLogger(zerolog.New(out).With().Caller().Logger())
logger.Info("foo")
assert.Equal(t, "<nil> INF hclog_test.go:21 > Foo\n", buf.String())
}

View file

@ -0,0 +1,29 @@
package logs
import (
"github.com/rs/zerolog"
)
type InstanaLogger struct {
logger zerolog.Logger
}
func NewInstanaLogger(logger zerolog.Logger) *InstanaLogger {
return &InstanaLogger{logger: logger}
}
func (l InstanaLogger) Debug(args ...interface{}) {
l.logger.Debug().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l InstanaLogger) Info(args ...interface{}) {
l.logger.Info().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l InstanaLogger) Warn(args ...interface{}) {
l.logger.Warn().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l InstanaLogger) Error(args ...interface{}) {
l.logger.Error().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}

View file

@ -0,0 +1,24 @@
package logs
import (
"bytes"
"os"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func TestNewInstanaLogger(t *testing.T) {
buf := bytes.NewBuffer(nil)
cwb := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.RFC3339, NoColor: true}
out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}, cwb)
logger := NewInstanaLogger(zerolog.New(out).With().Caller().Logger())
logger.Info("foo")
assert.Equal(t, "<nil> INF instana_test.go:21 > foo\n", buf.String())
}

View file

@ -0,0 +1,35 @@
package logs
import (
"fmt"
"github.com/rs/zerolog"
)
func NoLevel(logger zerolog.Logger, level zerolog.Level) zerolog.Logger {
return logger.Hook(NewNoLevelHook(logger.GetLevel(), level))
}
type NoLevelHook struct {
minLevel zerolog.Level
level zerolog.Level
}
func NewNoLevelHook(minLevel zerolog.Level, level zerolog.Level) *NoLevelHook {
return &NoLevelHook{minLevel: minLevel, level: level}
}
func (n NoLevelHook) Run(e *zerolog.Event, level zerolog.Level, _ string) {
if n.minLevel > n.level {
e.Discard()
return
}
if level == zerolog.NoLevel {
e.Str("level", n.level.String())
}
}
func msgFunc(i ...any) func() string {
return func() string { return fmt.Sprint(i...) }
}

View file

@ -0,0 +1,24 @@
package logs
import (
"bytes"
"os"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func TestNoLevel(t *testing.T) {
buf := bytes.NewBuffer(nil)
cwb := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.RFC3339, NoColor: true}
out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}, cwb)
logger := NoLevel(zerolog.New(out).With().Caller().Logger(), zerolog.DebugLevel)
logger.Info().Msg("foo")
assert.Equal(t, "<nil> INF log_test.go:21 > foo\n", buf.String())
}

View file

@ -0,0 +1,49 @@
package logs
import (
"github.com/rs/zerolog"
)
type LogrusStdWrapper struct {
logger zerolog.Logger
}
func NewLogrusWrapper(logger zerolog.Logger) *LogrusStdWrapper {
return &LogrusStdWrapper{logger: logger}
}
func (l LogrusStdWrapper) Print(args ...interface{}) {
l.logger.Debug().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l LogrusStdWrapper) Printf(s string, args ...interface{}) {
l.logger.Debug().CallerSkipFrame(1).Msgf(s, args...)
}
func (l LogrusStdWrapper) Println(args ...interface{}) {
l.logger.Debug().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l LogrusStdWrapper) Fatal(args ...interface{}) {
l.logger.Fatal().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l LogrusStdWrapper) Fatalf(s string, args ...interface{}) {
l.logger.Fatal().CallerSkipFrame(1).Msgf(s, args...)
}
func (l LogrusStdWrapper) Fatalln(args ...interface{}) {
l.logger.Fatal().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l LogrusStdWrapper) Panic(args ...interface{}) {
l.logger.Panic().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l LogrusStdWrapper) Panicf(s string, args ...interface{}) {
l.logger.Panic().CallerSkipFrame(1).Msgf(s, args...)
}
func (l LogrusStdWrapper) Panicln(args ...interface{}) {
l.logger.Panic().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}

View file

@ -0,0 +1,24 @@
package logs
import (
"bytes"
"os"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func TestNewLogrusStdWrapper(t *testing.T) {
buf := bytes.NewBuffer(nil)
cwb := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.RFC3339, NoColor: true}
out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}, cwb)
logger := NewLogrusWrapper(zerolog.New(out).With().Caller().Logger())
logger.Println("foo")
assert.Equal(t, "<nil> DBG logrus_test.go:21 > foo\n", buf.String())
}

View file

@ -0,0 +1,125 @@
package logs
import (
"context"
"encoding/json"
"fmt"
"reflect"
"time"
"github.com/rs/zerolog"
"github.com/traefik/traefik/v3/pkg/observability"
"github.com/traefik/traefik/v3/pkg/observability/types"
otellog "go.opentelemetry.io/otel/log"
)
// SetupOTelLogger sets up the OpenTelemetry logger.
func SetupOTelLogger(ctx context.Context, logger zerolog.Logger, config *types.OTelLog) (zerolog.Logger, error) {
if config == nil {
return logger, nil
}
if err := observability.EnsureUserEnvVar(); err != nil {
return zerolog.Logger{}, err
}
provider, err := config.NewLoggerProvider(ctx)
if err != nil {
return zerolog.Logger{}, fmt.Errorf("setting up OpenTelemetry logger provider: %w", err)
}
return logger.Hook(&otelLoggerHook{logger: provider.Logger("traefik")}), nil
}
// otelLoggerHook is a zerolog hook that forwards logs to OpenTelemetry.
type otelLoggerHook struct {
logger otellog.Logger
}
// Run forwards the log message to OpenTelemetry.
func (h *otelLoggerHook) Run(e *zerolog.Event, level zerolog.Level, message string) {
if level == zerolog.Disabled {
return
}
// Discard the event to avoid double logging.
e.Discard()
var record otellog.Record
record.SetTimestamp(time.Now().UTC())
record.SetSeverity(otelLogSeverity(level))
record.SetBody(otellog.StringValue(message))
// See https://github.com/rs/zerolog/issues/493.
// This is a workaround to get the log fields from the event.
// At the moment there's no way to get the log fields from the event, so we use reflection to get the buffer and parse it.
logData := make(map[string]any)
eventBuffer := fmt.Sprintf("%s}", reflect.ValueOf(e).Elem().FieldByName("buf"))
if err := json.Unmarshal([]byte(eventBuffer), &logData); err != nil {
record.AddAttributes(otellog.String("parsing_error", fmt.Sprintf("parsing log fields: %s", err)))
h.logger.Emit(e.GetCtx(), record)
return
}
recordAttributes := make([]otellog.KeyValue, 0, len(logData))
for k, v := range logData {
if k == "level" {
continue
}
if k == "time" {
eventTimestamp, ok := v.(string)
if !ok {
continue
}
t, err := time.Parse(time.RFC3339, eventTimestamp)
if err == nil {
record.SetTimestamp(t)
continue
}
}
var attributeValue otellog.Value
switch v := v.(type) {
case string:
attributeValue = otellog.StringValue(v)
case int:
attributeValue = otellog.IntValue(v)
case int64:
attributeValue = otellog.Int64Value(v)
case float64:
attributeValue = otellog.Float64Value(v)
case bool:
attributeValue = otellog.BoolValue(v)
case []byte:
attributeValue = otellog.BytesValue(v)
default:
attributeValue = otellog.StringValue(fmt.Sprintf("%v", v))
}
recordAttributes = append(recordAttributes, otellog.KeyValue{
Key: k,
Value: attributeValue,
})
}
record.AddAttributes(recordAttributes...)
h.logger.Emit(e.GetCtx(), record)
}
func otelLogSeverity(level zerolog.Level) otellog.Severity {
switch level {
case zerolog.TraceLevel:
return otellog.SeverityTrace
case zerolog.DebugLevel:
return otellog.SeverityDebug
case zerolog.InfoLevel:
return otellog.SeverityInfo
case zerolog.WarnLevel:
return otellog.SeverityWarn
case zerolog.ErrorLevel:
return otellog.SeverityError
case zerolog.FatalLevel:
return otellog.SeverityFatal
case zerolog.PanicLevel:
return otellog.SeverityFatal4
default:
return otellog.SeverityUndefined
}
}

View file

@ -0,0 +1,196 @@
package logs
import (
"compress/gzip"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
otypes "github.com/traefik/traefik/v3/pkg/observability/types"
"go.opentelemetry.io/collector/pdata/plog/plogotlp"
"go.opentelemetry.io/otel/trace"
)
func TestLog(t *testing.T) {
tests := []struct {
desc string
level zerolog.Level
assertFn func(*testing.T, string)
noLog bool
}{
{
desc: "no level log",
level: zerolog.NoLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityUndefined Severity = 0 // UNDEFINED
assert.NotContains(t, log, `"severityNumber"`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "trace log",
level: zerolog.TraceLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityTrace1 Severity = 1 // TRACE
assert.Contains(t, log, `"severityNumber":1`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "debug log",
level: zerolog.DebugLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityDebug1 Severity = 5 // DEBUG
assert.Contains(t, log, `"severityNumber":5`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "info log",
level: zerolog.InfoLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityInfo1 Severity = 9 // INFO
assert.Contains(t, log, `"severityNumber":9`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "warn log",
level: zerolog.WarnLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityWarn1 Severity = 13 // WARN
assert.Contains(t, log, `"severityNumber":13`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "error log",
level: zerolog.ErrorLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityError1 Severity = 17 // ERROR
assert.Contains(t, log, `"severityNumber":17`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "fatal log",
level: zerolog.FatalLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityFatal Severity = 21 // FATAL
assert.Contains(t, log, `"severityNumber":21`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "panic log",
level: zerolog.PanicLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityFatal4 Severity = 24 // FATAL
assert.Contains(t, log, `"severityNumber":24`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
}
logCh := make(chan string)
collector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gzr, err := gzip.NewReader(r.Body)
require.NoError(t, err)
body, err := io.ReadAll(gzr)
require.NoError(t, err)
req := plogotlp.NewExportRequest()
err = req.UnmarshalProto(body)
require.NoError(t, err)
marshalledReq, err := json.Marshal(req)
require.NoError(t, err)
logCh <- string(marshalledReq)
}))
t.Cleanup(collector.Close)
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
config := &otypes.OTelLog{
ServiceName: "test",
ResourceAttributes: map[string]string{"resource": "attribute"},
HTTP: &otypes.OTelHTTP{
Endpoint: collector.URL,
},
}
out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339})
logger := zerolog.New(out).With().Caller().Logger()
logger, err := SetupOTelLogger(t.Context(), logger, config)
require.NoError(t, err)
ctx := trace.ContextWithSpanContext(t.Context(), trace.NewSpanContext(trace.SpanContextConfig{
TraceID: trace.TraceID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
SpanID: trace.SpanID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
}))
logger = logger.With().Ctx(ctx).Logger()
logger.WithLevel(test.level).Str("foo", "bar").Msg("test")
select {
case <-time.After(5 * time.Second):
t.Error("Log not exported")
case log := <-logCh:
if test.assertFn != nil {
test.assertFn(t, log)
}
}
})
}
}

View file

@ -0,0 +1,27 @@
package logs
import "github.com/rs/zerolog"
type OxyWrapper struct {
logger zerolog.Logger
}
func NewOxyWrapper(logger zerolog.Logger) *OxyWrapper {
return &OxyWrapper{logger: logger}
}
func (l OxyWrapper) Debug(s string, i ...interface{}) {
l.logger.Debug().CallerSkipFrame(1).Msgf(s, i...)
}
func (l OxyWrapper) Info(s string, i ...interface{}) {
l.logger.Info().CallerSkipFrame(1).Msgf(s, i...)
}
func (l OxyWrapper) Warn(s string, i ...interface{}) {
l.logger.Warn().CallerSkipFrame(1).Msgf(s, i...)
}
func (l OxyWrapper) Error(s string, i ...interface{}) {
l.logger.Error().CallerSkipFrame(1).Msgf(s, i...)
}

View file

@ -0,0 +1,24 @@
package logs
import (
"bytes"
"os"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
func TestNewOxyWrapper(t *testing.T) {
buf := bytes.NewBuffer(nil)
cwb := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.RFC3339, NoColor: true}
out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}, cwb)
logger := NewOxyWrapper(zerolog.New(out).With().Caller().Logger())
logger.Info("foo")
assert.Equal(t, "<nil> INF oxy_test.go:21 > foo\n", buf.String())
}

View file

@ -0,0 +1,32 @@
package logs
import (
"context"
"github.com/http-wasm/http-wasm-host-go/api"
"github.com/rs/zerolog"
)
// compile-time check to ensure ConsoleLogger implements api.Logger.
var _ api.Logger = WasmLogger{}
// WasmLogger is a convenience which writes anything above LogLevelInfo to os.Stdout.
type WasmLogger struct {
logger *zerolog.Logger
}
func NewWasmLogger(logger *zerolog.Logger) *WasmLogger {
return &WasmLogger{
logger: logger,
}
}
// IsEnabled implements the same method as documented on api.Logger.
func (w WasmLogger) IsEnabled(level api.LogLevel) bool {
return true
}
// Log implements the same method as documented on api.Logger.
func (w WasmLogger) Log(_ context.Context, level api.LogLevel, message string) {
w.logger.WithLevel(zerolog.Level(level + 1)).Msg(message)
}