commit 7a6882c09ab3752e9e62a80dc3c5bdac8ac3e536 Author: Filip Gora Date: Fri Apr 26 15:09:54 2024 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/app/main.go b/app/main.go new file mode 100644 index 0000000..d3f272d --- /dev/null +++ b/app/main.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "log" + "maal/tracer/pkg/tracer" + "os" + "os/signal" + "time" + + "github.com/gofiber/fiber/v2" + "go.opentelemetry.io/otel/trace" +) + +func main() { + + main := fiber.New(fiber.Config{ + StreamRequestBody: true, + }) + + main.Use(tracer.NewTracer(tracer.Config{ + AppName: "test", + JaegerUrl: "http://localhost:4318/v1/traces", + GelfUrl: "192.168.220.30:12001", + Version: "1", + })) + defer tracer.ShutdownTracer() + + main.Get("/", func(c *fiber.Ctx) error { + _, span := tracer.Handler(c) + defer span.End() + + span.AddEvent("xd", trace.WithTimestamp(time.Now())) + + _, span = tracer.Handler(c) + defer span.End() + + return c.SendString("xd") + }) + + // handle interrupts (shutdown) + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + go func() { + oscall := <-c + log.Printf("system call: %+v", oscall) + main.Shutdown() + }() + if err := main.Listen(fmt.Sprintf(":%d", 3344)); err != nil { + log.Panic(err) + } + fmt.Println("Shutting down...") + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..064e3f5 --- /dev/null +++ b/go.mod @@ -0,0 +1,41 @@ +module maal/tracer + +go 1.22.0 + +require ( + github.com/gofiber/fiber/v2 v2.52.4 + github.com/psmarcin/fiber-opentelemetry v1.2.0 + go.opentelemetry.io/otel v1.26.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 + 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 + gopkg.in/Graylog2/go-gelf.v2 v2.0.0-20191017102106-1550ee647df0 +) + +require ( + github.com/andybalholm/brotli v1.0.5 // 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/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/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/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.51.0 // indirect + github.com/valyala/tcplisten v1.0.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 + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ce1cf28 --- /dev/null +++ b/go.sum @@ -0,0 +1,107 @@ +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/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= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +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/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= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= +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/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= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/psmarcin/fiber-opentelemetry v1.2.0 h1:3e3bz1jmwKMnoM5RVU4YzMXBq8tZQzzMDyM7DW1mTz8= +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/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= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +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/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +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= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 h1:1u/AyyOqAWzy+SkPxDpahCNZParHV8Vid1RnI2clyDE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0/go.mod h1:z46paqbJ9l7c9fIPCXTqTGwhQZ5XoTIsfeFYWboizjs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 h1:1wp/gyxsuYtuE/JFxsQRtcCDtMrO2qMvlfXALU5wkzI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0/go.mod h1:gbTHmghkGgqxMomVQQMur1Nba4M0MQ8AYThXDUjsJ38= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 h1:0W5o9SzoR15ocYHEQfvfipzcNog1lBxOLfnex91Hk6s= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0/go.mod h1:zVZ8nz+VSggWmnh6tTsJqXQ7rU4xLwRtna1M4x5jq58= +go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= +go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= +go.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8= +go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs= +go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs= +go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= +go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/Graylog2/go-gelf.v2 v2.0.0-20191017102106-1550ee647df0 h1:Xg23ydYYJLmb9AK3XdcEpplHZd1MpN3X2ZeeMoBClmY= +gopkg.in/Graylog2/go-gelf.v2 v2.0.0-20191017102106-1550ee647df0/go.mod h1:CeDeqW4tj9FrgZXF/dQCWZrBdcZWWBenhJtxLH4On2g= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/gelf_exporter/config.go b/pkg/gelf_exporter/config.go new file mode 100644 index 0000000..e3ef1b1 --- /dev/null +++ b/pkg/gelf_exporter/config.go @@ -0,0 +1,68 @@ +package gelfexporter + +type config struct { + GelfUrl string +} + +// newConfig creates a validated Config configured with options. +func newConfig(options ...Option) (config, error) { + cfg := config{} + for _, opt := range options { + cfg = opt.apply(cfg) + } + return cfg, nil +} + +// Option sets the value of an option for a Config. +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) +} + +type gelfUrlOption string + +func (o gelfUrlOption) apply(cfg config) config { + cfg.GelfUrl = string(o) + return cfg +} + +// // WithPrettyPrint prettifies the emitted output. +// func WithPrettyPrint() Option { +// return prettyPrintOption(true) +// } + +// type prettyPrintOption bool + +// 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 +// } diff --git a/pkg/gelf_exporter/gelf.go b/pkg/gelf_exporter/gelf.go new file mode 100644 index 0000000..a017386 --- /dev/null +++ b/pkg/gelf_exporter/gelf.go @@ -0,0 +1,46 @@ +package gelfexporter + +import ( + "log" + "maal/tracer/pkg/level" + "time" + + "gopkg.in/Graylog2/go-gelf.v2/gelf" +) + +type GELFMessage struct { + // Name of the application + Host string `json:"host"` + // Short, descriptive message + ShortMessage string `json:"short_message"` + // Optional long message. + LongMessage string `json:"long_message,omitempty"` + // Timestamp in Unix + Timestamp time.Time `json:"timestamp"` + // Severity level matching Syslog standard. + Level level.SyslogLevel `json:"level"` + + // All additional field names must start with an underline. + ExtraFields map[string]interface{} `json:"extrafields,omitempty"` +} + +func Log(writer *gelf.UDPWriter, msg GELFMessage) { + if writer != nil { + err := writer.WriteMessage(msg.GELFFormat()) + if err != nil { + log.Println(err) + } + } +} + +func (g GELFMessage) GELFFormat() *gelf.Message { + return &gelf.Message{ + Version: "1.1", + Host: g.Host, + Short: g.ShortMessage, + Full: g.LongMessage, + TimeUnix: float64(g.Timestamp.Unix()), + Level: int32(g.Level), + Extra: g.ExtraFields, + } +} diff --git a/pkg/gelf_exporter/trace.go b/pkg/gelf_exporter/trace.go new file mode 100644 index 0000000..1432b6d --- /dev/null +++ b/pkg/gelf_exporter/trace.go @@ -0,0 +1,104 @@ +package gelfexporter + +import ( + "context" + "fmt" + "sync" + "time" + + "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. +func New(options ...Option) (*Exporter, error) { + cfg, err := newConfig(options...) + if err != nil { + return nil, err + } + + writer, err := gelf.NewUDPWriter(cfg.GelfUrl) + if err != nil { + return nil, err + } + + return &Exporter{ + gelfWriter: writer, + }, nil + +} + +// 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 +} + +// 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 + } + e.stoppedMu.RLock() + stopped := e.stopped + e.stoppedMu.RUnlock() + if stopped { + return nil + } + + if len(spans) == 0 { + return nil + } + + stubs := tracetest.SpanStubsFromReadOnlySpans(spans) + + 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 + } + } + + fmt.Printf("stub: %v\n", stub) + + Log(e.gelfWriter, GELFMessage{ + Host: "test", + ShortMessage: "test", + }) + + } + return nil +} + +// Shutdown is called to stop the exporter, it performs no action. +func (e *Exporter) Shutdown(ctx context.Context) error { + e.stoppedMu.Lock() + e.stopped = true + e.stoppedMu.Unlock() + + return nil +} + +// // MarshalLog is the marshaling function used by the logging system to represent this Exporter. +// func (e *Exporter) MarshalLog() interface{} { +// return struct { +// Type string +// WithTimestamps bool +// }{ +// Type: "stdout", +// WithTimestamps: e.timestamps, +// } +// } diff --git a/pkg/level/level.go b/pkg/level/level.go new file mode 100644 index 0000000..7dc774b --- /dev/null +++ b/pkg/level/level.go @@ -0,0 +1,84 @@ +package level + +import ( + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +type SyslogLevel uint8 + +const ( + EMERG SyslogLevel = iota + ALERT + CRIT + ERR + WARNING + NOTICE + INFO + DEBUG +) + +// Level Keyword Description +// 0 emergencies System is unusable +// 1 alerts Immediate action is needed +// 2 critical Critical conditions exist +// 3 errors Error conditions exist +// 4 warnings Warning conditions exist +// 5 notification Normal, but significant, conditions exist +// 6 informational Informational messages +// 7 debugging Debugging messages + +func (l SyslogLevel) String() string { + switch l { + case EMERG: + return "EMERG" + case ALERT: + return "ALERT" + case CRIT: + return "CRIT" + case ERR: + return "ERR" + case WARNING: + return "WARN" + case NOTICE: + return "NOTICE" + case INFO: + return "INFO" + case DEBUG: + return "DEBUG" + default: + return "CRIT" + } +} + +func LevelFromString(level string) SyslogLevel { + switch level { + case "EMERG": + return EMERG + case "ALERT": + return ALERT + case "CRIT": + return CRIT + case "ERR": + return ERR + case "WARN": + return WARNING + case "NOTICE": + return NOTICE + case "INFO": + return INFO + case "DEBUG": + return DEBUG + default: + return CRIT + } +} + +func (lvl SyslogLevel) SetAttribute(att ...attribute.KeyValue) trace.SpanStartEventOption { + + att = append(att, attribute.Int("level", int(lvl))) + + return trace.WithAttributes( + att..., + ) +} diff --git a/pkg/tracer/middleware.go b/pkg/tracer/middleware.go new file mode 100644 index 0000000..271bb4a --- /dev/null +++ b/pkg/tracer/middleware.go @@ -0,0 +1,160 @@ +package tracer + +import ( + "context" + "log" + gelfexporter "maal/tracer/pkg/gelf_exporter" + "os" + + "github.com/gofiber/fiber/v2" + fiberOpentelemetry "github.com/psmarcin/fiber-opentelemetry/pkg/fiber-otel" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" + "go.opentelemetry.io/otel/sdk/resource" + trc "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + trace "go.opentelemetry.io/otel/trace" +) + +var ( + TracingError error = nil + TP trc.TracerProvider +) + +type CustomExporter struct { + jaeger *otlptrace.Exporter + stdouttrace *stdouttrace.Exporter +} + +type Config struct { + AppName string + JaegerUrl string + GelfUrl string + Version string +} + +func NewCustomExporter(jaegerUrl string) (trc.SpanExporter, error) { + var jaeg *otlptrace.Exporter + var outrace *stdouttrace.Exporter + var err error + + outrace, err = stdouttrace.New( + stdouttrace.WithWriter(os.Stdout), + stdouttrace.WithPrettyPrint(), + stdouttrace.WithoutTimestamps(), + ) + if err != nil { + return &CustomExporter{}, err + } + + if len(jaegerUrl) > 0 { + jaeg = otlptracehttp.NewUnstarted(otlptracehttp.WithEndpointURL(jaegerUrl)) + } + + return &CustomExporter{ + jaeger: jaeg, + stdouttrace: outrace, + }, nil + +} + +func (e *CustomExporter) ExportSpans(ctx context.Context, spans []trc.ReadOnlySpan) error { + if TracingError == nil { + if e.jaeger != nil { + err := e.jaeger.ExportSpans(ctx, spans) + return err + } else { + return e.printOnlyOnError(ctx, spans) + } + } + return nil +} + +func (e *CustomExporter) printOnlyOnError(ctx context.Context, spans []trc.ReadOnlySpan) error { + var err error + for _, s := range spans { + if s.Status().Code == codes.Error { + err = e.stdouttrace.ExportSpans(ctx, spans) + break + } + } + return err +} + +func (e *CustomExporter) Shutdown(ctx context.Context) error { + if e.jaeger != nil { + e.stdouttrace.Shutdown(ctx) + return e.jaeger.Shutdown(ctx) + } else { + return e.stdouttrace.Shutdown(ctx) + } +} + +func newResource(config Config) *resource.Resource { + r := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(config.AppName), + semconv.ServiceVersionKey.String(config.Version), + attribute.String("service.provider", "maal"), + ) + return r +} + +func NewTracer(config Config) func(*fiber.Ctx) error { + l := log.New(os.Stdout, "", 0) + + var tracerProviders []trc.TracerProviderOption + + otlpExporter := otlptracehttp.NewUnstarted(otlptracehttp.WithEndpointURL(config.JaegerUrl)) + + gelfExporter, err := gelfexporter.New(gelfexporter.WithGelfUrl(config.GelfUrl)) + 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...) + + otel.SetTracerProvider(&TP) + otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) { + if err != TracingError { + TracingError = err + log.Println(err) + } + })) + + tracer := TP.Tracer("fiber-otel-router") + + return fiberOpentelemetry.New( + fiberOpentelemetry.Config{ + Tracer: tracer, + SpanName: "{{ .Method }} {{ .Path }}", + TracerStartAttributes: []trace.SpanStartOption{ + trace.WithSpanKind(trace.SpanKindServer), + trace.WithNewRoot(), + }, + }, + ) +} + +func ShutdownTracer() { + if err := TP.Shutdown(context.Background()); err != nil { + log.Fatal(err) + } +} + +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")) + + return simpleCtx, span +}