From ddb397e1aefa9ddc093d01ffc438e8d7a428f79c Mon Sep 17 00:00:00 2001 From: Alexander Matveev Date: Wed, 14 Jun 2023 20:24:20 +0300 Subject: [PATCH] init --- .gitignore | 2 + config.go | 34 +++++++++++++++ go.mod | 19 +++++++++ go.sum | 32 ++++++++++++++ main.go | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 210 insertions(+) create mode 100644 .gitignore create mode 100644 config.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..38cb90f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.idea +/example/ diff --git a/config.go b/config.go new file mode 100644 index 0000000..c9eed9d --- /dev/null +++ b/config.go @@ -0,0 +1,34 @@ +package main + +import "flag" + +type NginxConfig struct { + LogFile string + Config string + ConfigFile string + TimeFormat string +} + +type SentryConfig struct { + Debug bool + Dsn string + Env string + Message string + ServerName string +} + +var nginxConfig NginxConfig +var sentryConfig SentryConfig + +func init() { + flag.StringVar(&nginxConfig.LogFile, "file", "/var/log/nginx/access.log", "Nginx access log to follow.") + flag.StringVar(&nginxConfig.ConfigFile, "config-file", "/var/log/nginx/access.log", "Nginx access log to follow.") + flag.StringVar(&nginxConfig.Config, "config", "", "Nginx config contents instead of `config-file`.") + flag.StringVar(&nginxConfig.TimeFormat, "time-format", "02/Jan/2006:15:04:05 -0700", "Nginx log time format.") + + flag.BoolVar(&sentryConfig.Debug, "debug", false, "Debug Sentry.") + flag.StringVar(&sentryConfig.Dsn, "dsn", "", "Sentry DSN. If not specified, get from SENTRY_DNS env (recommended).") + flag.StringVar(&sentryConfig.Env, "env", "", "Environment to use in event.") + flag.StringVar(&sentryConfig.Message, "message", "500", "Issue message.") + flag.StringVar(&sentryConfig.ServerName, "server-name", "", "Server name to use in event, default to current host name.") +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d98faf9 --- /dev/null +++ b/go.mod @@ -0,0 +1,19 @@ +module github.com/AlexanderMatveev/sentry-nginx + +go 1.20 + +require ( + github.com/getsentry/sentry-go v0.21.0 + github.com/hpcloud/tail v1.0.0 + github.com/papertrail/go-tail v0.0.0-20221103124010-5087eb6a0a07 + github.com/satyrius/gonx v1.4.0 +) + +require ( + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/smartystreets/goconvey v1.8.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + gopkg.in/fsnotify.v1 v1.4.7 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ad543ba --- /dev/null +++ b/go.sum @@ -0,0 +1,32 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/getsentry/sentry-go v0.21.0 h1:c9l5F1nPF30JIppulk4veau90PK6Smu3abgVtVQWon4= +github.com/getsentry/sentry-go v0.21.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/papertrail/go-tail v0.0.0-20221103124010-5087eb6a0a07 h1:LT8DNsrcjaqaKy/I2HTGjxRFazKu48G3AXoIXklcquw= +github.com/papertrail/go-tail v0.0.0-20221103124010-5087eb6a0a07/go.mod h1:aUf8RteFEultHEEQ5zPUnFIs/hLoI2z8vJHmn0rcuEk= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/satyrius/gonx v1.4.0 h1:F3uxif5Yx6FBzdQAh79bHQK6CTJugOcN0w0Z8azQuQg= +github.com/satyrius/gonx v1.4.0/go.mod h1:+r8KNe5d2tjkZU+DfhERo0G6KxkGih+1qYF6tqLHwvk= +github.com/smartystreets/assertions v1.13.1 h1:Ef7KhSmjZcK6AVf9YbJdvPYG9avaF0ZxudX+ThRdWfU= +github.com/smartystreets/goconvey v1.8.0 h1:Oi49ha/2MURE0WexF052Z0m+BNSGirfjg5RL+JXWq3w= +github.com/smartystreets/goconvey v1.8.0/go.mod h1:EdX8jtrTIj26jmjCOVNMVSIYAtgexqXKHOXW2Dx9JLg= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..f3ce4d0 --- /dev/null +++ b/main.go @@ -0,0 +1,123 @@ +package main + +import ( + "flag" + "github.com/getsentry/sentry-go" + "github.com/hpcloud/tail" + "github.com/satyrius/gonx" + "io" + "log" + "os" + "strings" + "time" +) + +func main() { + flag.Parse() + if sentryConfig.Dsn == "" { + sentryConfig.Dsn = os.Getenv("SENTRY_DSN") + } + if sentryConfig.ServerName == "" { + var err error + if sentryConfig.ServerName, err = os.Hostname(); err != nil { + log.Fatalf("can't get hostname from OS: %v", err) + } + } + + if err := sentry.Init(sentry.ClientOptions{ + Dsn: sentryConfig.Dsn, + TracesSampleRate: 1.0, + EnableTracing: true, + Debug: sentryConfig.Debug, + }); err != nil { + log.Fatalf("sentry.Init: %s", err) + } + defer sentry.Flush(2 * time.Second) + + log.Printf("server name: %s", sentryConfig.ServerName) + log.Printf("env: %s", sentryConfig.Env) + log.Printf("debug: %v", sentryConfig.Debug) + log.Printf("file: %v", nginxConfig.LogFile) + log.Printf("time format: %v", nginxConfig.TimeFormat) + + t, err := tail.TailFile(nginxConfig.LogFile, tail.Config{ + Location: &tail.SeekInfo{ + Whence: 2, + }, + Follow: true, + MustExist: false, + ReOpen: true, + }) + if err != nil { + log.Fatalf("tail.TailFile error: %v", err) + } + + var cr io.Reader + if nginxConfig.Config != "" { + cr = strings.NewReader(nginxConfig.Config) + } else { + if cr, err = os.Open(nginxConfig.ConfigFile); err != nil { + log.Fatalf("error opening config file: %v", err) + } + } + + parser, err := gonx.NewNginxParser(cr, "main") + if err != nil { + log.Fatalf("gonx.NewNginxParser error: %v", err) + } + + for line := range t.Lines { + if line.Text == "" { + continue + } + entry, err := parser.ParseString(line.Text) + if err != nil { + log.Fatalf("parser.ParseString error: %v", err) + } + status, err := entry.IntField("status") + if err != nil { + log.Fatalf("entry.IntField(status) error: %v", err) + } + if status != 500 { + continue + } + timeLocalString, err := entry.Field("time_local") + if err != nil { + log.Fatalf("entry.IntField(time_local) error: %v", err) + } + timeLocal, err := time.Parse(nginxConfig.TimeFormat, timeLocalString) + event := &sentry.Event{ + Message: sentryConfig.Message, + Level: sentry.LevelFatal, + Timestamp: timeLocal, + Environment: sentryConfig.Env, + ServerName: sentryConfig.ServerName, + Request: &sentry.Request{ + Headers: map[string]string{}, + }, + } + if referer := entry.Fields()["http_referer"]; referer != "" { + event.Request.Headers["Referer"] = referer + } + if userAgent := entry.Fields()["http_user_agent"]; userAgent != "" { + event.Request.Headers["User-Agent"] = userAgent + } + if uri := entry.Fields()["request_uri"]; uri != "" { + event.Request.URL = uri + } + if method := entry.Fields()["request_method"]; method != "" { + event.Request.Method = method + } + if ip := entry.Fields()["remote_addr"]; ip != "" { + event.User.IPAddress = ip + } + if xff := entry.Fields()["http_x_forwarded_for"]; xff != "" { + event.Request.Headers["X-Forwarded-For"] = xff + } + sentry.CaptureEvent(event) + } + + if err := t.Wait(); err != nil { + log.Fatalf("t.Wait error: %v", err) + } +}