From 3695447914bd8ed91585dbc2cc39c1e996c80ee8 Mon Sep 17 00:00:00 2001 From: lovung Date: Fri, 26 Jan 2024 22:35:11 +0800 Subject: [PATCH] feat: add zerolog (#46) - Update README, reorganize hook, fix unit tests, remove unnecessary code in CtxLogf --- logging/zerolog/README.md | 61 ++++++++++++++++++++++++---------- logging/zerolog/logger.go | 27 +++++---------- logging/zerolog/logger_test.go | 11 ++++-- logging/zerolog/option.go | 37 ++++++++++++++++++++- logging/zerolog/utils.go | 4 +-- 5 files changed, 99 insertions(+), 41 deletions(-) diff --git a/logging/zerolog/README.md b/logging/zerolog/README.md index 53b231a..5ac03d4 100644 --- a/logging/zerolog/README.md +++ b/logging/zerolog/README.md @@ -1,27 +1,54 @@ -# Notes -- If you're using the global logger in `github.com/rs/zerolog/log.Logger` to do log. You should add the middleware to pass the OTEL extra information (key, value) into the `context.Context`. -- Example: +# Zerolog + OpenTelemetry + Hezrt + +## [Document](https://www.cloudwego.io/docs/hertz/tutorials/observability/open-telemetry/) + +## Example + +1. See this [example](https://github.com/cloudwego/hertz-examples/tree/main/opentelemetry) +2. Small change in herzt server + ```go import ( - "context" - "time" + // ... - "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/common/hlog" + hertzZerolog "github.com/hertz-contrib/logger/zerolog" + hertzOtelZerolog "github.com/hertz-contrib/obs-opentelemetry/logging/zerolog" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "go.opentelemetry.io/otel/trace" - "github.com/hertz-contrib/obs-opentelemetry/logging/zerolog" ) -func OtelZerologMiddleware() app.HandlerFunc { - return func(ctx context.Context, reqCtx *app.RequestContext) { - ctx = log.Logger.WithContext(ctx) +func main() { + // ... + + p := provider.NewOpenTelemetryProvider( + provider.WithServiceName(serviceName), + // Support setting ExportEndpoint via environment variables: OTEL_EXPORTER_OTLP_ENDPOINT + provider.WithExportEndpoint("localhost:4317"), + provider.WithInsecure(), + ) + defer p.Shutdown(context.Background()) - span := trace.SpanFromContext(ctx) + tracer, cfg := hertztracing.NewServerTracer() + h := server.Default(tracer) + h.Use(hertztracing.ServerMiddleware(cfg)) - ctx = context.WithValue(ctx, zerolog.ExtraKey(zerolog.TraceIDKey), span.SpanContext().TraceID()) - ctx = context.WithValue(ctx, zerolog.ExtraKey(zerolog.SpanIDKey), span.SpanContext().SpanID()) - ctx = context.WithValue(ctx, zerolog.ExtraKey(zerolog.TraceFlagsKey), span.SpanContext().TraceFlags()) + logger := hertzZerolog.New( + hertzZerolog.WithOutput(w), // allows to specify output + hertzZerolog.WithLevel(hlog.LevelInfo), // option with log level + hertzZerolog.WithCaller(), // option with caller + hertzZerolog.WithTimestamp(), // option with timestamp + hertzZerolog.WithFormattedTimestamp(time.RFC3339), + ) - reqCtx.Next(ctx) - } + log.Logger = logger.Unwrap() // log.Output(w).With().Caller().Logger() + log.Logger = log.Level(zerolog.InfoLevel) + + otelLogger := hertzOtelZerolog.NewLogger(hertzOtelZerolog.WithLogger(logger)) + log.Logger = otelLogger.Unwrap() + hlog.SetLogger(otelLogger) + + // ... +} ``` + diff --git a/logging/zerolog/logger.go b/logging/zerolog/logger.go index cb99eb2..199fffc 100644 --- a/logging/zerolog/logger.go +++ b/logging/zerolog/logger.go @@ -1,4 +1,4 @@ -// Copyright 2022 CloudWeGo Authors. +// Copyright 2024 CloudWeGo Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import ( ) type Logger struct { - hertzzerolog.Logger + *hertzzerolog.Logger config *config } @@ -40,33 +40,23 @@ const ( type ExtraKey string func NewLogger(opts ...Option) *Logger { - config := defaultConfig() + cfg := defaultConfig() // apply options for _, opt := range opts { - opt.apply(config) + opt.apply(cfg) } - logger := *config.logger - newLogger := hertzzerolog.From(logger.Unwrap().Hook(zerolog.HookFunc(func(e *zerolog.Event, level zerolog.Level, message string) { - ctx := e.GetCtx() - e.Any(TraceIDKey, ctx.Value(ExtraKey(TraceIDKey))) - e.Any(SpanIDKey, ctx.Value(ExtraKey(SpanIDKey))) - e.Any(TraceFlagsKey, ctx.Value(ExtraKey(TraceFlagsKey))) - }))) + logger := *cfg.logger + zerologLogger := logger.Unwrap().Hook(cfg.getZerologHookFn()) return &Logger{ - Logger: *newLogger, - config: config, + Logger: hertzzerolog.From(zerologLogger), + config: cfg, } } func (l *Logger) CtxLogf(level hlog.Level, ctx context.Context, format string, kvs ...any) { var zlevel zerolog.Level - span := trace.SpanFromContext(ctx) - - ctx = context.WithValue(ctx, ExtraKey(TraceIDKey), span.SpanContext().TraceID()) - ctx = context.WithValue(ctx, ExtraKey(SpanIDKey), span.SpanContext().SpanID()) - ctx = context.WithValue(ctx, ExtraKey(TraceFlagsKey), span.SpanContext().TraceFlags()) switch level { case hlog.LevelDebug, hlog.LevelTrace: @@ -89,6 +79,7 @@ func (l *Logger) CtxLogf(level hlog.Level, ctx context.Context, format string, k l.Logger.CtxWarnf(ctx, format, kvs...) } + span := trace.SpanFromContext(ctx) if !span.IsRecording() { l.Logger.Logf(level, format, kvs...) return diff --git a/logging/zerolog/logger_test.go b/logging/zerolog/logger_test.go index 813abcb..53f4ac3 100644 --- a/logging/zerolog/logger_test.go +++ b/logging/zerolog/logger_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 CloudWeGo Authors. +// Copyright 2024 CloudWeGo Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import ( "testing" "github.com/cloudwego/hertz/pkg/common/hlog" + hertzZerolog "github.com/hertz-contrib/logger/zerolog" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel" @@ -56,14 +57,18 @@ func TestLogger(t *testing.T) { shutdown := stdoutProvider(ctx) defer shutdown() + hertzZerologer := hertzZerolog.New( + hertzZerolog.WithOutput(buf), + hertzZerolog.WithLevel(hlog.LevelDebug), + ) + logger := NewLogger( + WithLogger(hertzZerologer), WithTraceErrorSpanLevel(zerolog.WarnLevel), WithRecordStackTraceInSpan(true), ) hlog.SetLogger(logger) - hlog.SetOutput(buf) - hlog.SetLevel(hlog.LevelDebug) logger.Info("log from origin zerolog") assert.Contains(t, buf.String(), "log from origin zerolog") diff --git a/logging/zerolog/option.go b/logging/zerolog/option.go index 9bd8e39..fd86c67 100644 --- a/logging/zerolog/option.go +++ b/logging/zerolog/option.go @@ -1,4 +1,4 @@ -// Copyright 2022 CloudWeGo Authors. +// Copyright 2024 CloudWeGo Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,8 +15,12 @@ package zerolog import ( + "errors" + hertzzerolog "github.com/hertz-contrib/logger/zerolog" "github.com/rs/zerolog" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" ) type Option interface { @@ -37,6 +41,7 @@ type traceConfig struct { type config struct { logger *hertzzerolog.Logger traceConfig *traceConfig + hookFunc zerolog.HookFunc } // defaultConfig default config @@ -70,3 +75,33 @@ func WithRecordStackTraceInSpan(recordStackTraceInSpan bool) Option { cfg.traceConfig.recordStackTraceInSpan = recordStackTraceInSpan }) } + +func (cfg config) getZerologHookFn() zerolog.HookFunc { + if cfg.hookFunc != nil { + return cfg.hookFunc + } + return func(e *zerolog.Event, level zerolog.Level, message string) { + ctx := e.GetCtx() + span := trace.SpanFromContext(ctx) + spanCtx := span.SpanContext() + + if spanCtx.HasSpanID() { + e.Any(SpanIDKey, spanCtx.SpanID()) + } + if spanCtx.HasTraceID() { + e.Any(TraceIDKey, spanCtx.TraceID()) + } + e.Any(TraceFlagsKey, spanCtx.TraceFlags()) + + if !span.IsRecording() { + return + } + + // set span status + if level >= cfg.traceConfig.errorSpanLevel { + span.SetStatus(codes.Error, "") + span.RecordError(errors.New(message), + trace.WithStackTrace(cfg.traceConfig.recordStackTraceInSpan)) + } + } +} diff --git a/logging/zerolog/utils.go b/logging/zerolog/utils.go index 2fcbee1..b501e98 100644 --- a/logging/zerolog/utils.go +++ b/logging/zerolog/utils.go @@ -1,4 +1,4 @@ -// Copyright 2022 CloudWeGo Authors. +// Copyright 2024 CloudWeGo Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ func getMessage(template string, fmtArgs []any) string { // ref to https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#severity-fields func OtelSeverityText(lv zerolog.Level) string { s := strings.ToUpper(lv.String()) - if s == "DPANIC" || s == "PANIC" { + if s == "PANIC" { s = "FATAL" } return s