Skip to content

Commit

Permalink
Fix OTEL logging in HotRod example (jaegertracing#4556)
Browse files Browse the repository at this point in the history
## Short description of the changes
- A cleaned up version of
jaegertracing#4550.
- It uses a single spanLogger for both OpenTracing and OpenTelemetry
spans.
- The logging works for both types spans because of [this clever line of
code](https://github.com/opentracing/opentracing-go/blob/master/gocontext.go#L14)
where `tracerWithHook` is an OTEL bridge tracer, allowing span to be
stored in the `ctx` the "OTEL-way", making the span discoverable as a
legitimate OTEL span such that `ok == true`:

```go
if span, ok := ctx.Value(currentSpanKey).(Span); ok { ... }
```

## Testing

Ran locally to confirm logs appearing for both OpenTracing spans:

![Screenshot 2023-07-01 at 8 12 16
pm](https://github.com/jaegertracing/jaeger/assets/26584478/572b037a-8fbb-43aa-abdf-6a8a2a137f89)

and OpenTelemetry spans:
![Screenshot 2023-07-01 at 8 12 08
pm](https://github.com/jaegertracing/jaeger/assets/26584478/681da3b3-8f07-4f85-b60e-8235592c89bd)

---------

Signed-off-by: albertteoh <[email protected]>
Signed-off-by: Afzal Ansari <[email protected]>
  • Loading branch information
albertteoh authored and afzal442 committed Jul 10, 2023
1 parent 5ed80e5 commit 372d0b7
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 71 deletions.
15 changes: 5 additions & 10 deletions examples/hotrod/pkg/log/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package log
import (
"context"

ot "github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
Expand All @@ -44,16 +43,12 @@ func (b Factory) Bg() Logger {
// contains an OpenTracing span, all logging calls are also
// echo-ed into the span.
func (b Factory) For(ctx context.Context) Logger {
if otSpan := ot.SpanFromContext(ctx); otSpan != nil {
logger := spanLogger{span: otSpan, logger: b.logger}

if otelSpan := trace.SpanFromContext(ctx); otelSpan != nil {
logger.spanFields = []zapcore.Field{
zap.String("trace_id", otelSpan.SpanContext().TraceID().String()),
zap.String("span_id", otelSpan.SpanContext().SpanID().String()),
}
if otelSpan := trace.SpanFromContext(ctx); otelSpan != nil {
logger := spanLogger{span: otelSpan, logger: b.logger}
logger.spanFields = []zapcore.Field{
zap.String("trace_id", otelSpan.SpanContext().TraceID().String()),
zap.String("span_id", otelSpan.SpanContext().SpanID().String()),
}

return logger
}
return b.Bg()
Expand Down
143 changes: 82 additions & 61 deletions examples/hotrod/pkg/log/spanlogger.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,24 @@
package log

import (
"fmt"
"time"

"github.com/opentracing/opentracing-go"
tag "github.com/opentracing/opentracing-go/ext"
"github.com/opentracing/opentracing-go/log"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

type spanLogger struct {
logger *zap.Logger
span opentracing.Span
span trace.Span
spanFields []zapcore.Field
}

func (sl spanLogger) Debug(msg string, fields ...zapcore.Field) {
sl.logToSpan("Debug", msg, fields...)
sl.logToSpan("debug", msg, fields...)
sl.logger.Debug(msg, append(sl.spanFields, fields...)...)
}

Expand All @@ -48,7 +49,7 @@ func (sl spanLogger) Error(msg string, fields ...zapcore.Field) {

func (sl spanLogger) Fatal(msg string, fields ...zapcore.Field) {
sl.logToSpan("fatal", msg, fields...)
tag.Error.Set(sl.span, true)
sl.span.SetStatus(codes.Error, msg)
sl.logger.Fatal(msg, append(sl.spanFields, fields...)...)
}

Expand All @@ -57,99 +58,119 @@ func (sl spanLogger) With(fields ...zapcore.Field) Logger {
return spanLogger{logger: sl.logger.With(fields...), span: sl.span, spanFields: sl.spanFields}
}

func (sl spanLogger) logToSpan(level string, msg string, fields ...zapcore.Field) {
// TODO rather than always converting the fields, we could wrap them into a lazy logger
fa := fieldAdapter(make([]log.Field, 0, 2+len(fields)))
fa = append(fa, log.String("event", msg))
fa = append(fa, log.String("level", level))
func (sl spanLogger) logToSpan(level, msg string, fields ...zapcore.Field) {
fields = append(fields, zap.String("level", level))
sl.span.AddEvent(
msg,
trace.WithAttributes(logFieldsToOTelAttrs(fields)...),
)
}

func logFieldsToOTelAttrs(fields []zapcore.Field) []attribute.KeyValue {
encoder := &bridgeFieldEncoder{}
for _, field := range fields {
field.AddTo(&fa)
field.AddTo(encoder)
}
sl.span.LogFields(fa...)
return encoder.pairs
}

type fieldAdapter []log.Field
type bridgeFieldEncoder struct {
pairs []attribute.KeyValue
}

func (fa *fieldAdapter) AddBool(key string, value bool) {
*fa = append(*fa, log.Bool(key, value))
func (e *bridgeFieldEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
e.pairs = append(e.pairs, attribute.String(key, fmt.Sprint(marshaler)))
return nil
}

func (fa *fieldAdapter) AddFloat64(key string, value float64) {
*fa = append(*fa, log.Float64(key, value))
func (e *bridgeFieldEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) error {
e.pairs = append(e.pairs, attribute.String(key, fmt.Sprint(marshaler)))
return nil
}

func (fa *fieldAdapter) AddFloat32(key string, value float32) {
*fa = append(*fa, log.Float64(key, float64(value)))
func (e *bridgeFieldEncoder) AddBinary(key string, value []byte) {
e.pairs = append(e.pairs, attribute.String(key, fmt.Sprint(value)))
}

func (fa *fieldAdapter) AddInt(key string, value int) {
*fa = append(*fa, log.Int(key, value))
func (e *bridgeFieldEncoder) AddByteString(key string, value []byte) {
e.pairs = append(e.pairs, attribute.String(key, fmt.Sprint(value)))
}

func (fa *fieldAdapter) AddInt64(key string, value int64) {
*fa = append(*fa, log.Int64(key, value))
func (e *bridgeFieldEncoder) AddBool(key string, value bool) {
e.pairs = append(e.pairs, attribute.Bool(key, value))
}

func (fa *fieldAdapter) AddInt32(key string, value int32) {
*fa = append(*fa, log.Int64(key, int64(value)))
func (e *bridgeFieldEncoder) AddComplex128(key string, value complex128) {
e.pairs = append(e.pairs, attribute.String(key, fmt.Sprint(value)))
}

func (fa *fieldAdapter) AddInt16(key string, value int16) {
*fa = append(*fa, log.Int64(key, int64(value)))
func (e *bridgeFieldEncoder) AddComplex64(key string, value complex64) {
e.pairs = append(e.pairs, attribute.String(key, fmt.Sprint(value)))
}

func (fa *fieldAdapter) AddInt8(key string, value int8) {
*fa = append(*fa, log.Int64(key, int64(value)))
func (e *bridgeFieldEncoder) AddDuration(key string, value time.Duration) {
e.pairs = append(e.pairs, attribute.String(key, fmt.Sprint(value)))
}

func (fa *fieldAdapter) AddUint(key string, value uint) {
*fa = append(*fa, log.Uint64(key, uint64(value)))
func (e *bridgeFieldEncoder) AddFloat64(key string, value float64) {
e.pairs = append(e.pairs, attribute.Float64(key, value))
}

func (fa *fieldAdapter) AddUint64(key string, value uint64) {
*fa = append(*fa, log.Uint64(key, value))
func (e *bridgeFieldEncoder) AddFloat32(key string, value float32) {
e.pairs = append(e.pairs, attribute.Float64(key, float64(value)))
}

func (fa *fieldAdapter) AddUint32(key string, value uint32) {
*fa = append(*fa, log.Uint64(key, uint64(value)))
func (e *bridgeFieldEncoder) AddInt(key string, value int) {
e.pairs = append(e.pairs, attribute.Int(key, value))
}

func (fa *fieldAdapter) AddUint16(key string, value uint16) {
*fa = append(*fa, log.Uint64(key, uint64(value)))
func (e *bridgeFieldEncoder) AddInt64(key string, value int64) {
e.pairs = append(e.pairs, attribute.Int64(key, value))
}

func (fa *fieldAdapter) AddUint8(key string, value uint8) {
*fa = append(*fa, log.Uint64(key, uint64(value)))
func (e *bridgeFieldEncoder) AddInt32(key string, value int32) {
e.pairs = append(e.pairs, attribute.Int64(key, int64(value)))
}

func (fa *fieldAdapter) AddUintptr(key string, value uintptr) {}
func (fa *fieldAdapter) AddArray(key string, marshaler zapcore.ArrayMarshaler) error { return nil }
func (fa *fieldAdapter) AddComplex128(key string, value complex128) {}
func (fa *fieldAdapter) AddComplex64(key string, value complex64) {}
func (fa *fieldAdapter) AddObject(key string, value zapcore.ObjectMarshaler) error { return nil }
func (fa *fieldAdapter) AddReflected(key string, value interface{}) error { return nil }
func (fa *fieldAdapter) OpenNamespace(key string) {}
func (e *bridgeFieldEncoder) AddInt16(key string, value int16) {
e.pairs = append(e.pairs, attribute.Int64(key, int64(value)))
}

func (fa *fieldAdapter) AddDuration(key string, value time.Duration) {
// TODO inefficient
*fa = append(*fa, log.String(key, value.String()))
func (e *bridgeFieldEncoder) AddInt8(key string, value int8) {
e.pairs = append(e.pairs, attribute.Int64(key, int64(value)))
}

func (fa *fieldAdapter) AddTime(key string, value time.Time) {
// TODO inefficient
*fa = append(*fa, log.String(key, value.String()))
func (e *bridgeFieldEncoder) AddString(key, value string) {
e.pairs = append(e.pairs, attribute.String(key, value))
}

func (fa *fieldAdapter) AddBinary(key string, value []byte) {
*fa = append(*fa, log.Object(key, value))
func (e *bridgeFieldEncoder) AddTime(key string, value time.Time) {
e.pairs = append(e.pairs, attribute.String(key, fmt.Sprint(value)))
}

func (fa *fieldAdapter) AddByteString(key string, value []byte) {
*fa = append(*fa, log.Object(key, value))
func (e *bridgeFieldEncoder) AddUint(key string, value uint) {
e.pairs = append(e.pairs, attribute.String(key, fmt.Sprintf("%d", value)))
}

func (fa *fieldAdapter) AddString(key, value string) {
if key != "" && value != "" {
*fa = append(*fa, log.String(key, value))
}
func (e *bridgeFieldEncoder) AddUint64(key string, value uint64) {
e.pairs = append(e.pairs, attribute.String(key, fmt.Sprintf("%d", value)))
}

func (e *bridgeFieldEncoder) AddUint32(key string, value uint32) {
e.pairs = append(e.pairs, attribute.Int64(key, int64(value)))
}

func (e *bridgeFieldEncoder) AddUint16(key string, value uint16) {
e.pairs = append(e.pairs, attribute.Int64(key, int64(value)))
}

func (e *bridgeFieldEncoder) AddUint8(key string, value uint8) {
e.pairs = append(e.pairs, attribute.Int64(key, int64(value)))
}

func (e *bridgeFieldEncoder) AddUintptr(key string, value uintptr) {
e.pairs = append(e.pairs, attribute.String(key, fmt.Sprint(value)))
}

func (e *bridgeFieldEncoder) AddReflected(key string, value interface{}) error { return nil }
func (e *bridgeFieldEncoder) OpenNamespace(key string) {}

0 comments on commit 372d0b7

Please sign in to comment.