1
0
mirror of https://github.com/owenthereal/jqplay.git synced 2025-04-19 06:02:17 +03:00

Logs error to sentry (#176)

Logs to sentry
This commit is contained in:
Owen Ou 2024-01-10 22:43:41 -08:00 committed by GitHub
parent 30a195faff
commit 08458ba2a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 120 additions and 46 deletions

View File

@ -2,36 +2,40 @@ package main
import (
"context"
"log"
"os"
"github.com/owenthereal/jqplay/config"
"github.com/owenthereal/jqplay/jq"
"github.com/owenthereal/jqplay/server"
log "github.com/sirupsen/logrus"
)
func main() {
err := jq.Init()
if err != nil {
log.Fatal(err)
}
log.WithFields(log.Fields{
"version": jq.Version,
"path": jq.Path,
}).Info("initialized jq")
conf, err := config.Load()
if err != nil {
log.Fatal(err)
log.Fatalf("error loading config: %s", err.Error())
}
log.WithFields(log.Fields{
"host": conf.Host,
"port": conf.Port,
}).Infof("Starting server at %s:%s", conf.Host, conf.Port)
logger := conf.Logger
if err := jq.Init(); err != nil {
logger.Error("error initializing jq", "error", err)
os.Exit(1)
}
logger.Info(
"initialized jq",
"version", jq.Version,
"path", jq.Path,
)
logger.Info(
"starting server",
"host", conf.Host,
"port", conf.Port,
)
srv := server.New(conf)
if err := srv.Start(context.Background()); err != nil {
log.WithError(err).Fatal("error starting sever")
logger.Error("error starting server", "error", err)
os.Exit(1)
}
}

View File

@ -1,8 +1,12 @@
package config
import (
"log/slog"
"github.com/getsentry/sentry-go"
"github.com/joeshaw/envdecode"
"github.com/owenthereal/jqplay/jq"
"github.com/owenthereal/jqplay/log"
)
type Config struct {
@ -11,7 +15,10 @@ type Config struct {
GinMode string `env:"GIN_MODE,default=debug"`
DatabaseURL string `env:"DATABASE_URL,required"`
AssetHost string `env:"ASSET_HOST"`
SENTRY_DSN string `env:"SENTRY_DSN"`
JQVer string
Logger *slog.Logger
}
func (c *Config) IsProd() bool {
@ -27,5 +34,20 @@ func Load() (*Config, error) {
conf.JQVer = jq.Version
conf.Logger = log.NewJSONLogger()
if dsn := conf.SENTRY_DSN; dsn != "" {
if err := sentry.Init(sentry.ClientOptions{
Dsn: dsn,
EnableTracing: true,
TracesSampleRate: 0.2,
AttachStacktrace: true,
Environment: conf.GinMode,
}); err != nil {
return nil, err
}
conf.Logger = log.WrapWithSentryLogger(conf.Logger)
}
return conf, nil
}

8
go.mod
View File

@ -3,13 +3,15 @@ module github.com/owenthereal/jqplay
go 1.21
require (
github.com/getsentry/sentry-go v0.26.0
github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.5.0
github.com/jackc/pgx/v4 v4.18.1
github.com/jmoiron/sqlx v1.3.5
github.com/joeshaw/envdecode v0.0.0-20200121155833-099f1fc765bd
github.com/oklog/run v1.1.1-0.20200508094559-c7096881717e
github.com/sirupsen/logrus v1.9.3
github.com/samber/slog-multi v1.0.2
github.com/samber/slog-sentry/v2 v2.4.0
github.com/stretchr/testify v1.8.3
github.com/unrolled/secure v1.14.0
)
@ -39,12 +41,14 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/samber/slog-common v0.15.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect

23
go.sum
View File

@ -17,10 +17,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/getsentry/sentry-go v0.26.0 h1:IX3++sF6/4B5JcevhdZfdKIHfyvMmAq/UnqcyT2H6mA=
github.com/getsentry/sentry-go v0.26.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@ -39,8 +43,9 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
@ -137,6 +142,8 @@ github.com/oklog/run v1.1.1-0.20200508094559-c7096881717e h1:bxQ+jj+8fdl9112bovU
github.com/oklog/run v1.1.1-0.20200508094559-c7096881717e/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -146,14 +153,20 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/samber/slog-common v0.15.0 h1:pPUhFBPsKwjdzmUyOj1F5Ow5bw3w6hkole5CqJDNZoY=
github.com/samber/slog-common v0.15.0/go.mod h1:Qjrfhwk79XiCIhBj8+jTq1Cr0u9rlWbjawh3dWXzaHk=
github.com/samber/slog-multi v1.0.2 h1:6BVH9uHGAsiGkbbtQgAOQJMpKgV8unMrHhhJaw+X1EQ=
github.com/samber/slog-multi v1.0.2/go.mod h1:uLAvHpGqbYgX4FSL0p1ZwoLuveIAJvBECtE07XmYvFo=
github.com/samber/slog-sentry/v2 v2.4.0 h1:AUeY9GYO1mA8AsMsay4a8h4zNlOtNC/g4uhF1HX4+SE=
github.com/samber/slog-sentry/v2 v2.4.0/go.mod h1:dXRY6Ju31Mwqmu/smT5cW4PEdSZyTVvInPA0/fc3aDU=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@ -205,6 +218,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
@ -234,7 +249,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -268,7 +282,6 @@ golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=

22
log/log.go Normal file
View File

@ -0,0 +1,22 @@
package log
import (
"log/slog"
"os"
slogmulti "github.com/samber/slog-multi"
slogsentry "github.com/samber/slog-sentry/v2"
)
func NewJSONLogger() *slog.Logger {
return slog.New(slog.NewJSONHandler(os.Stderr, nil))
}
func WrapWithSentryLogger(logger *slog.Logger) *slog.Logger {
return slog.New(
slogmulti.Fanout(
logger.Handler(),
slogsentry.Option{Level: slog.LevelError}.NewSentryHandler(),
),
)
}

View File

@ -3,12 +3,12 @@ package server
import (
"encoding/json"
"fmt"
"log/slog"
"net/http"
"github.com/gin-gonic/gin"
"github.com/owenthereal/jqplay/config"
"github.com/owenthereal/jqplay/jq"
"github.com/sirupsen/logrus"
)
type JQHandlerContext struct {
@ -38,7 +38,7 @@ func (h *JQHandler) handleJqPost(c *gin.Context) {
var jq jq.JQ
if err := c.BindJSON(&jq); err != nil {
err = fmt.Errorf("error parsing JSON: %s", err)
h.logger(c).WithError(err).Info("error parsing JSON")
h.logger(c).Error("error parsing JSON", "error", err)
c.String(http.StatusUnprocessableEntity, err.Error())
return
}
@ -49,7 +49,7 @@ func (h *JQHandler) handleJqPost(c *gin.Context) {
// appending error message in the end if there's any
if err := h.JQExec.Eval(c.Request.Context(), jq, c.Writer); err != nil {
fmt.Fprint(c.Writer, err.Error())
h.logger(c).WithError(err).Info("jq error")
h.logger(c).Error("jq error", "error", err)
}
}
@ -74,7 +74,7 @@ func (h *JQHandler) handleJqSharePost(c *gin.Context) {
var jq *jq.JQ
if err := c.BindJSON(&jq); err != nil {
err = fmt.Errorf("error parsing JSON: %s", err)
h.logger(c).WithError(err).Info("error parsing JSON")
h.logger(c).Error("error parsing JSON", "error", err)
c.String(http.StatusUnprocessableEntity, err.Error())
return
}
@ -86,7 +86,7 @@ func (h *JQHandler) handleJqSharePost(c *gin.Context) {
id, err := h.DB.UpsertSnippet(FromJQ(jq))
if err != nil {
h.logger(c).WithError(err).Info("error upserting snippet")
h.logger(c).Error("error upserting snippet", "error", err)
c.String(http.StatusUnprocessableEntity, "error sharing snippet")
return
}
@ -99,7 +99,7 @@ func (h *JQHandler) handleJqShareGet(c *gin.Context) {
s, err := h.DB.GetSnippet(id)
if err != nil {
h.logger(c).WithError(err).WithField("id", id).Info("error getting snippet")
h.logger(c).Error("error getting snippet", "error", err, "id", id)
c.Redirect(http.StatusFound, "/")
return
}
@ -116,7 +116,7 @@ func (h *JQHandler) handleJqShareGet(c *gin.Context) {
})
}
func (h *JQHandler) logger(c *gin.Context) *logrus.Entry {
func (h *JQHandler) logger(c *gin.Context) *slog.Logger {
l, _ := c.Get("logger")
return l.(*logrus.Entry)
return l.(*slog.Logger)
}

View File

@ -1,16 +1,19 @@
package middleware
import (
"fmt"
"time"
"log/slog"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func Logger() gin.HandlerFunc {
func Logger(logger *slog.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
requestID, _ := c.Get("request_id")
logger := logrus.WithField("request_id", requestID)
logger := logger.With("request_id", requestID)
c.Set("logger", logger)
start := time.Now()
@ -20,13 +23,14 @@ func Logger() gin.HandlerFunc {
method := c.Request.Method
path := c.Request.URL.Path
latency := end.Sub(start)
logger.WithFields(logrus.Fields{
"method": method,
"path": path,
"status": c.Writer.Status(),
"client_ip": c.ClientIP(),
"latency": latency,
"bytes": c.Writer.Size(),
}).Infof("%s %s", method, path)
logger.Info(
fmt.Sprintf("%s %s", method, path),
"method", method,
"path", path,
"status", c.Writer.Status(),
"client_ip", c.ClientIP(),
"latency", latency,
"bytes", c.Writer.Size(),
)
}
}

View File

@ -3,17 +3,19 @@ package server
import (
"context"
"html/template"
"log/slog"
"net/http"
"os"
"syscall"
"time"
sentrygin "github.com/getsentry/sentry-go/gin"
"github.com/gin-gonic/gin"
"github.com/oklog/run"
"github.com/owenthereal/jqplay"
"github.com/owenthereal/jqplay/config"
"github.com/owenthereal/jqplay/jq"
"github.com/owenthereal/jqplay/server/middleware"
log "github.com/sirupsen/logrus"
)
const (
@ -49,7 +51,7 @@ func (s *Server) Start(ctx context.Context) error {
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.WithError(err).Error("error shutting down server")
s.Config.Logger.Error("error shutting down server", "error", err)
}
})
@ -71,11 +73,14 @@ func newHTTPServer(cfg *config.Config, db *DB) (*http.Server, error) {
router := gin.New()
router.Use(
sentrygin.New(sentrygin.Options{
Repanic: true,
}),
middleware.Timeout(requestTimeout),
middleware.LimitContentLength(10),
middleware.Secure(cfg.IsProd()),
middleware.RequestID(),
middleware.Logger(),
middleware.Logger(slog.New(slog.NewJSONHandler(os.Stderr, nil))),
gin.Recovery(),
)
router.SetHTMLTemplate(tmpl)