Compare commits
4 commits
9856e2b795
...
18125db231
Author | SHA1 | Date | |
---|---|---|---|
18125db231 | |||
f3bbc9ae88 | |||
11055de7da | |||
8b73581a2f |
13 changed files with 391 additions and 292 deletions
27
go.mod
27
go.mod
|
@ -4,8 +4,8 @@ go 1.22.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/getsentry/sentry-go v0.28.1
|
github.com/getsentry/sentry-go v0.28.1
|
||||||
|
github.com/go-logr/logr v1.4.2
|
||||||
github.com/gofiber/fiber/v2 v2.52.5
|
github.com/gofiber/fiber/v2 v2.52.5
|
||||||
github.com/jackc/pgx/v5 v5.6.0
|
|
||||||
github.com/qustavo/sqlhooks/v2 v2.1.0
|
github.com/qustavo/sqlhooks/v2 v2.1.0
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/valyala/fasthttp v1.55.0
|
github.com/valyala/fasthttp v1.55.0
|
||||||
|
@ -23,29 +23,24 @@ require (
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
|
||||||
github.com/klauspost/compress v1.17.9 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // 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/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||||
golang.org/x/crypto v0.24.0 // indirect
|
golang.org/x/net v0.28.0 // indirect
|
||||||
golang.org/x/net v0.26.0 // indirect
|
golang.org/x/sys v0.24.0 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/text v0.17.0 // indirect
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
google.golang.org/appengine v1.6.8 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
google.golang.org/grpc v1.65.0 // indirect
|
||||||
google.golang.org/grpc v1.64.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
)
|
)
|
||||||
|
|
80
go.sum
80
go.sum
|
@ -20,20 +20,15 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
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/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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
|
||||||
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
|
||||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
|
||||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
|
||||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
|
||||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||||
|
@ -44,8 +39,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
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-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.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
|
@ -57,15 +52,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/qustavo/sqlhooks/v2 v2.1.0 h1:54yBemHnGHp/7xgT+pxwmIlMSDNYKx5JW5dfRAiCZi0=
|
github.com/qustavo/sqlhooks/v2 v2.1.0 h1:54yBemHnGHp/7xgT+pxwmIlMSDNYKx5JW5dfRAiCZi0=
|
||||||
github.com/qustavo/sqlhooks/v2 v2.1.0/go.mod h1:aMREyKo7fOKTwiLuWPsaHRXEmtqG4yREztO0idF83AU=
|
github.com/qustavo/sqlhooks/v2 v2.1.0/go.mod h1:aMREyKo7fOKTwiLuWPsaHRXEmtqG4yREztO0idF83AU=
|
||||||
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.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/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.7.0/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 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
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 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
@ -74,6 +68,7 @@ github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8
|
||||||
github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM=
|
github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM=
|
||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk=
|
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk=
|
||||||
|
@ -98,31 +93,52 @@ go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeX
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
|
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||||
|
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
@ -32,7 +32,7 @@ func AddBreadcrumbToContext(c context.Context, breadcrumb *sentry.Breadcrumb) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Telemetry) Trace(ctx context.Context, op string, options []ConfigureSpanStartFunc, fn func(context.Context)) {
|
func (o *Telemetry) Trace(ctx context.Context, op string, options []SpanStartOpt, fn func(context.Context)) {
|
||||||
tx := o.StartSpan(ctx, op, op, options...)
|
tx := o.StartSpan(ctx, op, op, options...)
|
||||||
defer tx.End()
|
defer tx.End()
|
||||||
|
|
||||||
|
|
3
span.go
3
span.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.devminer.xyz/devminer/unitel/unitelutils"
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
@ -208,7 +209,7 @@ func (s *Span) SetUser(id uint64, username, ip, permissions string) *Span {
|
||||||
s.otel.SetAttributes(
|
s.otel.SetAttributes(
|
||||||
semconv.EnduserID(uid),
|
semconv.EnduserID(uid),
|
||||||
semconv.EnduserScope(permissions),
|
semconv.EnduserScope(permissions),
|
||||||
attribute.String(libBase+"/username", username),
|
attribute.String(unitelutils.Base+"/username", username),
|
||||||
semconv.ClientAddress(ip),
|
semconv.ClientAddress(ip),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
23
telemetry.go
23
telemetry.go
|
@ -26,7 +26,10 @@ type telemetryContextKey struct{}
|
||||||
|
|
||||||
type Opts struct {
|
type Opts struct {
|
||||||
Environment string
|
Environment string
|
||||||
|
|
||||||
ServiceName string
|
ServiceName string
|
||||||
|
ServiceVersion string
|
||||||
|
|
||||||
OTLPEndpoint string
|
OTLPEndpoint string
|
||||||
SentryDSN string
|
SentryDSN string
|
||||||
}
|
}
|
||||||
|
@ -42,21 +45,21 @@ func ParseOpts(serviceName string) Opts {
|
||||||
|
|
||||||
type Telemetry struct {
|
type Telemetry struct {
|
||||||
Opts Opts
|
Opts Opts
|
||||||
propagator propagation.TextMapPropagator
|
Propagator propagation.TextMapPropagator
|
||||||
tracerProvider trace.TracerProvider
|
TracerProvider trace.TracerProvider
|
||||||
meterProvider metric.MeterProvider
|
MeterProvider metric.MeterProvider
|
||||||
Tracer trace.Tracer
|
Tracer trace.Tracer
|
||||||
Meter metric.Meter
|
Meter metric.Meter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Telemetry) Shutdown(ctx context.Context) {
|
func (o *Telemetry) Shutdown(ctx context.Context) {
|
||||||
if tp, ok := o.tracerProvider.(*sdktrace.TracerProvider); ok {
|
if tp, ok := o.TracerProvider.(*sdktrace.TracerProvider); ok {
|
||||||
if err := tp.Shutdown(ctx); err != nil {
|
if err := tp.Shutdown(ctx); err != nil {
|
||||||
log.Error().Err(err).Msg("failed to shutdown TracerProvider")
|
log.Error().Err(err).Msg("failed to shutdown TracerProvider")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if mp, ok := o.meterProvider.(*sdkmetric.MeterProvider); ok {
|
if mp, ok := o.MeterProvider.(*sdkmetric.MeterProvider); ok {
|
||||||
if err := mp.Shutdown(ctx); err != nil {
|
if err := mp.Shutdown(ctx); err != nil {
|
||||||
log.Error().Err(err).Msg("failed to stop MeterProvider")
|
log.Error().Err(err).Msg("failed to stop MeterProvider")
|
||||||
}
|
}
|
||||||
|
@ -121,7 +124,7 @@ func initializeSentry(opts Opts) error {
|
||||||
|
|
||||||
return sentry.Init(sentry.ClientOptions{
|
return sentry.Init(sentry.ClientOptions{
|
||||||
Dsn: opts.SentryDSN,
|
Dsn: opts.SentryDSN,
|
||||||
Release: libVersion,
|
Release: opts.ServiceVersion,
|
||||||
AttachStacktrace: true,
|
AttachStacktrace: true,
|
||||||
SampleRate: getSampleRate(opts),
|
SampleRate: getSampleRate(opts),
|
||||||
EnableTracing: true,
|
EnableTracing: true,
|
||||||
|
@ -157,7 +160,7 @@ func Initialize(opts Opts) (*Telemetry, error) {
|
||||||
resource.NewWithAttributes(
|
resource.NewWithAttributes(
|
||||||
semconv.SchemaURL,
|
semconv.SchemaURL,
|
||||||
semconv.ServiceName(opts.ServiceName),
|
semconv.ServiceName(opts.ServiceName),
|
||||||
semconv.ServiceVersion(libVersion),
|
semconv.ServiceVersion(opts.ServiceVersion),
|
||||||
semconv.DeploymentEnvironment(opts.Environment),
|
semconv.DeploymentEnvironment(opts.Environment),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -219,9 +222,9 @@ func Initialize(opts Opts) (*Telemetry, error) {
|
||||||
|
|
||||||
return &Telemetry{
|
return &Telemetry{
|
||||||
Opts: opts,
|
Opts: opts,
|
||||||
propagator: propagator,
|
Propagator: propagator,
|
||||||
tracerProvider: tracerProvider,
|
TracerProvider: tracerProvider,
|
||||||
meterProvider: metricProvider,
|
MeterProvider: metricProvider,
|
||||||
Tracer: tracerProvider.Tracer(opts.ServiceName),
|
Tracer: tracerProvider.Tracer(opts.ServiceName),
|
||||||
Meter: metricProvider.Meter(opts.ServiceName),
|
Meter: metricProvider.Meter(opts.ServiceName),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
package unitel
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/getsentry/sentry-go"
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
)
|
|
||||||
|
|
||||||
const httpClientTransportClientID = libBase + "#HTTPTansport"
|
|
||||||
|
|
||||||
type HTTPTransport struct {
|
|
||||||
telemetry *Telemetry
|
|
||||||
forwardTrace bool
|
|
||||||
tracedRequestHeaders []string
|
|
||||||
tracedResponseHeaders []string
|
|
||||||
Transport http.RoundTripper
|
|
||||||
|
|
||||||
tracer trace.Tracer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Telemetry) NewTracedTransport(transport http.RoundTripper, forwardTrace bool, tracedRequestHeaders []string, tracedResponseHeaders []string) *HTTPTransport {
|
|
||||||
return &HTTPTransport{
|
|
||||||
telemetry: t,
|
|
||||||
forwardTrace: forwardTrace,
|
|
||||||
tracedRequestHeaders: tracedRequestHeaders,
|
|
||||||
tracedResponseHeaders: tracedResponseHeaders,
|
|
||||||
Transport: transport,
|
|
||||||
|
|
||||||
tracer: t.tracerProvider.Tracer(httpClientTransportClientID, trace.WithInstrumentationVersion(libVersion)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *HTTPTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
||||||
s := t.telemetry.
|
|
||||||
StartSpan(req.Context(), "http.client", fmt.Sprintf("%s %s", req.Method, req.URL), WithOtelOptions(trace.WithSpanKind(trace.SpanKindClient))).
|
|
||||||
AddAttributes(
|
|
||||||
semconv.HTTPRequestMethodKey.String(req.Method),
|
|
||||||
semconv.URLFull(req.URL.String()),
|
|
||||||
)
|
|
||||||
defer s.End()
|
|
||||||
ctx := s.Context()
|
|
||||||
req = req.WithContext(ctx)
|
|
||||||
|
|
||||||
if t.forwardTrace {
|
|
||||||
t.telemetry.InjectIntoHeaders(ctx, req.Header)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, header := range t.tracedRequestHeaders {
|
|
||||||
vv := req.Header.Values(header)
|
|
||||||
for i, v := range vv {
|
|
||||||
s.AddAttributes(attribute.String(formatHeaderAttribute("http.request", header, vv, i), v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := t.Transport.RoundTrip(req)
|
|
||||||
if err != nil {
|
|
||||||
s.CaptureBreadcrumb(SeverityError, BreadcrumbTypeHTTP, BreadcrumbCatagoryHTTP, fmt.Sprintf("Failed to send request to %s: %v", req.URL.String(), err), map[string]any{
|
|
||||||
"url": req.URL.String(),
|
|
||||||
"method": req.Method,
|
|
||||||
})
|
|
||||||
|
|
||||||
if hub := sentry.GetHubFromContext(ctx); hub != nil {
|
|
||||||
hub.CaptureException(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
severity := SeverityDebug
|
|
||||||
if resp.StatusCode < http.StatusBadRequest {
|
|
||||||
severity = SeverityInfo
|
|
||||||
} else if resp.StatusCode < http.StatusInternalServerError {
|
|
||||||
severity = SeverityError
|
|
||||||
} else {
|
|
||||||
severity = SeverityError
|
|
||||||
}
|
|
||||||
s.CaptureBreadcrumb(severity, BreadcrumbTypeHTTP, BreadcrumbCatagoryHTTP, req.URL.String(), map[string]any{
|
|
||||||
"url": req.URL.String(),
|
|
||||||
"method": req.Method,
|
|
||||||
"status": resp.StatusCode,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
s.
|
|
||||||
AddAttributes(semconv.HTTPResponseStatusCode(resp.StatusCode)).
|
|
||||||
SetStatus(httpStatusToSpanStatus(resp.StatusCode, false), "")
|
|
||||||
|
|
||||||
for _, header := range t.tracedResponseHeaders {
|
|
||||||
vv := resp.Header.Values(header)
|
|
||||||
for i, v := range vv {
|
|
||||||
s.AddAttributes(attribute.String(formatHeaderAttribute("http.response", header, vv, i), v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp, err
|
|
||||||
}
|
|
20
tracing.go
20
tracing.go
|
@ -23,9 +23,9 @@ var (
|
||||||
tracerContextKey = contextKey{"tracer"}
|
tracerContextKey = contextKey{"tracer"}
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigureSpanStartFunc = func(context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption)
|
type SpanStartOpt = func(context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption)
|
||||||
|
|
||||||
func (t *Telemetry) StartSpan(ctx context.Context, operation, name string, cfgs ...ConfigureSpanStartFunc) *Span {
|
func (t *Telemetry) StartSpan(ctx context.Context, operation, name string, cfgs ...SpanStartOpt) *Span {
|
||||||
otelStartOpts := make([]trace.SpanStartOption, 0)
|
otelStartOpts := make([]trace.SpanStartOption, 0)
|
||||||
sentryStartOpts := []sentry.SpanOption{sentry.WithTransactionName(name), sentry.WithDescription(name)}
|
sentryStartOpts := []sentry.SpanOption{sentry.WithTransactionName(name), sentry.WithDescription(name)}
|
||||||
|
|
||||||
|
@ -55,14 +55,14 @@ func (t *Telemetry) StartSpan(ctx context.Context, operation, name string, cfgs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithOtelOptions(opts ...trace.SpanStartOption) ConfigureSpanStartFunc {
|
func WithOtelOptions(opts ...trace.SpanStartOption) SpanStartOpt {
|
||||||
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
||||||
return ctx, opts, []sentry.SpanOption{}
|
return ctx, opts, []sentry.SpanOption{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Telemetry) InjectIntoHeaders(ctx context.Context, headers http.Header) {
|
func (t *Telemetry) InjectIntoHeaders(ctx context.Context, headers http.Header) {
|
||||||
t.propagator.Inject(ctx, propagation.HeaderCarrier(headers))
|
t.Propagator.Inject(ctx, propagation.HeaderCarrier(headers))
|
||||||
|
|
||||||
if sentrySpan := sentry.SpanFromContext(ctx); sentrySpan == nil {
|
if sentrySpan := sentry.SpanFromContext(ctx); sentrySpan == nil {
|
||||||
// this should never happen...
|
// this should never happen...
|
||||||
|
@ -74,7 +74,7 @@ func (t *Telemetry) InjectIntoHeaders(ctx context.Context, headers http.Header)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Telemetry) InjectIntoMap(ctx context.Context, m map[string]string) {
|
func (t *Telemetry) InjectIntoMap(ctx context.Context, m map[string]string) {
|
||||||
t.propagator.Inject(ctx, propagation.MapCarrier(m))
|
t.Propagator.Inject(ctx, propagation.MapCarrier(m))
|
||||||
|
|
||||||
if sentrySpan := sentry.SpanFromContext(ctx); sentrySpan == nil {
|
if sentrySpan := sentry.SpanFromContext(ctx); sentrySpan == nil {
|
||||||
// this should never happen...
|
// this should never happen...
|
||||||
|
@ -85,9 +85,9 @@ func (t *Telemetry) InjectIntoMap(ctx context.Context, m map[string]string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Telemetry) ContinueFromHeaders(h http.Header) ConfigureSpanStartFunc {
|
func (t *Telemetry) ContinueFromHeaders(h http.Header) SpanStartOpt {
|
||||||
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
||||||
ctx = t.propagator.Extract(ctx, propagation.HeaderCarrier(h))
|
ctx = t.Propagator.Extract(ctx, propagation.HeaderCarrier(h))
|
||||||
|
|
||||||
return ctx,
|
return ctx,
|
||||||
[]trace.SpanStartOption{},
|
[]trace.SpanStartOption{},
|
||||||
|
@ -98,9 +98,9 @@ func (t *Telemetry) ContinueFromHeaders(h http.Header) ConfigureSpanStartFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Telemetry) ContinueFromMap(m map[string]string) ConfigureSpanStartFunc {
|
func (t *Telemetry) ContinueFromMap(m map[string]string) SpanStartOpt {
|
||||||
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
||||||
ctx = t.propagator.Extract(ctx, propagation.MapCarrier(m))
|
ctx = t.Propagator.Extract(ctx, propagation.MapCarrier(m))
|
||||||
|
|
||||||
sentryTrace := m[sentry.SentryTraceHeader]
|
sentryTrace := m[sentry.SentryTraceHeader]
|
||||||
sentryBaggage := m[sentry.SentryBaggageHeader]
|
sentryBaggage := m[sentry.SentryBaggageHeader]
|
||||||
|
@ -114,7 +114,7 @@ func (t *Telemetry) ContinueFromMap(m map[string]string) ConfigureSpanStartFunc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithOtelTracer(tracer trace.Tracer) ConfigureSpanStartFunc {
|
func WithOtelTracer(tracer trace.Tracer) SpanStartOpt {
|
||||||
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
||||||
return context.WithValue(ctx, tracerContextKey, tracer), []trace.SpanStartOption{}, []sentry.SpanOption{}
|
return context.WithValue(ctx, tracerContextKey, tracer), []trace.SpanStartOption{}, []sentry.SpanOption{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package unitel
|
package unitelhttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -6,20 +6,21 @@ import (
|
||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.devminer.xyz/devminer/unitel"
|
||||||
|
"git.devminer.xyz/devminer/unitel/unitelutils"
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/utils"
|
"github.com/gofiber/fiber/v2/utils"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/valyala/fasthttp/fasthttpadaptor"
|
"github.com/valyala/fasthttp/fasthttpadaptor"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
"go.opentelemetry.io/otel/metric"
|
"go.opentelemetry.io/otel/metric"
|
||||||
"go.opentelemetry.io/otel/propagation"
|
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
const fiberMwClientID = libBase + "#FiberMiddleware"
|
const fiberMwClientID = unitelutils.Base + "#FiberMiddleware@" + unitelutils.Version
|
||||||
|
|
||||||
type FiberMiddlewareConfig struct {
|
type FiberMiddlewareConfig struct {
|
||||||
Repanic bool
|
Repanic bool
|
||||||
|
@ -28,6 +29,8 @@ type FiberMiddlewareConfig struct {
|
||||||
TraceRequestHeaders []string
|
TraceRequestHeaders []string
|
||||||
TraceResponseHeaders []string
|
TraceResponseHeaders []string
|
||||||
IgnoredRoutes []string
|
IgnoredRoutes []string
|
||||||
|
Logger logr.Logger
|
||||||
|
TracePropagator TracePropagator
|
||||||
}
|
}
|
||||||
|
|
||||||
var fiberMiddlewareConfigDefault = FiberMiddlewareConfig{
|
var fiberMiddlewareConfigDefault = FiberMiddlewareConfig{
|
||||||
|
@ -37,17 +40,11 @@ var fiberMiddlewareConfigDefault = FiberMiddlewareConfig{
|
||||||
TraceRequestHeaders: []string{},
|
TraceRequestHeaders: []string{},
|
||||||
TraceResponseHeaders: []string{},
|
TraceResponseHeaders: []string{},
|
||||||
IgnoredRoutes: []string{},
|
IgnoredRoutes: []string{},
|
||||||
|
Logger: logr.Discard(),
|
||||||
|
TracePropagator: PropagateNoTraces,
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFiberMiddlewareTracer(tp trace.TracerProvider) trace.Tracer {
|
func FiberMiddleware(t *unitel.Telemetry, config ...FiberMiddlewareConfig) fiber.Handler {
|
||||||
return tp.Tracer(fiberMwClientID, trace.WithInstrumentationVersion(libVersion))
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFiberMiddlewareMeter(mp metric.MeterProvider) metric.Meter {
|
|
||||||
return mp.Meter(fiberMwClientID, metric.WithInstrumentationVersion(libVersion))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Telemetry) FiberMiddleware(config ...FiberMiddlewareConfig) fiber.Handler {
|
|
||||||
cfg := fiberMiddlewareConfigDefault
|
cfg := fiberMiddlewareConfigDefault
|
||||||
if len(config) > 0 {
|
if len(config) > 0 {
|
||||||
cfg = config[0]
|
cfg = config[0]
|
||||||
|
@ -64,9 +61,12 @@ func (t *Telemetry) FiberMiddleware(config ...FiberMiddlewareConfig) fiber.Handl
|
||||||
if cfg.IgnoredRoutes == nil {
|
if cfg.IgnoredRoutes == nil {
|
||||||
cfg.IgnoredRoutes = []string{}
|
cfg.IgnoredRoutes = []string{}
|
||||||
}
|
}
|
||||||
|
if cfg.TracePropagator == nil {
|
||||||
|
cfg.TracePropagator = PropagateNoTraces
|
||||||
|
}
|
||||||
|
|
||||||
meter := newFiberMiddlewareMeter(t.meterProvider)
|
meter := t.MeterProvider.Meter(fiberMwClientID, metric.WithInstrumentationVersion(unitelutils.Version))
|
||||||
tracer := newFiberMiddlewareTracer(t.tracerProvider)
|
tracer := t.TracerProvider.Tracer(fiberMwClientID, trace.WithInstrumentationVersion(unitelutils.Version))
|
||||||
|
|
||||||
mDuration, err := meter.Float64Histogram(
|
mDuration, err := meter.Float64Histogram(
|
||||||
"http.server.duration",
|
"http.server.duration",
|
||||||
|
@ -74,7 +74,8 @@ func (t *Telemetry) FiberMiddleware(config ...FiberMiddlewareConfig) fiber.Handl
|
||||||
metric.WithUnit("ms"),
|
metric.WithUnit("ms"),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("failed to create request duration histogram")
|
cfg.Logger.Error(err, "Failed to create request duration histogram")
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mActiveRequests, err := meter.Int64UpDownCounter(
|
mActiveRequests, err := meter.Int64UpDownCounter(
|
||||||
|
@ -83,30 +84,33 @@ func (t *Telemetry) FiberMiddleware(config ...FiberMiddlewareConfig) fiber.Handl
|
||||||
metric.WithUnit("1"),
|
metric.WithUnit("1"),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("failed to create active requests counter")
|
cfg.Logger.Error(err, "Failed to create active requests counter")
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mRequestSize, err := meter.Int64Histogram(
|
mRequestSize, err := meter.Int64Histogram(
|
||||||
"http.server.request.size",
|
"http.server.request.size",
|
||||||
metric.WithUnit("By"),
|
metric.WithUnit("By"),
|
||||||
metric.WithDescription("HTTP request sizes"),
|
metric.WithDescription("HTTP request body sizes"),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("failed to create request size histogram")
|
cfg.Logger.Error(err, "Failed to create request body size counter")
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mResponseSize, err := meter.Int64Histogram(
|
mResponseSize, err := meter.Int64Histogram(
|
||||||
"http.server.response.size",
|
"http.server.response.size",
|
||||||
metric.WithUnit("By"),
|
metric.WithUnit("By"),
|
||||||
metric.WithDescription("HTTP response sizes"),
|
metric.WithDescription("HTTP response body sizes"),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("failed to create response size histogram")
|
cfg.Logger.Error(err, "Failed to create response body size counter")
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(c *fiber.Ctx) error {
|
return func(c *fiber.Ctx) error {
|
||||||
ctx := c.UserContext()
|
ctx := c.UserContext()
|
||||||
ctx = SetOnContext(t, ctx)
|
ctx = unitel.SetOnContext(t, ctx)
|
||||||
c.SetUserContext(ctx)
|
c.SetUserContext(ctx)
|
||||||
|
|
||||||
// Skip ignored routes (/ping for example)
|
// Skip ignored routes (/ping for example)
|
||||||
|
@ -127,30 +131,25 @@ func (t *Telemetry) FiberMiddleware(config ...FiberMiddlewareConfig) fiber.Handl
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = t.propagator.Extract(ctx, propagation.HeaderCarrier(stdRequest.Header))
|
opts := []unitel.SpanStartOpt{
|
||||||
|
unitel.WithOtelOptions(trace.WithSpanKind(trace.SpanKindServer)),
|
||||||
hub := sentry.CurrentHub().Clone()
|
unitel.WithOtelTracer(tracer),
|
||||||
if client := hub.Client(); client != nil {
|
}
|
||||||
client.SetSDKIdentifier(fiberMwClientID)
|
shouldPropagate := cfg.TracePropagator(&stdRequest)
|
||||||
|
if shouldPropagate {
|
||||||
|
opts = append(opts, t.ContinueFromHeaders(stdRequest.Header))
|
||||||
}
|
}
|
||||||
|
|
||||||
scope := hub.Scope()
|
|
||||||
scope.SetRequest(&stdRequest)
|
|
||||||
scope.SetRequestBody(utils.CopyBytes(c.Body()))
|
|
||||||
ctx = sentry.SetHubOnContext(ctx, hub)
|
|
||||||
|
|
||||||
description := fmt.Sprintf("%s %s", c.Method(), c.Path())
|
description := fmt.Sprintf("%s %s", c.Method(), c.Path())
|
||||||
span := t.StartSpan(
|
span := t.StartSpan(
|
||||||
ctx,
|
ctx,
|
||||||
"http.server",
|
"http.server",
|
||||||
description,
|
description,
|
||||||
WithOtelOptions(trace.WithSpanKind(trace.SpanKindServer)),
|
opts...,
|
||||||
WithOtelTracer(tracer),
|
|
||||||
t.ContinueFromHeaders(stdRequest.Header),
|
|
||||||
)
|
)
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
log.Error().Stack().Msgf("panic()'ed: %v", err)
|
cfg.Logger.WithCallDepth(0).Info("panic()ed: ", "err", err)
|
||||||
|
|
||||||
timeout := (*time.Duration)(nil)
|
timeout := (*time.Duration)(nil)
|
||||||
if cfg.WaitForDelivery {
|
if cfg.WaitForDelivery {
|
||||||
|
@ -167,11 +166,17 @@ func (t *Telemetry) FiberMiddleware(config ...FiberMiddlewareConfig) fiber.Handl
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
h := propagation.HeaderCarrier{}
|
if !shouldPropagate {
|
||||||
t.propagator.Inject(ctx, h)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, k := range h.Keys() {
|
h := http.Header{}
|
||||||
c.Set(k, h.Get(k))
|
t.InjectIntoHeaders(ctx, h)
|
||||||
|
|
||||||
|
for k, v := range h {
|
||||||
|
for _, vv := range v {
|
||||||
|
c.Response().Header.Add(k, vv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -182,6 +187,17 @@ func (t *Telemetry) FiberMiddleware(config ...FiberMiddlewareConfig) fiber.Handl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx = span.Context()
|
ctx = span.Context()
|
||||||
|
|
||||||
|
hub := sentry.CurrentHub().Clone()
|
||||||
|
if client := hub.Client(); client != nil {
|
||||||
|
client.SetSDKIdentifier(fiberMwClientID)
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := hub.Scope()
|
||||||
|
scope.SetRequest(&stdRequest)
|
||||||
|
scope.SetRequestBody(utils.CopyBytes(c.Body()))
|
||||||
|
ctx = sentry.SetHubOnContext(ctx, hub)
|
||||||
|
|
||||||
c.SetUserContext(ctx)
|
c.SetUserContext(ctx)
|
||||||
|
|
||||||
var err error = nil
|
var err error = nil
|
||||||
|
@ -235,24 +251,24 @@ func (t *Telemetry) FiberMiddleware(config ...FiberMiddlewareConfig) fiber.Handl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpStatusToSpanStatus(code int, isServer bool) SpanStatus {
|
func httpStatusToSpanStatus(code int, isServer bool) unitel.SpanStatus {
|
||||||
sentryStatus := sentry.HTTPtoSpanStatus(code)
|
sentryStatus := sentry.HTTPtoSpanStatus(code)
|
||||||
|
|
||||||
if code < http.StatusBadRequest {
|
if code < http.StatusBadRequest {
|
||||||
return SpanStatus{codes.Ok, sentryStatus}
|
return unitel.SpanStatus{codes.Ok, sentryStatus}
|
||||||
}
|
}
|
||||||
|
|
||||||
if code < http.StatusInternalServerError {
|
if code < http.StatusInternalServerError {
|
||||||
// For HTTP status codes in the 4xx range span status MUST be left unset
|
// For HTTP status codes in the 4xx range span status MUST be left unset
|
||||||
// in case of SpanKind.SERVER and MUST be set to Error in case of SpanKind.CLIENT.
|
// in case of SpanKind.SERVER and MUST be set to Error in case of SpanKind.CLIENT.
|
||||||
if isServer {
|
if isServer {
|
||||||
return SpanStatus{codes.Unset, sentryStatus}
|
return unitel.SpanStatus{codes.Unset, sentryStatus}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SpanStatus{codes.Error, sentryStatus}
|
return unitel.SpanStatus{codes.Error, sentryStatus}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SpanStatus{codes.Error, sentryStatus}
|
return unitel.SpanStatus{codes.Error, sentryStatus}
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpFlavorAttribute(c *fiber.Ctx) attribute.KeyValue {
|
func httpFlavorAttribute(c *fiber.Ctx) attribute.KeyValue {
|
|
@ -1,19 +1,19 @@
|
||||||
package unitel
|
package unitelhttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/utils"
|
"github.com/gofiber/fiber/v2/utils"
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
const reqLoggerBufSize = 1000
|
const reqLoggerBufSize = 1000
|
||||||
|
|
||||||
func RequestLogger(l zerolog.Logger, requestHeaders, responseHeaders bool) fiber.Handler {
|
func RequestLogger(l logr.Logger, requestHeaders, responseHeaders bool) fiber.Handler {
|
||||||
type request struct {
|
type request struct {
|
||||||
Method string
|
Method string
|
||||||
Path string
|
Path string
|
||||||
|
@ -39,56 +39,54 @@ func RequestLogger(l zerolog.Logger, requestHeaders, responseHeaders bool) fiber
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case r := <-reqCh:
|
case r := <-reqCh:
|
||||||
l2 := l.Trace().
|
attrs := []any{
|
||||||
Str("method", r.Method).
|
"method", r.Method,
|
||||||
Str("path", r.Path).
|
"path", r.Path,
|
||||||
Time("start", r.Start)
|
"start", r.Start,
|
||||||
|
}
|
||||||
|
|
||||||
if r.OtelSpanID != nil {
|
if r.OtelSpanID != nil {
|
||||||
l2 = l2.Str("otel_span_id", r.OtelSpanID.String())
|
attrs = append(attrs, "otel_span_id", r.OtelSpanID.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.SentrySpanID != nil {
|
if r.SentrySpanID != nil {
|
||||||
l2 = l2.Str("sentry_span_id", r.SentrySpanID.String())
|
attrs = append(attrs, "sentry_span_id", r.SentrySpanID.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestHeaders {
|
if requestHeaders {
|
||||||
for k, v := range r.RequestHeaders {
|
for k, v := range r.RequestHeaders {
|
||||||
for i, vv := range v {
|
for i, vv := range v {
|
||||||
attr := formatHeaderAttribute("request", k, v, i)
|
attrs = append(attrs, formatHeaderAttribute("request", k, v, i), vv)
|
||||||
|
|
||||||
l2 = l2.Str(attr, vv)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
l2.Msg("request")
|
l.V(1).Info("request", attrs...)
|
||||||
|
|
||||||
case r := <-resCh:
|
case r := <-resCh:
|
||||||
l2 := l.Trace().
|
attrs := []any{
|
||||||
Int("status", r.Status).
|
"status", r.Status,
|
||||||
Time("end", r.End).
|
"end", r.End,
|
||||||
Dur("duration", r.Duration)
|
"duration", r.Duration,
|
||||||
|
}
|
||||||
|
|
||||||
if r.OtelSpanID != nil {
|
if r.OtelSpanID != nil {
|
||||||
l2 = l2.Str("otel_span_id", r.OtelSpanID.String())
|
attrs = append(attrs, "otel_span_id", r.OtelSpanID.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.SentrySpanID != nil {
|
if r.SentrySpanID != nil {
|
||||||
l2 = l2.Str("sentry_span_id", r.SentrySpanID.String())
|
attrs = append(attrs, "sentry_span_id", r.SentrySpanID.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if responseHeaders {
|
if responseHeaders {
|
||||||
for k, v := range r.ResponseHeaders {
|
for k, v := range r.ResponseHeaders {
|
||||||
for i, vv := range v {
|
for i, vv := range v {
|
||||||
attr := formatHeaderAttribute("response", k, v, i)
|
attrs = append(attrs, formatHeaderAttribute("response", k, v, i), vv)
|
||||||
|
|
||||||
l2 = l2.Str(attr, vv)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
l2.Msg("response")
|
l.V(1).Info("response", attrs...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
158
unitelhttp/transport.go
Normal file
158
unitelhttp/transport.go
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
package unitelhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.devminer.xyz/devminer/unitel"
|
||||||
|
"git.devminer.xyz/devminer/unitel/unitelutils"
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
const transportClientID = unitelutils.Base + "#HTTPTansport@" + unitelutils.Version
|
||||||
|
|
||||||
|
type HTTPTransportOpt func(t *HTTPTransport)
|
||||||
|
|
||||||
|
func WithLogger(l logr.Logger) HTTPTransportOpt {
|
||||||
|
return func(t *HTTPTransport) {
|
||||||
|
t.logger = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TracePropagator = func(r *http.Request) bool
|
||||||
|
|
||||||
|
func WithTracePropagation(propagator TracePropagator) HTTPTransportOpt {
|
||||||
|
return func(t *HTTPTransport) {
|
||||||
|
t.tracePropagator = propagator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PropagateAllTraces(req *http.Request) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func PropagateNoTraces(req *http.Request) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPTransport struct {
|
||||||
|
logger logr.Logger
|
||||||
|
telemetry *unitel.Telemetry
|
||||||
|
transport http.RoundTripper
|
||||||
|
|
||||||
|
tracePropagator TracePropagator
|
||||||
|
|
||||||
|
tracedRequestHeaders []string
|
||||||
|
tracedResponseHeaders []string
|
||||||
|
|
||||||
|
tracer trace.Tracer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTracedTransport(t *unitel.Telemetry, inner http.RoundTripper, tracedRequestHeaders []string, tracedResponseHeaders []string, opts ...HTTPTransportOpt) *HTTPTransport {
|
||||||
|
transport := &HTTPTransport{
|
||||||
|
logger: logr.Discard(),
|
||||||
|
telemetry: t,
|
||||||
|
transport: inner,
|
||||||
|
|
||||||
|
tracePropagator: PropagateNoTraces,
|
||||||
|
|
||||||
|
tracedRequestHeaders: tracedRequestHeaders,
|
||||||
|
tracedResponseHeaders: tracedResponseHeaders,
|
||||||
|
|
||||||
|
tracer: t.TracerProvider.Tracer(transportClientID, trace.WithInstrumentationVersion(unitelutils.Version)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(transport)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *HTTPTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
s := t.telemetry.
|
||||||
|
StartSpan(req.Context(), "http.client", fmt.Sprintf("%s %s", req.Method, req.URL), unitel.WithOtelOptions(trace.WithSpanKind(trace.SpanKindClient))).
|
||||||
|
AddAttributes(
|
||||||
|
semconv.HTTPRequestMethodKey.String(req.Method),
|
||||||
|
semconv.URLFull(req.URL.String()),
|
||||||
|
)
|
||||||
|
defer s.End()
|
||||||
|
ctx := s.Context()
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
|
if t.tracePropagator(req) {
|
||||||
|
t.telemetry.InjectIntoHeaders(ctx, req.Header)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, header := range t.tracedRequestHeaders {
|
||||||
|
vv := req.Header.Values(header)
|
||||||
|
for i, v := range vv {
|
||||||
|
s.AddAttributes(attribute.String(formatHeaderAttribute("http.request", header, vv, i), v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
resp, err := t.transport.RoundTrip(req)
|
||||||
|
elapsed := time.Since(start)
|
||||||
|
if err == nil {
|
||||||
|
t.logger.V(1).Info("fetch succeeded", "url", req.URL.String(), "status", resp.StatusCode, "duration", elapsed)
|
||||||
|
} else {
|
||||||
|
t.logger.V(1).Error(err, "fetch failed", "url", req.URL.String(), "duration", elapsed)
|
||||||
|
|
||||||
|
s.CaptureBreadcrumb(
|
||||||
|
unitel.SeverityError,
|
||||||
|
unitel.BreadcrumbTypeHTTP,
|
||||||
|
unitel.BreadcrumbCatagoryHTTP,
|
||||||
|
fmt.Sprintf("Failed to send request to %s: %v", req.URL.String(), err),
|
||||||
|
map[string]any{
|
||||||
|
"url": req.URL.String(),
|
||||||
|
"method": req.Method,
|
||||||
|
"duration": elapsed,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if hub := sentry.GetHubFromContext(ctx); hub != nil {
|
||||||
|
hub.CaptureException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
severity := unitel.SeverityDebug
|
||||||
|
if resp.StatusCode < http.StatusBadRequest {
|
||||||
|
severity = unitel.SeverityInfo
|
||||||
|
} else if resp.StatusCode < http.StatusInternalServerError {
|
||||||
|
severity = unitel.SeverityError
|
||||||
|
}
|
||||||
|
|
||||||
|
s.CaptureBreadcrumb(
|
||||||
|
severity,
|
||||||
|
unitel.BreadcrumbTypeHTTP,
|
||||||
|
unitel.BreadcrumbCatagoryHTTP,
|
||||||
|
req.URL.String(),
|
||||||
|
map[string]any{
|
||||||
|
"url": req.URL.String(),
|
||||||
|
"method": req.Method,
|
||||||
|
"status": resp.StatusCode,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.AddAttributes(semconv.HTTPResponseStatusCode(resp.StatusCode)).
|
||||||
|
SetStatus(httpStatusToSpanStatus(resp.StatusCode, false), "")
|
||||||
|
|
||||||
|
for _, header := range t.tracedResponseHeaders {
|
||||||
|
vv := resp.Header.Values(header)
|
||||||
|
for i, v := range vv {
|
||||||
|
s.AddAttributes(attribute.String(formatHeaderAttribute("http.response", header, vv, i), v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package unitel
|
package unitelsql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -7,6 +7,9 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.devminer.xyz/devminer/unitel"
|
||||||
|
"git.devminer.xyz/devminer/unitel/unitelutils"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
"github.com/qustavo/sqlhooks/v2"
|
"github.com/qustavo/sqlhooks/v2"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
@ -17,11 +20,11 @@ import (
|
||||||
|
|
||||||
// TODO: Port to github.com/loghole/dbhook for proper error handling
|
// TODO: Port to github.com/loghole/dbhook for proper error handling
|
||||||
|
|
||||||
const (
|
const sqlClientID = unitelutils.Base + "#TracedSQL@" + unitelutils.Version
|
||||||
sqlClientID = libBase + "#TracedSQL"
|
|
||||||
)
|
|
||||||
|
|
||||||
var dbCtxKey = struct{}{}
|
type dbCtxKeyT struct{}
|
||||||
|
|
||||||
|
var dbCtxKey = dbCtxKeyT{}
|
||||||
|
|
||||||
type dbCtxVal struct {
|
type dbCtxVal struct {
|
||||||
start time.Time
|
start time.Time
|
||||||
|
@ -29,13 +32,49 @@ type dbCtxVal struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type tracedSQLHooks struct {
|
type tracedSQLHooks struct {
|
||||||
|
telemetry *unitel.Telemetry
|
||||||
|
logger logr.Logger
|
||||||
|
|
||||||
dbType string
|
dbType string
|
||||||
|
|
||||||
t *Telemetry
|
|
||||||
printQueries bool
|
|
||||||
tracer trace.Tracer
|
|
||||||
|
|
||||||
mDuration metric.Float64Histogram
|
mDuration metric.Float64Histogram
|
||||||
|
tracer trace.Tracer
|
||||||
|
}
|
||||||
|
|
||||||
|
type TracedSQLOpt func(t *tracedSQLHooks)
|
||||||
|
|
||||||
|
func WithLogger(l logr.Logger) TracedSQLOpt {
|
||||||
|
return func(t *tracedSQLHooks) {
|
||||||
|
t.logger = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTracedSQL(t *unitel.Telemetry, driver driver.Driver, dbType string, opts ...TracedSQLOpt) driver.Driver {
|
||||||
|
meter := t.MeterProvider.Meter(sqlClientID, metric.WithInstrumentationVersion(unitelutils.Version))
|
||||||
|
mDuration, err := meter.Float64Histogram(
|
||||||
|
"db.client.operation.duration",
|
||||||
|
metric.WithDescription("Database query response times"),
|
||||||
|
metric.WithUnit("ms"),
|
||||||
|
metric.WithExplicitBucketBoundaries(0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("failed to create request duration histogram")
|
||||||
|
}
|
||||||
|
|
||||||
|
traced := &tracedSQLHooks{
|
||||||
|
dbType: dbType,
|
||||||
|
|
||||||
|
telemetry: t,
|
||||||
|
|
||||||
|
mDuration: mDuration,
|
||||||
|
tracer: t.TracerProvider.Tracer(sqlClientID, trace.WithInstrumentationVersion(unitelutils.Version)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range opts {
|
||||||
|
o(traced)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sqlhooks.Wrap(driver, traced)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *tracedSQLHooks) Before(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
|
func (h *tracedSQLHooks) Before(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
|
||||||
|
@ -61,18 +100,17 @@ func (h *tracedSQLHooks) Before(ctx context.Context, query string, args ...inter
|
||||||
attrs = append(attrs, attribute.String(fmt.Sprintf("db.query.parameter.%d", i), formatArg(arg)))
|
attrs = append(attrs, attribute.String(fmt.Sprintf("db.query.parameter.%d", i), formatArg(arg)))
|
||||||
}
|
}
|
||||||
|
|
||||||
s := h.t.StartSpan(ctx, "db.sql.query", cleanedQuery, WithOtelTracer(h.tracer))
|
s := h.telemetry.StartSpan(ctx, "db.sql.query", cleanedQuery, unitel.WithOtelTracer(h.tracer))
|
||||||
s.AddAttributes(attrs...)
|
s.AddAttributes(attrs...)
|
||||||
|
|
||||||
if h.printQueries {
|
opts := make([]any, 2*len(args))
|
||||||
l := log.Trace()
|
|
||||||
|
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
l = l.Interface(fmt.Sprintf("arg%d", i), arg)
|
opts = append(opts, fmt.Sprintf("arg%d", i), arg)
|
||||||
}
|
}
|
||||||
|
h.logger.Info(cleanedQuery, opts...)
|
||||||
|
|
||||||
l.Msg(cleanedQuery)
|
s.CaptureBreadcrumb(unitel.SeverityDebug, unitel.BreadcrumbTypeQuery, "started", query, map[string]any{"args": args}).
|
||||||
}
|
End()
|
||||||
|
|
||||||
return context.WithValue(s.Context(), dbCtxKey, dbCtxVal{
|
return context.WithValue(s.Context(), dbCtxKey, dbCtxVal{
|
||||||
start: time.Now(),
|
start: time.Now(),
|
||||||
|
@ -87,39 +125,14 @@ func (h *tracedSQLHooks) After(ctx context.Context, query string, args ...interf
|
||||||
h.mDuration.Record(ctx, float64(time.Since(val.start).Milliseconds()), metric.WithAttributes(attrs...))
|
h.mDuration.Record(ctx, float64(time.Since(val.start).Milliseconds()), metric.WithAttributes(attrs...))
|
||||||
}
|
}
|
||||||
|
|
||||||
if s := SpanFromContext(ctx); s != nil {
|
if s := unitel.SpanFromContext(ctx); s != nil {
|
||||||
s.CaptureBreadcrumb(SeverityDebug, BreadcrumbTypeQuery, "started", query, map[string]any{"args": args}).
|
s.CaptureBreadcrumb(unitel.SeverityDebug, unitel.BreadcrumbTypeQuery, "finished", query, map[string]any{"args": args}).
|
||||||
End()
|
End()
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx, nil
|
return ctx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Telemetry) TraceSQL(driver driver.Driver, dbType string, printQueries bool) driver.Driver {
|
|
||||||
tracer := t.tracerProvider.Tracer(sqlClientID, trace.WithInstrumentationVersion(libVersion))
|
|
||||||
meter := t.meterProvider.Meter(sqlClientID, metric.WithInstrumentationVersion(libVersion))
|
|
||||||
|
|
||||||
mDuration, err := meter.Float64Histogram(
|
|
||||||
"db.client.operation.duration",
|
|
||||||
metric.WithDescription("Database query response times"),
|
|
||||||
metric.WithUnit("ms"),
|
|
||||||
metric.WithExplicitBucketBoundaries(0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("failed to create request duration histogram")
|
|
||||||
}
|
|
||||||
|
|
||||||
return sqlhooks.Wrap(driver, &tracedSQLHooks{
|
|
||||||
dbType: dbType,
|
|
||||||
|
|
||||||
printQueries: printQueries,
|
|
||||||
t: t,
|
|
||||||
tracer: tracer,
|
|
||||||
|
|
||||||
mDuration: mDuration,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatArgs(args []interface{}) []string {
|
func formatArgs(args []interface{}) []string {
|
||||||
formattedArgs := make([]string, len(args))
|
formattedArgs := make([]string, len(args))
|
||||||
|
|
6
unitelutils/constants.go
Normal file
6
unitelutils/constants.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package unitelutils
|
||||||
|
|
||||||
|
const (
|
||||||
|
Base = "git.devminer.xyz/devminer/unitel"
|
||||||
|
Version = "0.0.1"
|
||||||
|
)
|
|
@ -1,6 +0,0 @@
|
||||||
package unitel
|
|
||||||
|
|
||||||
const (
|
|
||||||
libBase = "git.devminer.xyz/devminer/unitel"
|
|
||||||
libVersion = "0.0.1"
|
|
||||||
)
|
|
Loading…
Reference in a new issue