Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logging bridge for Zap #5279

Draft
wants to merge 55 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
ba244b3
Logging bridge for Zap
khushijain21 Mar 14, 2024
10eca40
added array and object encoder
khushijain21 Mar 16, 2024
7cd1ec9
added array and object encoder
khushijain21 Mar 16, 2024
9eb58df
Merge branch 'zap' of https://github.com/khushijain21/opentelemetry-g…
khushijain21 Mar 16, 2024
b796f0f
Add catalog-info.yaml config file
khushijain21 Mar 18, 2024
57df264
Merge pull request #1 from khushijain21/backstage-integration
khushijain21 Mar 18, 2024
01b3336
refactored getattr method and added test for map
khushijain21 Mar 18, 2024
780893b
redefined encoder def
khushijain21 Mar 20, 2024
6c22d59
redefined encoder def
khushijain21 Mar 20, 2024
5c324e6
Merge branch 'zap' of https://github.com/khushijain21/opentelemetry-g…
khushijain21 Mar 20, 2024
edf1ccc
Added more tests
khushijain21 Mar 21, 2024
8414d22
Refactored code
khushijain21 Mar 21, 2024
44ac333
Refactoring
khushijain21 Mar 21, 2024
64136ad
added func for reflect and opennamespace
khushijain21 Mar 22, 2024
2c519e4
Linting - Refactoring
khushijain21 Mar 23, 2024
5d2be47
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
khushijain21 Mar 23, 2024
fcd1c39
More function defintiions
khushijain21 Mar 23, 2024
1c5cea2
fixed tests
khushijain21 Mar 23, 2024
ff04bfc
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
khushijain21 Mar 26, 2024
b5fa0e9
added options for zapcore
khushijain21 Mar 26, 2024
988bbf6
move benchmarks around
khushijain21 Mar 28, 2024
2fc2dfe
more refactoring
khushijain21 Mar 29, 2024
68502a8
Merge branch 'main' into zap
khushijain21 Mar 29, 2024
d8dd462
go version
khushijain21 Mar 29, 2024
3187c86
Merge branch 'zap' of https://github.com/khushijain21/opentelemetry-g…
khushijain21 Mar 29, 2024
f551e18
Update bridges/otelzap/zap_logger.go
khushijain21 Mar 29, 2024
0c1a8c1
fix text and address comments
khushijain21 Mar 30, 2024
b990993
Added tests for loggel level
khushijain21 Mar 30, 2024
2725aed
otel tests
khushijain21 Apr 2, 2024
b49e6df
add undefined level
khushijain21 Apr 2, 2024
9579fa0
Merge branch 'main' into zap
pellared Apr 2, 2024
cb1cb84
added context, linting failures checked
khushijain21 Apr 3, 2024
f23d8a9
removed catalog
khushijain21 Apr 3, 2024
519f6e9
Merge branch 'zap' of https://github.com/khushijain21/opentelemetry-g…
khushijain21 Apr 3, 2024
ce712af
remove catalog
khushijain21 Apr 3, 2024
e65e25c
add sync pool to array & object encoder for better performance
khushijain21 Apr 5, 2024
647773c
Merge branch 'main' of https://github.com/khushijain21/opentelemetry-…
khushijain21 Apr 5, 2024
c33fc68
Update encoder.go
khushijain21 Apr 5, 2024
e9d4e0f
added opennamespace
khushijain21 Apr 5, 2024
0fc747d
Merge branch 'zap' of https://github.com/khushijain21/opentelemetry-g…
khushijain21 Apr 5, 2024
f954bc2
Merge branch 'main' into zap
khushijain21 Apr 5, 2024
23a04c9
Update encoder.go
khushijain21 Apr 8, 2024
2989fcf
Merge branch 'main' into zap
pellared Apr 8, 2024
1aa341c
Merge branch 'main' into zap
khushijain21 Apr 11, 2024
4c46d23
adds support for opennamespace, requires optimization
khushijain21 Apr 11, 2024
e890935
Merge branch 'zap' of https://github.com/khushijain21/opentelemetry-g…
khushijain21 Apr 11, 2024
5ac93ef
improvements on top of ns
khushijain21 Apr 12, 2024
5ab6fc3
matches zap benchmarks
khushijain21 Apr 14, 2024
ac34acd
adds Enabled&Configuration tests
khushijain21 Apr 17, 2024
bddcf3e
looks good now
khushijain21 Apr 17, 2024
07b623a
Update encoder.go
khushijain21 Apr 17, 2024
5f6cc16
correct the logic of recursion
khushijain21 Apr 18, 2024
9501563
Merge branch 'zap' of https://github.com/khushijain21/opentelemetry-g…
khushijain21 Apr 18, 2024
fc4b1d1
Merge branch 'main' into zap
khushijain21 Apr 18, 2024
f60e0c3
Delete catalog-info.yaml
khushijain21 May 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Expand Up @@ -109,6 +109,15 @@ updates:
schedule:
interval: weekly
day: sunday
- package-ecosystem: gomod
directory: /bridges/otelzap
labels:
- dependencies
- go
- Skip Changelog
schedule:
interval: weekly
day: sunday
- package-ecosystem: gomod
directory: /bridges/prometheus
labels:
Expand Down
82 changes: 82 additions & 0 deletions bridges/otelzap/config.go
@@ -0,0 +1,82 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package otelzap // import "go.opentelemetry.io/contrib/bridges/otelzap"

import (
"go.opentelemetry.io/otel/log"
"go.opentelemetry.io/otel/log/global"
"go.opentelemetry.io/otel/sdk/instrumentation"
)

type config struct {
provider log.LoggerProvider
scope instrumentation.Scope
}

func newConfig(options []Option) config {
var c config
for _, opt := range options {
c = opt.apply(c)
}

var emptyScope instrumentation.Scope
if c.scope == emptyScope {
c.scope = instrumentation.Scope{
Name: bridgeName,
Version: version,
}
}

if c.provider == nil {
c.provider = global.GetLoggerProvider()
}

return c
}

func (c config) logger() log.Logger {
var opts []log.LoggerOption
if c.scope.Version != "" {
opts = append(opts, log.WithInstrumentationVersion(c.scope.Version))
}
if c.scope.SchemaURL != "" {
opts = append(opts, log.WithSchemaURL(c.scope.SchemaURL))
}
return c.provider.Logger(c.scope.Name, opts...)
}

// Option configures a [zapcore].
type Option interface {
apply(config) config
}

type optFunc func(config) config

func (f optFunc) apply(c config) config { return f(c) }

// WithInstrumentationScope returns an [Option] that configures the scope of
// the [log.Logger] used by a [zapcore].
//
// By default if this Option is not provided, the zapcore will use a default
// instrumentation scope describing this bridge package. It is recommended to
// provide this so log data can be associated with its source package or
// module.
func WithInstrumentationScope(scope instrumentation.Scope) Option {
return optFunc(func(c config) config {
c.scope = scope
return c
})
}

// WithLoggerProvider returns an [Option] that configures [log.LoggerProvider]
// used by a [Handler] to create its [log.Logger].
//
// By default if this Option is not provided, the Handler will use the global
// LoggerProvider.
func WithLoggerProvider(provider log.LoggerProvider) Option {
return optFunc(func(c config) config {
c.provider = provider
return c
})
}
136 changes: 136 additions & 0 deletions bridges/otelzap/core.go
@@ -0,0 +1,136 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

// Package otelzap provides a bridge between the [go.uber.org/zap] and
// OpenTelemetry logging.
package otelzap // import "go.opentelemetry.io/contrib/bridges/otelzap"

import (
"context"
"slices"

"go.uber.org/zap/zapcore"

"go.opentelemetry.io/otel/log"
)

const (
bridgeName = "go.opentelemetry.io/contrib/bridge/zapcore"
)

// Core is a [zapcore.Core] that sends logging records to OpenTelemetry.
type Core struct {
logger log.Logger
// holds structured context passed using Core.With()
attr []log.KeyValue
ctx context.Context
}

// // Compile-time check *Core implements zapcore.Core.
var _ zapcore.Core = (*Core)(nil)

// NewOTelZapCore creates a new [zapcore.Core] that can be used with zap.New()
// this instance will translate zap logs to opentelemetry logs and export them.
func NewCore(opts ...Option) *Core {
cfg := newConfig(opts)
return &Core{
logger: cfg.logger(),
ctx: context.Background(),
}
}

// LevelEnabler decides whether a given logging level is enabled when logging a message.
func (o *Core) Enabled(level zapcore.Level) bool {
r := log.Record{}
r.SetSeverity(getOTelLevel(level))
// should we use the context set on core?
return o.logger.Enabled(context.Background(), r)
}

// With adds structured context to the Core.
func (o *Core) With(fields []zapcore.Field) zapcore.Core {
clone := o.clone()
if len(fields) > 0 {
attrbuf := convertField(&clone.ctx, &fields)
clone.attr = append(clone.attr, attrbuf...)
}
return clone
}

// Sync flushes buffered logs (if any).
func (o *Core) Sync() error {
return nil
}

// Check determines whether the supplied Entry should be logged using core.Enabled method.
func (o *Core) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if o.Enabled(ent.Level) {
return ce.AddCore(ent, o)
}
return ce
}

// Write method encodes zap fields to OTel logs and emits them.
func (o *Core) Write(ent zapcore.Entry, fields []zapcore.Field) error {
r := log.Record{}
r.SetTimestamp(ent.Time)
r.SetBody(log.StringValue(ent.Message))
r.SetSeverity(getOTelLevel(ent.Level))

if len(fields) > 0 {
attrbuf := convertField(&o.ctx, &fields)
attrbuf = slices.Grow(attrbuf, len(o.attr))
attrbuf = append(attrbuf, o.attr...)
r.AddAttributes(attrbuf...)
} else {
r.AddAttributes(o.attr...)
}

o.logger.Emit(o.ctx, r)
return nil
}

func (o *Core) clone() *Core {
return &Core{
logger: o.logger,
attr: slices.Clone(o.attr),
ctx: o.ctx,
}
}

// converts zap fields to OTel log attributes.
func convertField(ctx *context.Context, fields *[]zapcore.Field) []log.KeyValue {
enc, free := getObjectEncoder()
defer free()
for _, field := range *fields {
if ctxFld, ok := field.Interface.(context.Context); ok {
*ctx = ctxFld
continue
}
field.AddTo(enc)
}
enc.getObjValue(enc.root)
return enc.root.kv
}

// converts zap level to OTel log level.
func getOTelLevel(level zapcore.Level) log.Severity {
switch level {
case zapcore.DebugLevel:
return log.SeverityDebug
case zapcore.InfoLevel:
return log.SeverityInfo
case zapcore.WarnLevel:
return log.SeverityWarn
case zapcore.ErrorLevel:
return log.SeverityError
case zapcore.DPanicLevel:
return log.SeverityFatal1
case zapcore.PanicLevel:
return log.SeverityFatal2
case zapcore.FatalLevel:
return log.SeverityFatal3
default:
return log.SeverityUndefined
}
}