observer/pkg/exporters/console_exporter/console_exporter.go

126 lines
3.0 KiB
Go

package console_exporter
import (
"context"
"fmt"
"sync"
"git.ma-al.com/gora_filip/pkg/level"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/trace"
)
type TraceFormatter interface {
FormatSpans(spans []trace.ReadOnlySpan, removeFields []attribute.Key, verbosityLevel level.SeverityLevel, addTraceId bool, onlyErrors bool) (string, error)
}
// Configuration for the exporter.
//
// Most of options are passed to the formatter.
type ExporterOptions struct {
// Try to parse filters from an environment variable with a name provided by this field.
// Result will only by applied to unset options. NOT IMPLEMENTED!
FilterFromEnvVar *string
// Filter the output based on the [level.SeverityLevel].
FilterOnLevel *level.SeverityLevel
// Fields that should be removed from the output.
FilterOutFields []attribute.Key
// Print only trace events instead of whole traces.
EmitEventsOnly bool
// Add trace id to output
EmitTraceId bool
// Print output only when an error is found
EmitOnlyOnError bool
// Used only when `EmitEventsOnly` is set to true.
TraceFormatter *TraceFormatter
}
type Exporter struct {
lvl level.SeverityLevel
removedFields []attribute.Key
addTraceId bool
onlyErrs bool
traceFormatter TraceFormatter
printerMu sync.Mutex
stoppedMu sync.RWMutex
stopped bool
}
// NOTE: The configuration might change in future releases
func DefaultConsoleExporter() trace.SpanExporter {
lvl := level.DEBUG
fmt := NewEventsOnlyFormatter()
return NewExporter(ExporterOptions{
FilterFromEnvVar: nil,
FilterOnLevel: &lvl,
FilterOutFields: []attribute.Key{},
EmitEventsOnly: false,
EmitTraceId: true,
EmitOnlyOnError: false,
TraceFormatter: &fmt,
})
}
func NewExporter(opts ExporterOptions) trace.SpanExporter {
var formatter TraceFormatter
var lvl level.SeverityLevel
if opts.TraceFormatter != nil {
formatter = *opts.TraceFormatter
} else {
formatter = TraceFormatter(&EventsOnlyFormatter{})
}
if opts.FilterOnLevel != nil {
lvl = *opts.FilterOnLevel
} else {
lvl = level.TRACE
}
return &Exporter{
traceFormatter: formatter,
removedFields: opts.FilterOutFields,
addTraceId: opts.EmitTraceId,
onlyErrs: opts.EmitOnlyOnError,
lvl: lvl,
}
}
// Implements [trace.SpanExporter]
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
e.stoppedMu.RLock()
stopped := e.stopped
e.stoppedMu.RUnlock()
if stopped {
return nil
}
if len(spans) == 0 {
return nil
}
e.printerMu.Lock()
defer e.printerMu.Unlock()
printLine, err := e.traceFormatter.FormatSpans(spans, e.removedFields, e.lvl, e.addTraceId, e.onlyErrs)
if err != nil {
fmt.Printf("FAILED TO FORMAT A TRACE WITH ERR: %#v\n", err)
}
if len(printLine) > 0 {
fmt.Println(printLine)
}
return nil
}
// Implements [trace.SpanExporter]
func (e *Exporter) Shutdown(ctx context.Context) error {
e.stoppedMu.Lock()
e.stopped = true
e.stoppedMu.Unlock()
select {
case <-ctx.Done():
return ctx.Err()
default:
}
return nil
}