grafana/pkg/api/frontend_logging.go
Domas 76df096791
Logging: Log frontend errors (#28073)
* basic frontend  Sentry integration

* backend endpoint to capture sentry events

* WIP!

* log user email for frontend logs

* remove debug logging

* lint fixes

* Fix type exports & property names

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* additional struct naming fix

* rename log endpoint, config section & interface

* add sentry sample rate to config

* refac to use EchoSrv

* log user id

* backend tests

* tests for SentryEchoBackend

* sentry echo backend tests

* CustomEndpointTransport tests

* Update pkg/api/frontend_logging_test.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update conf/defaults.ini

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/api/frontend_logging_test.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* don't export unnecesasrily

* update go.sum

* get rid of Convey in tests, use stdlib

* add sentry config to sample.ini

* cleanup to set orig logging handler in test

* Apply suggestions from code review

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* PR feedback changes

* lock sentry version

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-12 12:29:43 +01:00

91 lines
2.3 KiB
Go

package api
import (
"fmt"
"strings"
"github.com/getsentry/sentry-go"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/inconshreveable/log15"
)
var frontendLogger = log.New("frontend")
type frontendSentryExceptionValue struct {
Value string `json:"value,omitempty"`
Type string `json:"type,omitempty"`
Stacktrace sentry.Stacktrace `json:"stacktrace,omitempty"`
}
type frontendSentryException struct {
Values []frontendSentryExceptionValue `json:"values,omitempty"`
}
type frontendSentryEvent struct {
*sentry.Event
Exception *frontendSentryException `json:"exception,omitempty"`
}
func (value *frontendSentryExceptionValue) FmtMessage() string {
return fmt.Sprintf("%s: %s", value.Type, value.Value)
}
func (value *frontendSentryExceptionValue) FmtStacktrace() string {
var stacktrace = value.FmtMessage()
for _, frame := range value.Stacktrace.Frames {
stacktrace += fmt.Sprintf("\n at %s (%s:%v:%v)", frame.Function, frame.Filename, frame.Lineno, frame.Colno)
}
return stacktrace
}
func (exception *frontendSentryException) FmtStacktraces() string {
var stacktraces []string
for _, value := range exception.Values {
stacktraces = append(stacktraces, value.FmtStacktrace())
}
return strings.Join(stacktraces, "\n\n")
}
func (event *frontendSentryEvent) ToLogContext() log15.Ctx {
var ctx = make(log15.Ctx)
ctx["url"] = event.Request.URL
ctx["user_agent"] = event.Request.Headers["User-Agent"]
ctx["event_id"] = event.EventID
ctx["original_timestamp"] = event.Timestamp
if event.Exception != nil {
ctx["stacktrace"] = event.Exception.FmtStacktraces()
}
if len(event.User.Email) > 0 {
ctx["user_email"] = event.User.Email
ctx["user_id"] = event.User.ID
}
return ctx
}
func (hs *HTTPServer) logFrontendMessage(c *models.ReqContext, event frontendSentryEvent) Response {
var msg = "unknown"
if len(event.Message) > 0 {
msg = event.Message
} else if event.Exception != nil && len(event.Exception.Values) > 0 {
msg = event.Exception.Values[0].FmtMessage()
}
var ctx = event.ToLogContext()
switch event.Level {
case sentry.LevelError:
frontendLogger.Error(msg, ctx)
case sentry.LevelWarning:
frontendLogger.Warn(msg, ctx)
case sentry.LevelDebug:
frontendLogger.Debug(msg, ctx)
default:
frontendLogger.Info(msg, ctx)
}
return Success("ok")
}