added event wrappers, bug fixes, API improvements

This commit is contained in:
Natalia Goc 2024-05-17 18:21:09 +02:00
parent 4f4a7e09c5
commit 076196c03e
7 changed files with 218 additions and 49 deletions

View File

@ -8,19 +8,17 @@ import (
"os/signal" "os/signal"
"time" "time"
"git.ma-al.com/gora_filip/observer/pkg/tracer"
"git.ma-al.com/gora_filip/pkg/attr/layer_attr" "git.ma-al.com/gora_filip/pkg/attr/layer_attr"
"git.ma-al.com/gora_filip/pkg/event"
"git.ma-al.com/gora_filip/pkg/exporters" "git.ma-al.com/gora_filip/pkg/exporters"
tracing "git.ma-al.com/gora_filip/pkg/fiber_tracing" tracing "git.ma-al.com/gora_filip/pkg/fiber_tracing"
"git.ma-al.com/gora_filip/pkg/level" "git.ma-al.com/gora_filip/pkg/level"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
) )
func main() { func main() {
main := fiber.New()
main := fiber.New(fiber.Config{
StreamRequestBody: true,
})
exps := make([]exporters.TraceExporter, 0) exps := make([]exporters.TraceExporter, 0)
exps = append(exps, exporters.DevConsoleExporter()) exps = append(exps, exporters.DevConsoleExporter())
@ -28,7 +26,7 @@ func main() {
if err == nil { if err == nil {
exps = append(exps, gelfExp) exps = append(exps, gelfExp)
} }
jaegerExp, err := exporters.OtlpHTTPExporter() jaegerExp, err := exporters.OtlpHTTPExporter(otlptracehttp.WithEndpointURL("http://localhost:4318/v1/traces"))
if err == nil { if err == nil {
exps = append(exps, jaegerExp) exps = append(exps, jaegerExp)
} }
@ -41,26 +39,7 @@ func main() {
})) }))
defer tracing.ShutdownTracer() defer tracing.ShutdownTracer()
main.Get("/", func(c *fiber.Ctx) error { main.Get("/", Handler)
ctx, span := tracing.FStart(c, layer_attr.Handler{
Level: level.DEBUG,
}.AsOpts())
defer span.End()
span.AddEvent(
"smthing is happening",
layer_attr.Handler{
Level: level.INFO,
}.AsOpts(),
)
err := Serv(ctx)
if err != nil {
return tracer.RecordError(span, err)
}
return c.SendString("xd")
})
// handle interrupts (shutdown) // handle interrupts (shutdown)
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
@ -77,8 +56,29 @@ func main() {
} }
func Handler(c *fiber.Ctx) error {
ctx, span := tracing.FStart(c, layer_attr.Handler{
Level: level.DEBUG,
}.AsOpts())
defer span.End()
event.NewInSpan(event.Event{
Level: level.WARN,
ShortMessage: "a warning event",
}, span)
err := Serv(ctx)
if err != nil {
return event.NewErrInSpan(event.Error{Err: err, Level: level.ERR}, span)
}
return c.SendStatus(fiber.StatusOK)
}
func Serv(ctx context.Context) *fiber.Error { func Serv(ctx context.Context) *fiber.Error {
ctx, span := tracer.Service(ctx, "service", "service span") ctx, span := tracing.Start(ctx, "service span", layer_attr.Service{
Level: level.INFO,
Name: "some service",
}.AsOpts())
defer span.End() defer span.End()
for range []int{1, 2, 3} { for range []int{1, 2, 3} {
@ -94,7 +94,10 @@ func Serv(ctx context.Context) *fiber.Error {
} }
func Repo(ctx context.Context) error { func Repo(ctx context.Context) error {
ctx, span := tracer.Repository(ctx, "repo", "repo span") ctx, span := tracing.Start(ctx, "repo span", layer_attr.Repo{
Level: level.DEBUG,
Name: "some repo",
}.AsOpts())
defer span.End() defer span.End()
for range []int{1, 2, 3} { for range []int{1, 2, 3} {

View File

@ -51,6 +51,7 @@ const (
SessionCurrencyIdKey = attribute.Key("session.currency_id") SessionCurrencyIdKey = attribute.Key("session.currency_id")
ProcessThreadsAvailableKey = attribute.Key("process.threads_available") ProcessThreadsAvailableKey = attribute.Key("process.threads_available")
ServiceLayerKey = attribute.Key("service.layer") ServiceLayerKey = attribute.Key("service.layer")
ServiceLayerNameKey = attribute.Key("service.layer_name")
) )
type ServiceArchitectureLayer string type ServiceArchitectureLayer string
@ -244,3 +245,10 @@ func ServiceLayer(layer ServiceArchitectureLayer) attribute.KeyValue {
Value: attribute.StringValue(string(layer)), Value: attribute.StringValue(string(layer)),
} }
} }
func ServiceLayerName(name string) attribute.KeyValue {
return attribute.KeyValue{
Key: ServiceLayerNameKey,
Value: attribute.StringValue(name),
}
}

View File

@ -11,6 +11,7 @@ type Handler struct {
// Extra attributes to be attached. Can be also added with [Handler.CustomAttrs] method. // Extra attributes to be attached. Can be also added with [Handler.CustomAttrs] method.
Attributes []attribute.KeyValue Attributes []attribute.KeyValue
Level level.SeverityLevel Level level.SeverityLevel
Name string
extraSkipInStack int extraSkipInStack int
} }
@ -18,6 +19,9 @@ func (h Handler) IntoTraceAttributes() []attribute.KeyValue {
attrs := make([]attribute.KeyValue, 5+len(h.Attributes)) attrs := make([]attribute.KeyValue, 5+len(h.Attributes))
attrs = append(attrs, attr.SourceCodeLocation(1+h.extraSkipInStack)...) // 3 attrs added here attrs = append(attrs, attr.SourceCodeLocation(1+h.extraSkipInStack)...) // 3 attrs added here
attrs = append(attrs, attr.ServiceLayer(attr.LayerHandler), attr.SeverityLevel(h.Level)) attrs = append(attrs, attr.ServiceLayer(attr.LayerHandler), attr.SeverityLevel(h.Level))
if len(h.Name) > 0 {
attrs = append(attrs, attr.ServiceLayerName(h.Name))
}
attrs = append(attrs, h.Attributes...) attrs = append(attrs, h.Attributes...)
return attrs return attrs
} }
@ -31,6 +35,12 @@ func (h *Handler) SkipMoreInCallStack(skip int) {
h.extraSkipInStack += skip h.extraSkipInStack += skip
} }
// Works the same as [Handler.IntoTraceAttributes]
func (h Handler) AsAttrs() []attribute.KeyValue {
h.extraSkipInStack += 1
return h.IntoTraceAttributes()
}
func (h Handler) AsOpts() trace.SpanStartEventOption { func (h Handler) AsOpts() trace.SpanStartEventOption {
h.extraSkipInStack += 1 h.extraSkipInStack += 1
return trace.WithAttributes(h.IntoTraceAttributes()...) return trace.WithAttributes(h.IntoTraceAttributes()...)
@ -40,17 +50,27 @@ type Service struct {
// Extra attributes to be attached. Can be also added with [Service.CustomAttrs] method. // Extra attributes to be attached. Can be also added with [Service.CustomAttrs] method.
Attributes []attribute.KeyValue Attributes []attribute.KeyValue
Level level.SeverityLevel Level level.SeverityLevel
Name string
extraSkipInStack int extraSkipInStack int
} }
func (s Service) IntoTraceAttributes() []attribute.KeyValue { func (s Service) IntoTraceAttributes() []attribute.KeyValue {
attrs := make([]attribute.KeyValue, 5+len(s.Attributes)) attrs := make([]attribute.KeyValue, 6+len(s.Attributes))
attrs = append(attrs, attr.SourceCodeLocation(1+s.extraSkipInStack)...) attrs = append(attrs, attr.SourceCodeLocation(1+s.extraSkipInStack)...)
attrs = append(attrs, attr.ServiceLayer(attr.LayerService), attr.SeverityLevel(s.Level)) attrs = append(attrs, attr.ServiceLayer(attr.LayerService), attr.SeverityLevel(s.Level))
if len(s.Name) > 0 {
attrs = append(attrs, attr.ServiceLayerName(s.Name))
}
attrs = append(attrs, s.Attributes...) attrs = append(attrs, s.Attributes...)
return attrs return attrs
} }
// Works the same as [Service.IntoTraceAttributes]
func (s Service) AsAttrs() []attribute.KeyValue {
s.extraSkipInStack += 1
return s.IntoTraceAttributes()
}
func (s Service) CustomAttrs(attrs ...interface{}) Service { func (s Service) CustomAttrs(attrs ...interface{}) Service {
s.Attributes = append(s.Attributes, attr.CollectAttributes(attrs...)...) s.Attributes = append(s.Attributes, attr.CollectAttributes(attrs...)...)
return s return s
@ -69,17 +89,27 @@ type Repo struct {
// Extra attributes to be attached. Can be also added with [Repo.CustomAttrs] method // Extra attributes to be attached. Can be also added with [Repo.CustomAttrs] method
Attributes []attribute.KeyValue Attributes []attribute.KeyValue
Level level.SeverityLevel Level level.SeverityLevel
Name string
extraSkipInStack int extraSkipInStack int
} }
func (r Repo) IntoTraceAttributes() []attribute.KeyValue { func (r Repo) IntoTraceAttributes() []attribute.KeyValue {
attrs := make([]attribute.KeyValue, 5+len(r.Attributes)) attrs := make([]attribute.KeyValue, 6+len(r.Attributes))
attrs = append(attrs, attr.SourceCodeLocation(1+r.extraSkipInStack)...) attrs = append(attrs, attr.SourceCodeLocation(1+r.extraSkipInStack)...)
attrs = append(attrs, attr.ServiceLayer(attr.LayerRepository), attr.SeverityLevel(r.Level)) attrs = append(attrs, attr.ServiceLayer(attr.LayerRepository), attr.SeverityLevel(r.Level))
if len(r.Name) > 0 {
attrs = append(attrs, attr.ServiceLayerName(r.Name))
}
attrs = append(attrs, r.Attributes...) attrs = append(attrs, r.Attributes...)
return attrs return attrs
} }
// Works the same as [Repo.IntoTraceAttributes]
func (r Repo) AsAttrs() []attribute.KeyValue {
r.extraSkipInStack += 1
return r.IntoTraceAttributes()
}
func (r Repo) CustomAttrs(attrs ...interface{}) Repo { func (r Repo) CustomAttrs(attrs ...interface{}) Repo {
r.Attributes = append(r.Attributes, attr.CollectAttributes(attrs...)...) r.Attributes = append(r.Attributes, attr.CollectAttributes(attrs...)...)
return r return r

101
pkg/event/event.go Normal file
View File

@ -0,0 +1,101 @@
package event
import (
"git.ma-al.com/gora_filip/pkg/attr"
"git.ma-al.com/gora_filip/pkg/level"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
// An event that maps well to a log message
type Event struct {
FullMessage string
ShortMessage string
Attributes []attribute.KeyValue
// Defaults to INFO when converted into attributes and unset
Level level.SeverityLevel
extraSkipInStack int
}
type IntoEvent interface {
IntoEvent() Event
}
func (e Event) IntoEvent() Event {
return e
}
func (e Event) IntoTraceAttributes() []attribute.KeyValue {
attrs := make([]attribute.KeyValue, 0)
attrs = append(attrs, attr.SourceCodeLocation(1+e.extraSkipInStack)...)
attrs = append(attrs, e.Attributes...)
if len(e.FullMessage) > 0 {
attrs = append(attrs, attr.LogMessageLong(e.FullMessage))
}
if len(e.ShortMessage) > 0 {
attrs = append(attrs, attr.LogMessageShort(e.ShortMessage))
}
if e.Level == 0 {
e.Level = level.INFO
}
attrs = append(attrs, attr.SeverityLevel(e.Level))
return attrs
}
func (e Event) AsOpts() trace.EventOption {
e.extraSkipInStack += 1
return trace.WithAttributes(e.IntoTraceAttributes()...)
}
func NewInSpan[E IntoEvent](ev E, span trace.Span) {
event := ev.IntoEvent()
event.extraSkipInStack += 1
span.AddEvent(event.ShortMessage, trace.WithAttributes(event.IntoTraceAttributes()...))
}
type Error struct {
Err error
ExtendedMessage string
// Defaults to ALERT when converted into attributes and unset
Level level.SeverityLevel
Attributes []attribute.KeyValue
extraSkipInStack int
}
type IntoErrorEvent interface {
IntoErrorEvent() Error
}
func (e Error) Error() string {
return e.Err.Error()
}
func (e Error) IntoErrorEvent() Error {
return e
}
func (e Error) IntoTraceAttributes() []attribute.KeyValue {
attrs := make([]attribute.KeyValue, 0)
attrs = append(attrs, e.Attributes...)
if len(e.ExtendedMessage) > 0 {
attrs = append(attrs, attr.LogMessageLong(e.ExtendedMessage))
}
if e.Level == 0 {
e.Level = level.ALERT
}
attrs = append(attrs, attr.SeverityLevel(e.Level), attr.LogMessageShort(e.Err.Error()))
attrs = append(attrs, attr.SourceCodeLocation(1+e.extraSkipInStack)...)
return attrs
}
func (e Error) AsOpts() trace.EventOption {
e.extraSkipInStack += 1
return trace.WithAttributes(e.IntoTraceAttributes()...)
}
func NewErrInSpan[E IntoErrorEvent](err E, span trace.Span) E {
er := err.IntoErrorEvent()
er.extraSkipInStack += 1
span.RecordError(er.Err, er.AsOpts())
return err
}

View File

@ -14,7 +14,7 @@ import (
type TraceFormatter interface { type TraceFormatter interface {
FormatSpanStart(span trace.ReadOnlySpan, selectedAttrs []attribute.KeyValue, lvl level.SeverityLevel) (string, error) FormatSpanStart(span trace.ReadOnlySpan, selectedAttrs []attribute.KeyValue, lvl level.SeverityLevel) (string, error)
FormatSpanEnd(span trace.ReadOnlySpan, selectedAttrs []attribute.KeyValue, lvl level.SeverityLevel) (string, error) FormatSpanEnd(span trace.ReadOnlySpan, selectedAttrs []attribute.KeyValue, lvl level.SeverityLevel) (string, error)
FormatEvent(event trace.Event, selectedAttr []attribute.KeyValue, lvl level.SeverityLevel) (string, error) FormatEvent(event trace.Event, span trace.ReadOnlySpan, selectedAttr []attribute.KeyValue, lvl level.SeverityLevel) (string, error)
} }
// Configuration for the exporter. // Configuration for the exporter.
@ -57,7 +57,7 @@ type Processor struct {
// NOTE: The configuration might change in future releases // NOTE: The configuration might change in future releases
func DefaultConsoleExportProcessor() trace.SpanProcessor { func DefaultConsoleExportProcessor() trace.SpanProcessor {
fmt := NewMultilineFormatter() fmt := NewPrettyMultilineFormatter()
return NewProcessor(ProcessorOptions{ return NewProcessor(ProcessorOptions{
FilterFromEnvVar: nil, FilterFromEnvVar: nil,
FilterOutFields: []attribute.Key{}, FilterOutFields: []attribute.Key{},
@ -85,15 +85,16 @@ func NewProcessor(opts ProcessorOptions) trace.SpanProcessor {
} }
return &Processor{ return &Processor{
traceFormatter: formatter, traceFormatter: formatter,
removedFields: opts.FilterOutFields, removedFields: opts.FilterOutFields,
addTraceId: opts.AddTraceId, addTraceId: opts.AddTraceId,
onlyEvents: opts.EmitEventsOnly, onlyEvents: opts.EmitEventsOnly,
onlyErrs: opts.SkipNonErrors, onlyErrs: opts.SkipNonErrors,
skipSpanStart: opts.SkipEmittingOnSpanStart, skipSpanStart: opts.SkipEmittingOnSpanStart,
skipSpanEnd: opts.SkipEmittingOnSpanEnd, skipSpanEnd: opts.SkipEmittingOnSpanEnd,
skipAttrsOnSpanEnd: opts.SkipAttributesOnSpanEnd, skipAttrsOnSpanEnd: opts.SkipAttributesOnSpanEnd,
lvl: lvl, skipAttrsOnSpanStart: opts.SkippAttributesOnSpanStart,
lvl: lvl,
} }
} }
@ -151,7 +152,7 @@ func (e *Processor) OnEnd(span trace.ReadOnlySpan) {
filteredAttrs = append(filteredAttrs, attribute.String("trace_id", span.SpanContext().TraceID().String())) filteredAttrs = append(filteredAttrs, attribute.String("trace_id", span.SpanContext().TraceID().String()))
} }
if severityLvl <= e.lvl { if severityLvl <= e.lvl {
eventString, err := e.traceFormatter.FormatEvent(event, filteredAttrs, severityLvl) eventString, err := e.traceFormatter.FormatEvent(event, span, filteredAttrs, severityLvl)
if err != nil { if err != nil {
fmt.Println("FAILED TO FORMAT TRACE EVENT") fmt.Println("FAILED TO FORMAT TRACE EVENT")
} else { } else {

View File

@ -2,6 +2,7 @@ package console_exporter
import ( import (
"fmt" "fmt"
"slices"
"git.ma-al.com/gora_filip/pkg/console_fmt" "git.ma-al.com/gora_filip/pkg/console_fmt"
"git.ma-al.com/gora_filip/pkg/level" "git.ma-al.com/gora_filip/pkg/level"
@ -9,16 +10,24 @@ import (
"go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace"
) )
func NewMultilineFormatter() TraceFormatter { func NewPrettyMultilineFormatter() TraceFormatter {
return &MultilineFormatter{} return &PrettyMultilineFormatter{}
} }
// A formatter that will print only events using a multiline format with colors. // A formatter that will print only events using a multiline format with colors.
// It uses attributes from the [attr] and [semconv] packages. // It uses attributes from the [attr] and [semconv] packages.
type MultilineFormatter struct{} type PrettyMultilineFormatter struct{}
func (f *MultilineFormatter) FormatSpanStart(span trace.ReadOnlySpan, selectedAttrs []attribute.KeyValue, lvl level.SeverityLevel) (string, error) { func (f *PrettyMultilineFormatter) FormatSpanStart(span trace.ReadOnlySpan, selectedAttrs []attribute.KeyValue, lvl level.SeverityLevel) (string, error) {
attrs := "" attrs := ""
slices.SortFunc(selectedAttrs, func(a, b attribute.KeyValue) int {
if a.Key > b.Key {
return 1
} else {
return -1
}
})
for _, kv := range selectedAttrs { for _, kv := range selectedAttrs {
if len(kv.Key) > 0 { if len(kv.Key) > 0 {
attrs += fmt.Sprintf("\t%s = %s\n", string(kv.Key), kv.Value.AsString()) attrs += fmt.Sprintf("\t%s = %s\n", string(kv.Key), kv.Value.AsString())
@ -33,8 +42,16 @@ func (f *MultilineFormatter) FormatSpanStart(span trace.ReadOnlySpan, selectedAt
return formattedSpanString, nil return formattedSpanString, nil
} }
func (f *MultilineFormatter) FormatSpanEnd(span trace.ReadOnlySpan, selectedAttrs []attribute.KeyValue, lvl level.SeverityLevel) (string, error) { func (f *PrettyMultilineFormatter) FormatSpanEnd(span trace.ReadOnlySpan, selectedAttrs []attribute.KeyValue, lvl level.SeverityLevel) (string, error) {
attrs := "" attrs := ""
slices.SortFunc(selectedAttrs, func(a, b attribute.KeyValue) int {
if a.Key > b.Key {
return 1
} else {
return -1
}
})
for _, kv := range selectedAttrs { for _, kv := range selectedAttrs {
if len(kv.Key) > 0 { if len(kv.Key) > 0 {
attrs += fmt.Sprintf("\t%s = %s\n", string(kv.Key), kv.Value.AsString()) attrs += fmt.Sprintf("\t%s = %s\n", string(kv.Key), kv.Value.AsString())
@ -50,8 +67,16 @@ func (f *MultilineFormatter) FormatSpanEnd(span trace.ReadOnlySpan, selectedAttr
return formattedSpanString, nil return formattedSpanString, nil
} }
func (f *MultilineFormatter) FormatEvent(event trace.Event, selectedAttrs []attribute.KeyValue, lvl level.SeverityLevel) (string, error) { func (f *PrettyMultilineFormatter) FormatEvent(event trace.Event, span trace.ReadOnlySpan, selectedAttrs []attribute.KeyValue, lvl level.SeverityLevel) (string, error) {
attrs := "" attrs := ""
slices.SortFunc(selectedAttrs, func(a, b attribute.KeyValue) int {
if a.Key > b.Key {
return 1
} else {
return -1
}
})
for _, kv := range selectedAttrs { for _, kv := range selectedAttrs {
if len(kv.Key) > 0 { if len(kv.Key) > 0 {
attrs += fmt.Sprintf("\t%s = %s\n", string(kv.Key), kv.Value.AsString()) attrs += fmt.Sprintf("\t%s = %s\n", string(kv.Key), kv.Value.AsString())

View File

@ -45,7 +45,8 @@ func Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption)
// NOTE: You can use [trace.WithAttributes] as a parameter to opts argument // NOTE: You can use [trace.WithAttributes] as a parameter to opts argument
func FStart(c *fiber.Ctx, opts ...trace.SpanStartOption) (context.Context, trace.Span) { func FStart(c *fiber.Ctx, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
return fiberOpentelemetry.Tracer.Start(c.UserContext(), c.Method()+" "+c.Path(), opts...) ctx := fiberOpentelemetry.FromCtx(c)
return fiberOpentelemetry.Tracer.Start(ctx, c.Method()+" "+c.Path(), opts...)
} }
func NewMiddleware(config Config) func(*fiber.Ctx) error { func NewMiddleware(config Config) func(*fiber.Ctx) error {