-
Notifications
You must be signed in to change notification settings - Fork 582
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add skeleton for zerolog bridge (#5782)
This PR introduces the `otelzerolog` package, which provides a `SeverityHook` to bridge the logging capabilities of Zerolog with OpenTelemetry. The following key features are included: ## Changes - Added `config` struct to handle configuration options. - Implemented `Option` interface and related functions (`WithVersion`, `WithSchemaURL`, `WithLoggerProvider`). - Created `NewSeverityHook` function to initialize a `SeverityHook`. - Defined `SeverityHook` struct to perform the logging bridge functionality. Part of #5405 --------- Co-authored-by: Damien Mathieu <[email protected]> Co-authored-by: Robert Pająk <[email protected]>
- Loading branch information
1 parent
e2d29ee
commit fb316b5
Showing
6 changed files
with
296 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package otelzerolog_test | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/rs/zerolog" | ||
|
||
"go.opentelemetry.io/contrib/bridges/otelzerolog" | ||
"go.opentelemetry.io/otel/log/noop" | ||
) | ||
|
||
func Example() { | ||
// Use a working LoggerProvider implementation instead e.g. using go.opentelemetry.io/otel/sdk/log. | ||
provider := noop.NewLoggerProvider() | ||
|
||
// Create a logger that emits logs to both STDOUT and the OTel Go SDK. | ||
hook := otelzerolog.NewHook("my/pkg/name", otelzerolog.WithLoggerProvider(provider)) | ||
logger := zerolog.New(os.Stdout).With().Logger() | ||
logger = logger.Hook(hook) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
module go.opentelemetry.io/contrib/bridges/otelzerolog | ||
|
||
go 1.21 | ||
|
||
require ( | ||
github.com/rs/zerolog v1.33.0 | ||
github.com/stretchr/testify v1.9.0 | ||
go.opentelemetry.io/otel/log v0.3.0 | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/go-logr/logr v1.4.1 // indirect | ||
github.com/go-logr/stdr v1.2.2 // indirect | ||
github.com/mattn/go-colorable v0.1.13 // indirect | ||
github.com/mattn/go-isatty v0.0.19 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
go.opentelemetry.io/otel v1.27.0 // indirect | ||
go.opentelemetry.io/otel/metric v1.27.0 // indirect | ||
go.opentelemetry.io/otel/trace v1.27.0 // indirect | ||
golang.org/x/sys v0.12.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= | ||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= | ||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= | ||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= | ||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= | ||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= | ||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | ||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | ||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= | ||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= | ||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= | ||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= | ||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= | ||
go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= | ||
go.opentelemetry.io/otel/log v0.3.0 h1:kJRFkpUFYtny37NQzL386WbznUByZx186DpEMKhEGZs= | ||
go.opentelemetry.io/otel/log v0.3.0/go.mod h1:ziCwqZr9soYDwGNbIL+6kAvQC+ANvjgG367HVcyR/ys= | ||
go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= | ||
go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= | ||
go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= | ||
go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= | ||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= | ||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// Package otelzerolog provides a [Hook], a [zerolog.Hook] implementation that | ||
// can be used to bridge between the [zerolog] API and [OpenTelemetry]. | ||
// [OpenTelemetry]: https://opentelemetry.io/docs/concepts/signals/logs/ | ||
package otelzerolog // import "go.opentelemetry.io/contrib/bridges/otelzerolog" | ||
|
||
import ( | ||
"github.com/rs/zerolog" | ||
|
||
"go.opentelemetry.io/otel/log" | ||
"go.opentelemetry.io/otel/log/global" | ||
) | ||
|
||
type config struct { | ||
provider log.LoggerProvider | ||
version string | ||
schemaURL string | ||
} | ||
|
||
func newConfig(options []Option) config { | ||
var c config | ||
for _, opt := range options { | ||
c = opt.apply(c) | ||
} | ||
|
||
if c.provider == nil { | ||
c.provider = global.GetLoggerProvider() | ||
} | ||
return c | ||
} | ||
|
||
func (c config) logger(name string) log.Logger { | ||
var opts []log.LoggerOption | ||
if c.version != "" { | ||
opts = append(opts, log.WithInstrumentationVersion(c.version)) | ||
} | ||
if c.schemaURL != "" { | ||
opts = append(opts, log.WithSchemaURL(c.schemaURL)) | ||
} | ||
return c.provider.Logger(name, opts...) | ||
} | ||
|
||
// Option configures a Hook. | ||
type Option interface { | ||
apply(config) config | ||
} | ||
type optFunc func(config) config | ||
|
||
func (f optFunc) apply(c config) config { return f(c) } | ||
|
||
// WithVersion returns an [Option] that configures the version of the | ||
// [log.Logger] used by a [Hook]. The version should be the version of the | ||
// package that is being logged. | ||
func WithVersion(version string) Option { | ||
return optFunc(func(c config) config { | ||
c.version = version | ||
return c | ||
}) | ||
} | ||
|
||
// WithSchemaURL returns an [Option] that configures the semantic convention | ||
// schema URL of the [log.Logger] used by a [Hook]. The schemaURL should be | ||
// the schema URL for the semantic conventions used in log records. | ||
func WithSchemaURL(schemaURL string) Option { | ||
return optFunc(func(c config) config { | ||
c.schemaURL = schemaURL | ||
return c | ||
}) | ||
} | ||
|
||
// WithLoggerProvider returns an [Option] that configures [log.LoggerProvider] | ||
// used by a [Hook]. | ||
// | ||
// By default if this Option is not provided, the Hook will use the global | ||
// LoggerProvider. | ||
func WithLoggerProvider(provider log.LoggerProvider) Option { | ||
return optFunc(func(c config) config { | ||
c.provider = provider | ||
return c | ||
}) | ||
} | ||
|
||
// Hook is a [zerolog.Hook] that sends all logging records it receives to | ||
// OpenTelemetry. See package documentation for how conversions are made. | ||
type Hook struct { | ||
logger log.Logger | ||
} | ||
|
||
// NewHook returns a new [Hook] to be used as a [Zerolog.Hook]. | ||
// If [WithLoggerProvider] is not provided, the returned Hook will use the | ||
// global LoggerProvider. | ||
func NewHook(name string, options ...Option) *Hook { | ||
cfg := newConfig(options) | ||
return &Hook{ | ||
logger: cfg.logger(name), | ||
} | ||
} | ||
|
||
// Run handles the passed record, and sends it to OpenTelemetry. | ||
func (h Hook) Run(e *zerolog.Event, level zerolog.Level, msg string) { | ||
// TODO | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package otelzerolog | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"go.opentelemetry.io/otel/log" | ||
"go.opentelemetry.io/otel/log/embedded" | ||
"go.opentelemetry.io/otel/log/global" | ||
) | ||
|
||
type mockLoggerProvider struct { | ||
embedded.LoggerProvider | ||
} | ||
|
||
func (mockLoggerProvider) Logger(name string, options ...log.LoggerOption) log.Logger { | ||
return nil | ||
} | ||
|
||
func TestNewConfig(t *testing.T) { | ||
customLoggerProvider := mockLoggerProvider{} | ||
|
||
for _, tt := range []struct { | ||
name string | ||
options []Option | ||
|
||
wantConfig config | ||
}{ | ||
{ | ||
name: "with no options", | ||
wantConfig: config{ | ||
provider: global.GetLoggerProvider(), | ||
}, | ||
}, | ||
{ | ||
name: "with a custom version", | ||
options: []Option{ | ||
WithVersion("1.0"), | ||
}, | ||
wantConfig: config{ | ||
version: "1.0", | ||
provider: global.GetLoggerProvider(), | ||
}, | ||
}, | ||
{ | ||
name: "with a custom schema URL", | ||
options: []Option{ | ||
WithSchemaURL("https://example.com"), | ||
}, | ||
wantConfig: config{ | ||
schemaURL: "https://example.com", | ||
provider: global.GetLoggerProvider(), | ||
}, | ||
}, | ||
{ | ||
name: "with a custom logger provider", | ||
options: []Option{ | ||
WithLoggerProvider(customLoggerProvider), | ||
}, | ||
wantConfig: config{ | ||
provider: customLoggerProvider, | ||
}, | ||
}, | ||
} { | ||
t.Run(tt.name, func(t *testing.T) { | ||
assert.Equal(t, tt.wantConfig, newConfig(tt.options)) | ||
}) | ||
} | ||
} | ||
|
||
func TestNewHook(t *testing.T) { | ||
const name = "test_hook" | ||
provider := global.GetLoggerProvider() | ||
|
||
for _, tt := range []struct { | ||
name string | ||
options []Option | ||
wantLogger log.Logger | ||
}{ | ||
{ | ||
name: "with default options", | ||
wantLogger: provider.Logger(name), | ||
}, | ||
{ | ||
name: "with version and schema URL", | ||
options: []Option{ | ||
WithVersion("1.0"), | ||
WithSchemaURL("https://example.com"), | ||
}, | ||
wantLogger: provider.Logger(name, | ||
log.WithInstrumentationVersion("1.0"), | ||
log.WithSchemaURL("https://example.com"), | ||
), | ||
}, | ||
} { | ||
t.Run(tt.name, func(t *testing.T) { | ||
hook := NewHook(name, tt.options...) | ||
assert.NotNil(t, hook) | ||
assert.Equal(t, tt.wantLogger, hook.logger) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters