implemented tracing
Some checks failed
Build / build (push) Has been cancelled

This commit is contained in:
Gustavo Maronato 2023-09-21 16:19:17 -03:00
parent 371de006ad
commit 8ac3eef33a
Signed by: maronato
SSH Key Fingerprint: SHA256:2Gw7kwMz/As+2UkR1qQ/qYYhn+WNh3FGv6ozhoRrLcs
14 changed files with 408 additions and 39 deletions

View File

@ -16,6 +16,7 @@ import (
"git.maronato.dev/maronato/goshort/cmd/shared"
"git.maronato.dev/maronato/goshort/internal/config"
"git.maronato.dev/maronato/goshort/internal/util/logging"
"git.maronato.dev/maronato/goshort/internal/util/tracing"
"github.com/peterbourgon/ff/v3/ffcli"
)
@ -78,6 +79,15 @@ func Run(version string) error {
l := logging.NewLogger(cfg)
ctx = logging.WithLogger(ctx, l)
// Initialize tracing
ctx, stopTracing, err := tracing.InitTracer(ctx, cfg, version)
if err != nil {
return fmt.Errorf("%w", err)
}
// Close the tracer when the application exits
defer stopTracing()
// Run the command.
if err := rootCmd.Run(ctx); err != nil {
if errors.Is(err, flag.ErrHelp) {

39
go.mod
View File

@ -9,9 +9,15 @@ require (
github.com/go-chi/render v1.0.3
github.com/peterbourgon/ff/v3 v3.4.0
github.com/stretchr/testify v1.8.4
github.com/uptrace/bun v1.1.14
github.com/uptrace/bun/dialect/sqlitedialect v1.1.14
github.com/uptrace/bun/extra/bundebug v1.1.14
github.com/uptrace/bun v1.1.16
github.com/uptrace/bun/dialect/sqlitedialect v1.1.16
github.com/uptrace/bun/extra/bundebug v1.1.16
github.com/uptrace/bun/extra/bunotel v1.1.16
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0
go.opentelemetry.io/otel v1.18.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0
go.opentelemetry.io/otel/sdk v1.18.0
golang.org/x/crypto v0.12.0
golang.org/x/sync v0.3.0
modernc.org/sqlite v1.25.0
@ -19,25 +25,46 @@ require (
require (
github.com/ajg/form v1.5.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
go.opentelemetry.io/contrib/propagators/autoprop v0.44.0 // indirect
go.opentelemetry.io/contrib/propagators/aws v1.19.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.19.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.19.0 // indirect
go.opentelemetry.io/contrib/propagators/ot v1.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 // indirect
go.opentelemetry.io/otel/metric v1.18.0 // indirect
go.opentelemetry.io/otel/trace v1.18.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/tools v0.12.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/grpc v1.58.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect

95
go.sum
View File

@ -2,7 +2,8 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/alexedwards/scs/v2 v2.5.1 h1:EhAz3Kb3OSQzD8T+Ub23fKsiuvE0GzbF5Lgn0uTwM3Y=
github.com/alexedwards/scs/v2 v2.5.1/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/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=
@ -10,27 +11,39 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/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.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@ -42,42 +55,94 @@ github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc=
github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/uptrace/bun v1.1.14 h1:S5vvNnjEynJ0CvnrBOD7MIRW7q/WbtvFXrdfy0lddAM=
github.com/uptrace/bun v1.1.14/go.mod h1:RHk6DrIisO62dv10pUOJCz5MphXThuOTpVNYEYv7NI8=
github.com/uptrace/bun/dialect/sqlitedialect v1.1.14 h1:SlwXLxr+N1kEo8Q0cheRlnIZLZlWniEB1OI+jkiLgWE=
github.com/uptrace/bun/dialect/sqlitedialect v1.1.14/go.mod h1:9RTEj1l4bB9a4l1Mnc9y4COTwWlFYe1dh6fyxq1rR7A=
github.com/uptrace/bun/extra/bundebug v1.1.14 h1:9OCGfP9ZDlh41u6OLerWdhBtJAVGXHr0xtxO4xWi6t0=
github.com/uptrace/bun/extra/bundebug v1.1.14/go.mod h1:lto3guzS2v6mnQp1+akyE+ecBLOltevDDe324NXEYdw=
github.com/uptrace/bun v1.1.16 h1:cn9cgEMFwcyYRsQLfxCRMUxyK1WaHwOVrR3TvzEFZ/A=
github.com/uptrace/bun v1.1.16/go.mod h1:7HnsMRRvpLFUcquJxp22JO8PsWKpFQO/gNXqqsuGWg8=
github.com/uptrace/bun/dialect/sqlitedialect v1.1.16 h1:gbc9BP/e4sNOB9VBj+Si46dpOz2oktmZPidkda92GYY=
github.com/uptrace/bun/dialect/sqlitedialect v1.1.16/go.mod h1:YNezpK7fIn5Wa2WGmTCZ/nEyiswcXmuT4iNWADeL1x4=
github.com/uptrace/bun/extra/bundebug v1.1.16 h1:SgicRQGtnjhrIhlYOxdkOm1Em4s6HykmT3JblHnoTBM=
github.com/uptrace/bun/extra/bundebug v1.1.16/go.mod h1:SkiOkfUirBiO1Htc4s5bQKEq+JSeU1TkBVpMsPz2ePM=
github.com/uptrace/bun/extra/bunotel v1.1.16 h1:qkLTaTZK3FZk3b2P/stO/krS7KX9Fq5wSOj7Hlb2HG8=
github.com/uptrace/bun/extra/bunotel v1.1.16/go.mod h1:JwEH0kdXFnzYuK8D6eXUrf9HKsYy5wmB+lqQ/+dvH4E=
github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 h1:USRngIQppxeyb39XzkVHXwQesKK0+JSwnHE/1c7fgic=
github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2/go.mod h1:1frv9RN1rlTq0jzCq+mVuEQisubZCQ4OU6S/8CaHzGY=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 h1:KfYpVmrjI7JuToy5k8XV3nkapjWx48k4E4JOtVstzQI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0/go.mod h1:SeQhzAEccGVZVEy7aH87Nh0km+utSpo1pTv6eMMop48=
go.opentelemetry.io/contrib/propagators/autoprop v0.44.0 h1:HgXKc1D1PrpsYKdO8fc5XuyVmYFxcY2mLVAHq8XBZMU=
go.opentelemetry.io/contrib/propagators/autoprop v0.44.0/go.mod h1:B3RxUeb5nXP5iTp9NH1fGTrAbzCTXjvUIyS4pDxqwks=
go.opentelemetry.io/contrib/propagators/aws v1.19.0 h1:fXXcgurRq5CbEKxHg8Ge9pgTMSaCX9KcBnELHe9bHbc=
go.opentelemetry.io/contrib/propagators/aws v1.19.0/go.mod h1:W1bbfg19rs+luEUEYKSR65H2psL2YFutZmPWOdaswJg=
go.opentelemetry.io/contrib/propagators/b3 v1.19.0 h1:ulz44cpm6V5oAeg5Aw9HyqGFMS6XM7untlMEhD7YzzA=
go.opentelemetry.io/contrib/propagators/b3 v1.19.0/go.mod h1:OzCmE2IVS+asTI+odXQstRGVfXQ4bXv9nMBRK0nNyqQ=
go.opentelemetry.io/contrib/propagators/jaeger v1.19.0 h1:mGrx7XEAE+7ybCLM0T6iRl/jUTuHg6qKUJAtsAlknec=
go.opentelemetry.io/contrib/propagators/jaeger v1.19.0/go.mod h1:cHWVPhYWMZOanEf1qexqMIRhr4TKVjZWBKwZTL/tdR4=
go.opentelemetry.io/contrib/propagators/ot v1.19.0 h1:vODRLMlKN4ApM8ri0UDk8nnEeISuwxpf67sE7PmOHhE=
go.opentelemetry.io/contrib/propagators/ot v1.19.0/go.mod h1:S2Uc7th2ZmLiHu0lrCmDCgTQ/y5Nbbis+TNjR1jjm4Q=
go.opentelemetry.io/otel v1.18.0 h1:TgVozPGZ01nHyDZxK5WGPFB9QexeTMXEH7+tIClWfzs=
go.opentelemetry.io/otel v1.18.0/go.mod h1:9lWqYO0Db579XzVuCKFNPDl4s73Voa+zEck3wHaAYQI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 h1:yE32ay7mJG2leczfREEhoW3VfSZIvHaB+gvVo1o8DQ8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0/go.mod h1:G17FHPDLt74bCI7tJ4CMitEk4BXTYG4FW6XUpkPBXa4=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0 h1:hSWWvDjXHVLq9DkmB+77fl8v7+t+yYiS+eNkiplDK54=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0/go.mod h1:zG7KQql1WjZCaUJd+L/ReSYx4bjbYJxg5ws9ws+mYes=
go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ=
go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k=
go.opentelemetry.io/otel/sdk v1.18.0 h1:e3bAB0wB3MljH38sHzpV/qWrOTCFrdZF2ct9F8rBkcY=
go.opentelemetry.io/otel/sdk v1.18.0/go.mod h1:1RCygWV7plY2KmdskZEDDBs4tJeHG92MdHZIluiYs/M=
go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10=
go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
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.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o=
google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@ -17,6 +17,7 @@ import (
userservice "git.maronato.dev/maronato/goshort/internal/service/user"
"git.maronato.dev/maronato/goshort/internal/storage/models"
"git.maronato.dev/maronato/goshort/internal/util/logging"
"git.maronato.dev/maronato/goshort/internal/util/tracing"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
)
@ -43,19 +44,25 @@ func NewAPIHandler(
}
func (h *APIHandler) Me(w http.ResponseWriter, r *http.Request) {
_, span := tracing.StartSpan(r.Context(), "api.Me")
defer span.End()
// Get user from context
user, ok := h.findUserOrRespond(w, r)
if !ok {
return
}
span.AddEvent("found user")
// Respond with the user
render.Status(r, http.StatusOK)
render.JSON(w, r, user)
}
func (h *APIHandler) DeleteMe(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.DeleteMe")
defer span.End()
// Get user from context
user, ok := h.findUserOrRespond(w, r)
@ -63,12 +70,16 @@ func (h *APIHandler) DeleteMe(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("found user")
// Delete all user's sessions
err := authmiddleware.DeleteAllUserSessions(ctx, user)
if err != nil {
server.RenderServerError(w, r, err)
}
span.AddEvent("deleted all user sessions")
// Delete the user
err = h.users.DeleteUser(ctx, user)
if err != nil {
@ -77,13 +88,19 @@ func (h *APIHandler) DeleteMe(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("deleted user")
// Logout and return
authmiddleware.LogoutUser(ctx)
span.AddEvent("logged out user")
render.NoContent(w, r)
}
func (h *APIHandler) Login(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.Login")
defer span.End()
l := logging.FromCtx(ctx)
type loginForm struct {
@ -119,8 +136,11 @@ func (h *APIHandler) Login(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("authenticated user")
// Login user
authmiddleware.LoginUser(ctx, user, r)
span.AddEvent("logged in user")
// Render the response
render.Status(r, http.StatusOK)
@ -128,10 +148,12 @@ func (h *APIHandler) Login(w http.ResponseWriter, r *http.Request) {
}
func (h *APIHandler) Logout(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.Logout")
defer span.End()
// Logout user
authmiddleware.LogoutUser(ctx)
span.AddEvent("logged out user")
// Render the response
render.Status(r, http.StatusOK)
@ -139,7 +161,9 @@ func (h *APIHandler) Logout(w http.ResponseWriter, r *http.Request) {
}
func (h *APIHandler) Signup(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.Signup")
defer span.End()
l := logging.FromCtx(ctx)
// Get the user from the json body
@ -156,6 +180,8 @@ func (h *APIHandler) Signup(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("parsed form")
// Get user and pass from form
user := &form.User
pass := form.Password
@ -171,6 +197,8 @@ func (h *APIHandler) Signup(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("hashed password")
// Create user
newUser, err := h.users.CreateUser(ctx, user)
if err != nil {
@ -188,13 +216,16 @@ func (h *APIHandler) Signup(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("created user")
// Render the response
render.Status(r, http.StatusCreated)
render.JSON(w, r, newUser)
}
func (h *APIHandler) CreateShort(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.CreateShort")
defer span.End()
// Get the URL from the json body
var short *models.Short
@ -205,12 +236,16 @@ func (h *APIHandler) CreateShort(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("parsed form")
// Get user from context
user, ok := h.findUserOrRespond(w, r)
if !ok {
return
}
span.AddEvent("found user")
// Set the user
short.UserID = &user.ID
@ -226,15 +261,20 @@ func (h *APIHandler) CreateShort(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("created short")
shortResponse := newShortResponse(r, newShort)
span.AddEvent("created shorted response")
// Render the response
render.Status(r, http.StatusCreated)
render.JSON(w, r, shortResponse)
}
func (h *APIHandler) ListShorts(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.ListShorts")
defer span.End()
// Get user from context
user, ok := h.findUserOrRespond(w, r)
@ -242,6 +282,8 @@ func (h *APIHandler) ListShorts(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("found user")
// Get shorts
shorts, err := h.shorts.ListShorts(ctx, user)
if err != nil {
@ -250,29 +292,41 @@ func (h *APIHandler) ListShorts(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("listed shorts")
shortsResponse := newShortResponseList(r, shorts)
span.AddEvent("created shorts response")
// Render the response
render.Status(r, http.StatusOK)
render.JSON(w, r, shortsResponse)
}
func (h *APIHandler) FindShort(w http.ResponseWriter, r *http.Request) {
_, span := tracing.StartSpan(r.Context(), "api.FindShort")
defer span.End()
// Find own short or respond
short, ok := h.findShortOrRespond(w, r)
if !ok {
return
}
span.AddEvent("found short")
shortResponse := newShortResponse(r, short)
span.AddEvent("created short response")
// Render the short
render.Status(r, http.StatusOK)
render.JSON(w, r, shortResponse)
}
func (h *APIHandler) DeleteShort(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.DeleteShort")
defer span.End()
// Find own short or respond
short, ok := h.findShortOrRespond(w, r)
@ -280,6 +334,8 @@ func (h *APIHandler) DeleteShort(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("found short")
// Delete short
err := h.shorts.DeleteShort(ctx, short)
if err != nil {
@ -288,12 +344,15 @@ func (h *APIHandler) DeleteShort(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("deleted short")
// Deleted, return no content
render.NoContent(w, r)
}
func (h *APIHandler) ListShortLogs(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.ListShortLogs")
defer span.End()
// Find own short or respond
short, ok := h.findShortOrRespond(w, r)
@ -301,6 +360,8 @@ func (h *APIHandler) ListShortLogs(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("found short")
// Get logs
logs, err := h.shortLogs.ListLogs(ctx, short)
if err != nil {
@ -309,13 +370,15 @@ func (h *APIHandler) ListShortLogs(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("listed logs")
// Render the response
render.Status(r, http.StatusOK)
render.JSON(w, r, logs)
}
func (h *APIHandler) ListSessions(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.ListSessions")
// Get user from context
user, ok := h.findUserOrRespond(w, r)
@ -323,18 +386,24 @@ func (h *APIHandler) ListSessions(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("found user")
sessions, err := authmiddleware.ListUserSessions(ctx, user)
if err != nil {
server.RenderServerError(w, r, err)
}
span.AddEvent("listed sessions")
// Render the response
render.Status(r, http.StatusOK)
render.JSON(w, r, sessions)
}
func (h *APIHandler) DeleteSession(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.DeleteSession")
defer span.End()
l := logging.FromCtx(ctx)
// Get user from context
@ -343,6 +412,8 @@ func (h *APIHandler) DeleteSession(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("found user")
// Get session token from request
sessionToken := chi.URLParam(r, "id")
@ -360,13 +431,16 @@ func (h *APIHandler) DeleteSession(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("deleted session")
// Render the response
render.NoContent(w, r)
}
// ListTokens lists all tokens belonging to the user.
func (h *APIHandler) ListTokens(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.ListTokens")
defer span.End()
// Get user from context
user, ok := h.findUserOrRespond(w, r)
@ -374,12 +448,16 @@ func (h *APIHandler) ListTokens(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("found user")
// Get tokens
tokens, err := h.tokens.ListTokens(ctx, user)
if err != nil {
server.RenderServerError(w, r, err)
}
span.AddEvent("listed tokens")
// Render the response
render.Status(r, http.StatusOK)
render.JSON(w, r, tokens)
@ -387,7 +465,8 @@ func (h *APIHandler) ListTokens(w http.ResponseWriter, r *http.Request) {
// CreateToken creates a new token for the user.
func (h *APIHandler) CreateToken(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.CreateToken")
defer span.End()
type tokenNameForm struct {
Name string `json:"name"`
@ -402,12 +481,16 @@ func (h *APIHandler) CreateToken(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("parsed form")
// Get user from context
user, ok := h.findUserOrRespond(w, r)
if !ok {
return
}
span.AddEvent("found user")
token, err := h.tokens.CreateToken(ctx, user, form.Name)
if err != nil {
server.RenderServerError(w, r, err)
@ -415,6 +498,8 @@ func (h *APIHandler) CreateToken(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("created token")
// Render the response
render.Status(r, http.StatusCreated)
render.JSON(w, r, token)
@ -422,7 +507,8 @@ func (h *APIHandler) CreateToken(w http.ResponseWriter, r *http.Request) {
// ChangeTokenName changes a token's name belonging to the user.
func (h *APIHandler) ChangeTokenName(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.ChangeTokenName")
defer span.End()
type tokenNameForm struct {
Name string `json:"name"`
@ -435,12 +521,16 @@ func (h *APIHandler) ChangeTokenName(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("parsed form")
// Find token or respond
token, ok := h.findTokenOrRespond(w, r)
if !ok {
return
}
span.AddEvent("found token")
// Rename token
newToken, err := h.tokens.ChangeTokenName(ctx, token, form.Name)
if err != nil {
@ -449,6 +539,8 @@ func (h *APIHandler) ChangeTokenName(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("changed token name")
// Changed. Return the token
render.Status(r, http.StatusOK)
render.JSON(w, r, newToken)
@ -456,7 +548,8 @@ func (h *APIHandler) ChangeTokenName(w http.ResponseWriter, r *http.Request) {
// DeleteToken deletes a token belonging to the user.
func (h *APIHandler) DeleteToken(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "api.DeleteToken")
defer span.End()
// Find token or respond
token, ok := h.findTokenOrRespond(w, r)
@ -464,6 +557,8 @@ func (h *APIHandler) DeleteToken(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("found token")
// Delete token
err := h.tokens.DeleteToken(ctx, token)
if err != nil {
@ -472,6 +567,8 @@ func (h *APIHandler) DeleteToken(w http.ResponseWriter, r *http.Request) {
return
}
span.AddEvent("deleted token")
// Deleted, return no content
render.NoContent(w, r)
}

View File

@ -5,6 +5,7 @@ import (
"git.maronato.dev/maronato/goshort/internal/storage"
"git.maronato.dev/maronato/goshort/internal/util/logging"
"git.maronato.dev/maronato/goshort/internal/util/tracing"
"github.com/go-chi/render"
)
@ -27,7 +28,9 @@ func NewHealthcheckHandler(strg storage.Storage) *HealthcheckHandler {
}
func (h *HealthcheckHandler) CheckHealth(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "healthcheck.CheckHealth")
defer span.End()
l := logging.FromCtx(ctx)
// Defaults
@ -41,6 +44,8 @@ func (h *HealthcheckHandler) CheckHealth(w http.ResponseWriter, r *http.Request)
databaseOk = false
}
span.AddEvent("database check done")
// All checks must pass
overallStatus := HealthCheckStatusOk
statusCode := http.StatusOK

View File

@ -7,6 +7,8 @@ import (
tokenservice "git.maronato.dev/maronato/goshort/internal/service/token"
userservice "git.maronato.dev/maronato/goshort/internal/service/user"
"git.maronato.dev/maronato/goshort/internal/storage/models"
"git.maronato.dev/maronato/goshort/internal/util/tracing"
"go.opentelemetry.io/otel/attribute"
)
type userCtxKey struct{}
@ -15,15 +17,23 @@ func Auth(userService *userservice.UserService, tokenService *tokenservice.Token
) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "authmiddleware.Auth")
defer span.End()
// Set default span attributes
span.SetAttributes(attribute.String("auth_method", "none"))
// Authenticate user
authMethod := TokenAuth
user, err := authenticateViaToken(r, tokenService)
span.AddEvent("queried token auth")
if err != nil {
// Failed to authenticate via token. Try to authenticate via session.
authMethod = SessionAuth
user, err = authenticateUserViaSession(r, userService)
span.AddEvent("queried session auth")
if err != nil {
// Failed to authenticate via session. Call the next handler.
next.ServeHTTP(w, r)
@ -37,6 +47,10 @@ func Auth(userService *userservice.UserService, tokenService *tokenservice.Token
// Register the auth method used
ctx = context.WithValue(ctx, authMethodCtxKey{}, authMethod)
// Set span attributes
span.SetAttributes(attribute.String("auth_method", string(authMethod)))
span.SetAttributes(attribute.String("user_id", user.ID))
// Call the next handler
next.ServeHTTP(w, r.WithContext(ctx))
})

View File

@ -8,6 +8,7 @@ import (
"git.maronato.dev/maronato/goshort/internal/config"
"git.maronato.dev/maronato/goshort/internal/util/logging"
"github.com/go-chi/chi/v5/middleware"
"go.opentelemetry.io/otel/trace"
)
type RequestLogFormatter struct {
@ -41,6 +42,8 @@ func (rl *RequestLogFormatter) NewLogEntry(r *http.Request) middleware.LogEntry
var requestGroup slog.Attr
traceID := trace.SpanContextFromContext(ctx).TraceID().String()
if rl.verbose >= config.VerboseLevelAccessLogs {
scheme := "http"
if r.TLS != nil {
@ -48,6 +51,7 @@ func (rl *RequestLogFormatter) NewLogEntry(r *http.Request) middleware.LogEntry
}
requestGroup = slog.Group("request",
slog.String("trace_id", traceID),
slog.String("id", reqID),
slog.String("method", r.Method),
slog.String("path", r.URL.Path),
@ -61,6 +65,7 @@ func (rl *RequestLogFormatter) NewLogEntry(r *http.Request) middleware.LogEntry
)
} else {
requestGroup = slog.Group("request",
slog.String("trace_id", traceID),
slog.String("id", reqID),
slog.String("method", r.Method),
slog.String("path", r.URL.Path),

View File

@ -5,6 +5,7 @@ import (
"net/http"
"git.maronato.dev/maronato/goshort/internal/config"
"git.maronato.dev/maronato/goshort/internal/util/tracing"
"github.com/alexedwards/scs/v2"
)
@ -29,7 +30,9 @@ func SessionManager(cfg *config.Config) func(http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Add the session manager to the context.
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "sessionmiddleware.SessionManager")
defer span.End()
ctx = context.WithValue(ctx, sessionContextKey{}, sessionManager)
// Call the next handler.

View File

@ -0,0 +1,33 @@
package tracingmiddleware
import (
"fmt"
"net/http"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
"go.opentelemetry.io/otel/trace"
)
func Tracer() func(http.Handler) http.Handler {
middleware := otelhttp.NewMiddleware("serve",
otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string {
return fmt.Sprintf("%s %s", r.Method, r.URL.Path)
}),
)
return func(next http.Handler) http.Handler {
return middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Set some extra attributes to the span
span := trace.SpanFromContext(r.Context())
span.SetAttributes(
semconv.HTTPRoute(r.URL.Path),
semconv.HTTPRequestContentLength(int(r.ContentLength)),
semconv.HTTPURL(r.URL.String()),
semconv.HTTPClientIP(r.RemoteAddr),
)
next.ServeHTTP(w, r)
}))
}
}

View File

@ -10,6 +10,7 @@ import (
"git.maronato.dev/maronato/goshort/internal/config"
servermiddleware "git.maronato.dev/maronato/goshort/internal/server/middleware"
sessionmiddleware "git.maronato.dev/maronato/goshort/internal/server/middleware/session"
tracingmiddleware "git.maronato.dev/maronato/goshort/internal/server/middleware/tracing"
"git.maronato.dev/maronato/goshort/internal/util/logging"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
@ -35,9 +36,14 @@ func NewServer(cfg *config.Config) *Server {
mux.Use(middleware.RealIP)
mux.Use(middleware.StripSlashes)
// Register tracing middleware
mux.Use(tracingmiddleware.Tracer())
// Register logging middleware
requestLogger := servermiddleware.NewLogFormatter(cfg)
mux.Use(middleware.RequestLogger(requestLogger))
// Register secondary middlewares
mux.Use(middleware.Recoverer)
mux.Use(sessionmiddleware.SessionManager(cfg))
mux.Use(middleware.Timeout(config.RequestTimeout))

View File

@ -8,6 +8,7 @@ import (
"git.maronato.dev/maronato/goshort/internal/server"
shortservice "git.maronato.dev/maronato/goshort/internal/service/short"
shortlogservice "git.maronato.dev/maronato/goshort/internal/service/shortlog"
"git.maronato.dev/maronato/goshort/internal/util/tracing"
"github.com/go-chi/chi/v5"
)
@ -24,7 +25,8 @@ func NewShortHandler(shorts *shortservice.ShortService, shortLogs *shortlogservi
}
func (h *ShortHandler) FindShort(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracing.StartSpan(r.Context(), "short.FindShort")
defer span.End()
// Get the short URL from the request
name := chi.URLParam(r, "short")
@ -34,8 +36,10 @@ func (h *ShortHandler) FindShort(w http.ResponseWriter, r *http.Request) {
switch {
case err == nil:
span.AddEvent("short found")
// If there's no error, log the access and redirect to the URL
h.shortLogs.LogShortAccess(ctx, short, r)
span.AddEvent("submitted access log")
http.Redirect(w, r, short.URL, http.StatusSeeOther)
case errors.Is(err, errs.ErrInvalidShort):
@ -43,6 +47,7 @@ func (h *ShortHandler) FindShort(w http.ResponseWriter, r *http.Request) {
// If the short name is invalid, do nothing and let the static handler
// take care of it.
case errors.Is(err, errs.ErrShortDoesNotExist):
span.AddEvent("short not found")
// If the short doesn't exist, do nothing and let the static handler
// take care of it.

View File

@ -7,6 +7,7 @@ import (
"os"
"git.maronato.dev/maronato/goshort/internal/config"
"git.maronato.dev/maronato/goshort/internal/util/tracing"
)
type StaticHandler struct {
@ -29,6 +30,9 @@ func NewStaticHandler(_ *config.Config, prefix string, assets fs.FS) *StaticHand
}
func (h *StaticHandler) ServeFiles(w http.ResponseWriter, r *http.Request) {
_, span := tracing.StartSpan(r.Context(), "static.ServeFiles")
defer span.End()
// Serve the files
h.assetServer.ServeHTTP(w, r)
}

View File

@ -12,6 +12,7 @@ import (
"git.maronato.dev/maronato/goshort/internal/storage/models"
"github.com/uptrace/bun"
"github.com/uptrace/bun/extra/bundebug"
"github.com/uptrace/bun/extra/bunotel"
)
type BunStorage struct {
@ -29,6 +30,11 @@ func NewBunStorage(cfg *config.Config, db *bun.DB) *BunStorage {
db.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true)))
}
// Enable tracing.
db.AddQueryHook(bunotel.NewQueryHook(
bunotel.WithDBName("goshort")),
)
return &BunStorage{
db: db,
started: false,

View File

@ -0,0 +1,89 @@
package tracing
import (
"context"
"git.maronato.dev/maronato/goshort/internal/config"
"git.maronato.dev/maronato/goshort/internal/errs"
"git.maronato.dev/maronato/goshort/internal/util/logging"
"go.opentelemetry.io/contrib/propagators/autoprop"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
"go.opentelemetry.io/otel/trace"
)
const tracerName = "goshort"
type tracerKey struct{}
func InitTracer(ctx context.Context, cfg *config.Config, version string) (context.Context, func(), error) {
var ex sdktrace.SpanExporter
shutdown := func() {}
ex, err := otlptracegrpc.New(ctx)
if err != nil {
return ctx, shutdown, errs.Errorf("failed to initialize otlptracegrpc exporter %w", err)
}
// Create a new resource with service name
env := "development"
if cfg.Prod {
env = "production"
}
r := resource.NewSchemaless(
semconv.ServiceNameKey.String("goshort"),
semconv.ServiceVersion(version),
semconv.DeploymentEnvironment(env),
semconv.ServiceName("goshort"),
)
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(ex),
sdktrace.WithResource(r),
)
// Set the global trace provider
otel.SetTracerProvider(tp)
tracer := tp.Tracer(tracerName)
// Set context tracer
ctx = context.WithValue(ctx, tracerKey{}, tracer)
// Set the global propagator
p := autoprop.NewTextMapPropagator()
otel.SetTextMapPropagator(p)
// Define shutdown function
shutdown = func() {
l := logging.FromCtx(ctx)
if err := tp.ForceFlush(ctx); err != nil {
l.Error("Failed to flush tracer provider", err)
return
}
if err := tp.Shutdown(context.WithoutCancel(ctx)); err != nil {
l.Error("Failed to shutdown tracer provider", err)
return
}
}
return ctx, shutdown, nil
}
func StartSpan(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { //nolint:ireturn // Span is only available as an interface
tracer, ok := ctx.Value(tracerKey{}).(trace.Tracer)
if !ok {
tracer = trace.SpanFromContext(ctx).TracerProvider().Tracer("goshort")
}
return tracer.Start(ctx, name, opts...)
}