Skip to content

Commit

Permalink
feat: add zerolog (#46) - Update README, reorganize hook, fix unit te…
Browse files Browse the repository at this point in the history
…sts, remove unnecessary code in CtxLogf
  • Loading branch information
lovung committed Jan 26, 2024
1 parent 7c41e04 commit 3695447
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 41 deletions.
61 changes: 44 additions & 17 deletions logging/zerolog/README.md
Original file line number Diff line number Diff line change
@@ -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)

// ...
}
```

27 changes: 9 additions & 18 deletions logging/zerolog/logger.go
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -26,7 +26,7 @@ import (
)

type Logger struct {
hertzzerolog.Logger
*hertzzerolog.Logger
config *config
}

Expand All @@ -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:
Expand All @@ -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
Expand Down
11 changes: 8 additions & 3 deletions logging/zerolog/logger_test.go
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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"
Expand Down Expand Up @@ -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")
Expand Down
37 changes: 36 additions & 1 deletion logging/zerolog/option.go
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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 {
Expand All @@ -37,6 +41,7 @@ type traceConfig struct {
type config struct {
logger *hertzzerolog.Logger
traceConfig *traceConfig
hookFunc zerolog.HookFunc
}

// defaultConfig default config
Expand Down Expand Up @@ -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))
}
}
}
4 changes: 2 additions & 2 deletions logging/zerolog/utils.go
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 3695447

Please sign in to comment.