diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..3b865ee --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "./app" + } + ] +} \ No newline at end of file diff --git a/app/main.go b/app/main.go index d3f272d..b410bad 100644 --- a/app/main.go +++ b/app/main.go @@ -1,8 +1,10 @@ package main import ( + "context" "fmt" "log" + "maal/tracer/pkg/level" "maal/tracer/pkg/tracer" "os" "os/signal" @@ -12,6 +14,9 @@ import ( "go.opentelemetry.io/otel/trace" ) +type AttributesX struct { +} + func main() { main := fiber.New(fiber.Config{ @@ -21,19 +26,30 @@ func main() { main.Use(tracer.NewTracer(tracer.Config{ AppName: "test", JaegerUrl: "http://localhost:4318/v1/traces", - GelfUrl: "192.168.220.30:12001", + GelfUrl: "192.168.220.30:12201", Version: "1", })) defer tracer.ShutdownTracer() main.Get("/", func(c *fiber.Ctx) error { - _, span := tracer.Handler(c) + ctx, span := tracer.Handler(c) defer span.End() - span.AddEvent("xd", trace.WithTimestamp(time.Now())) + span.AddEvent( + "smthing is happening", + trace.WithAttributes( + tracer.LongMessage("smthing is happening - long"), + tracer.JsonAttr("smth", map[string]interface{}{ + "xd": 1, + }), + tracer.Level(level.ALERT), + ), + ) - _, span = tracer.Handler(c) - defer span.End() + err := Serv(ctx) + if err != nil { + return tracer.RecordError(span, err) + } return c.SendString("xd") }) @@ -52,3 +68,30 @@ func main() { fmt.Println("Shutting down...") } + +func Serv(ctx context.Context) *fiber.Error { + ctx, span := tracer.Service(ctx, "serv", "name of the span") + defer span.End() + + for range []int{1, 2, 3} { + time.Sleep(time.Millisecond * 100) + } + + err := Repo(ctx) + if err != nil { + return fiber.NewError(500, "xd") + } + + return fiber.NewError(500, "x") +} + +func Repo(ctx context.Context) error { + ctx, span := tracer.Repository(ctx, "serv", "name of the span") + defer span.End() + + for range []int{1, 2, 3} { + time.Sleep(time.Millisecond * 100) + } + + return nil +} diff --git a/go.mod b/go.mod index 064e3f5..2f7ca32 100644 --- a/go.mod +++ b/go.mod @@ -11,28 +11,30 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 go.opentelemetry.io/otel/sdk v1.26.0 go.opentelemetry.io/otel/trace v1.26.0 + golang.org/x/sys v0.19.0 gopkg.in/Graylog2/go-gelf.v2 v2.0.0-20191017102106-1550ee647df0 ) require ( - github.com/andybalholm/brotli v1.0.5 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/gofiber/contrib/otelfiber/v2 v2.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/klauspost/compress v1.17.8 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.51.0 // indirect + github.com/valyala/fasthttp v1.52.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect + go.opentelemetry.io/contrib v1.26.0 // indirect go.opentelemetry.io/otel/metric v1.26.0 // indirect go.opentelemetry.io/proto/otlp v1.2.0 // indirect golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect diff --git a/go.sum b/go.sum index ce1cf28..185ce23 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -12,6 +14,8 @@ 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/gofiber/contrib/otelfiber/v2 v2.1.0 h1:BKKRxHy9nYU1dKQeYb8cqNDz+ZSXMD1TfoU1VJO11fs= +github.com/gofiber/contrib/otelfiber/v2 v2.1.0/go.mod h1:cvzG6aRv44JMKs838sq3bh2S7hPfbsLXFEhhLaqIGeU= github.com/gofiber/fiber/v2 v2.31.0/go.mod h1:1Ega6O199a3Y7yDGuM9FyXDPYQfv+7/y48wl6WCwUF4= github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM= github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= @@ -25,6 +29,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHg github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 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= @@ -38,6 +44,8 @@ github.com/psmarcin/fiber-opentelemetry v1.2.0 h1:3e3bz1jmwKMnoM5RVU4YzMXBq8tZQz github.com/psmarcin/fiber-opentelemetry v1.2.0/go.mod h1:qcEVkzlD0GrjtCS+hd5/0QhbTOy12KNyaqmp4yfXi1c= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= @@ -47,8 +55,12 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0= github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0= +github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +go.opentelemetry.io/contrib v1.26.0 h1:8/CmxLl5uDm37V9rdqbZcVLvYigAE2vMostBq3nGDrI= +go.opentelemetry.io/contrib v1.26.0/go.mod h1:Tmhw9grdWtmXy6DxZNpIAudzYJqLeEM2P6QTZQSRwU8= go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI= go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= diff --git a/pkg/gelf_exporter/config.go b/pkg/gelf_exporter/config.go index e3ef1b1..e9315d2 100644 --- a/pkg/gelf_exporter/config.go +++ b/pkg/gelf_exporter/config.go @@ -2,6 +2,7 @@ package gelfexporter type config struct { GelfUrl string + AppName string } // newConfig creates a validated Config configured with options. @@ -18,20 +19,6 @@ type Option interface { apply(config) config } -// WithWriter sets the export stream destination. -// func WithWriter(w io.Writer) Option { -// return writerOption{w} -// } - -// type writerOption struct { -// W *gelf.Writer -// } - -// func (o writerOption) apply(cfg config) config { -// cfg.Writer = o.W -// return cfg -// } - func WithGelfUrl(url string) Option { return gelfUrlOption(url) } @@ -43,26 +30,13 @@ func (o gelfUrlOption) apply(cfg config) config { return cfg } -// // WithPrettyPrint prettifies the emitted output. -// func WithPrettyPrint() Option { -// return prettyPrintOption(true) -// } +func WithAppName(url string) Option { + return appName(url) +} -// type prettyPrintOption bool +type appName string -// func (o prettyPrintOption) apply(cfg config) config { -// cfg.PrettyPrint = bool(o) -// return cfg -// } - -// // WithoutTimestamps sets the export stream to not include timestamps. -// func WithoutTimestamps() Option { -// return timestampsOption(false) -// } - -// type timestampsOption bool - -// func (o timestampsOption) apply(cfg config) config { -// cfg.Timestamps = bool(o) -// return cfg -// } +func (o appName) apply(cfg config) config { + cfg.AppName = string(o) + return cfg +} diff --git a/pkg/gelf_exporter/gelf.go b/pkg/gelf_exporter/gelf.go index a017386..6381e78 100644 --- a/pkg/gelf_exporter/gelf.go +++ b/pkg/gelf_exporter/gelf.go @@ -1,6 +1,7 @@ package gelfexporter import ( + "fmt" "log" "maal/tracer/pkg/level" "time" @@ -30,6 +31,7 @@ func Log(writer *gelf.UDPWriter, msg GELFMessage) { if err != nil { log.Println(err) } + fmt.Printf("msg: %v sent\n", msg) } } diff --git a/pkg/gelf_exporter/trace.go b/pkg/gelf_exporter/trace.go index 1432b6d..4c5c899 100644 --- a/pkg/gelf_exporter/trace.go +++ b/pkg/gelf_exporter/trace.go @@ -2,17 +2,15 @@ package gelfexporter import ( "context" - "fmt" + "maal/tracer/pkg/level" "sync" - "time" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "gopkg.in/Graylog2/go-gelf.v2/gelf" ) -var zeroTime time.Time - var _ trace.SpanExporter = &Exporter{} // New creates an Exporter with the passed options. @@ -33,16 +31,13 @@ func New(options ...Option) (*Exporter, error) { } -// Exporter is an implementation of trace.SpanSyncer that writes spans to stdout. type Exporter struct { - timestamps bool gelfWriter *gelf.UDPWriter - - stoppedMu sync.RWMutex - stopped bool + appName string + stoppedMu sync.RWMutex + stopped bool } -// ExportSpans writes spans in json format to stdout. func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { if err := ctx.Err(); err != nil { return err @@ -62,27 +57,70 @@ func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) for i := range stubs { stub := &stubs[i] - // Remove timestamps - if !e.timestamps { - stub.StartTime = zeroTime - stub.EndTime = zeroTime - for j := range stub.Events { - ev := &stub.Events[j] - ev.Time = zeroTime - } + + var attributes = make(map[string]interface{}) + for _, attr := range stub.Attributes { + attributes[string(attr.Key)] = GetByType(attr.Value) } - fmt.Printf("stub: %v\n", stub) + attributes["from"] = "test" - Log(e.gelfWriter, GELFMessage{ - Host: "test", - ShortMessage: "test", - }) + for i := range stub.Events { + event := stub.Events[i] + + var gelf GELFMessage = GELFMessage{ + Host: "salego", + ShortMessage: event.Name, + Timestamp: stub.StartTime, + Level: level.DEBUG, + ExtraFields: attributes, + } + for _, attr := range event.Attributes { + if attr.Key == "long_message_" { + gelf.LongMessage = attr.Value.AsString() + continue + } + + if attr.Key == "level_" { + gelf.Level = level.SyslogLevel(attr.Value.AsInt64()) + continue + } + + attributes[string(attr.Key)] = GetByType(attr.Value) + } + + Log(e.gelfWriter, gelf) + } } return nil } +func GetByType(val attribute.Value) interface{} { + switch val.Type() { + case attribute.INVALID: + return "invalid value" + case attribute.BOOL: + return val.AsBool() + case attribute.INT64: + return val.AsInt64() + case attribute.FLOAT64: + return val.AsFloat64() + case attribute.STRING: + return val.AsString() + case attribute.BOOLSLICE: + return val.AsBoolSlice() + case attribute.INT64SLICE: + return val.AsInt64Slice() + case attribute.FLOAT64SLICE: + return val.AsFloat64Slice() + case attribute.STRINGSLICE: + return val.AsStringSlice() + } + + return "invalid value" +} + // Shutdown is called to stop the exporter, it performs no action. func (e *Exporter) Shutdown(ctx context.Context) error { e.stoppedMu.Lock() diff --git a/pkg/tracer/event.go b/pkg/tracer/event.go new file mode 100644 index 0000000..6a5ce04 --- /dev/null +++ b/pkg/tracer/event.go @@ -0,0 +1,32 @@ +package tracer + +import ( + "encoding/json" + "maal/tracer/pkg/level" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +func LongMessage(message string) attribute.KeyValue { + return attribute.KeyValue{Key: "long_message_", Value: attribute.StringValue(message)} +} + +func Level(level level.SyslogLevel) attribute.KeyValue { + return attribute.KeyValue{Key: "level_", Value: attribute.Int64Value(int64(level))} +} + +func JsonAttr(key string, jsonEl map[string]interface{}) attribute.KeyValue { + + jsonStr, _ := json.Marshal(jsonEl) + return attribute.KeyValue{Key: attribute.Key(key), Value: attribute.StringValue(string(jsonStr))} + +} + +func RecordError(span trace.Span, err error) error { + span.SetStatus(codes.Error, err.Error()) + span.RecordError(err) + + return nil +} diff --git a/pkg/tracer/middleware.go b/pkg/tracer/middleware.go index 271bb4a..9726d08 100644 --- a/pkg/tracer/middleware.go +++ b/pkg/tracer/middleware.go @@ -2,9 +2,11 @@ package tracer import ( "context" + "fmt" "log" gelfexporter "maal/tracer/pkg/gelf_exporter" "os" + "runtime" "github.com/gofiber/fiber/v2" fiberOpentelemetry "github.com/psmarcin/fiber-opentelemetry/pkg/fiber-otel" @@ -111,13 +113,15 @@ func NewTracer(config Config) func(*fiber.Ctx) error { otlpExporter := otlptracehttp.NewUnstarted(otlptracehttp.WithEndpointURL(config.JaegerUrl)) - gelfExporter, err := gelfexporter.New(gelfexporter.WithGelfUrl(config.GelfUrl)) + gelfExporter, err := gelfexporter.New( + gelfexporter.WithGelfUrl(config.GelfUrl), + gelfexporter.WithAppName("salego"), + ) if err != nil { l.Fatal(err) } tracerProviders = append(tracerProviders, trc.WithBatcher(otlpExporter)) tracerProviders = append(tracerProviders, trc.WithBatcher(gelfExporter)) - tracerProviders = append(tracerProviders, trc.WithResource(newResource(config))) TP = *trc.NewTracerProvider(tracerProviders...) @@ -154,7 +158,45 @@ func Handler(fc *fiber.Ctx) (context.Context, trace.Span) { simpleCtx, span := fiberOpentelemetry.Tracer.Start(fc.UserContext(), fc.OriginalURL()) fc.SetUserContext(simpleCtx) - span.SetAttributes(attribute.String("service.layer", "handler")) + _, file, line, _ := runtime.Caller(1) + span.SetAttributes( + attribute.String("service.layer", "handler"), + attribute.String("file", file), + attribute.String("line", fmt.Sprintf("%d", line)), + ) return simpleCtx, span } + +func Service(c context.Context, serviceName string, spanName string) (context.Context, trace.Span) { + simpleCtx, span := fiberOpentelemetry.Tracer.Start(c, spanName) + var attribs []attribute.KeyValue + + _, file, line, _ := runtime.Caller(1) + attribs = append( + attribs, + attribute.String("service.name", serviceName), + attribute.String("service.layer", "service"), + attribute.String("file", file), + attribute.String("line", fmt.Sprintf("%d", line)), + ) + + span.SetAttributes(attribs...) + + return simpleCtx, span +} + +func Repository(c context.Context, repositoryName string, spanName string) (context.Context, trace.Span) { + ctx2, span := fiberOpentelemetry.Tracer.Start(c, spanName) + var attribs []attribute.KeyValue + + _, file, line, _ := runtime.Caller(1) + attribs = append(attribs, + attribute.String("service.repository", repositoryName), + attribute.String("file", file), + attribute.String("line", fmt.Sprintf("%d", line)), + ) + span.SetAttributes(attribs...) + + return ctx2, span +}