Files
Serge Zaitsev d9cdcb550e Chore: Refactor api handlers to use web.Bind (#42199)
* Chore: Refactor api handlers to use web.Bind

* fix comments

* fix comment

* trying to fix most of the tests and force routing.Wrap type check

* fix library panels tests

* fix frontend logging tests

* allow passing nil as a response to skip writing

* return nil instead of the response

* rewrite login handler function types

* remove handlerFuncCtx

* make linter happy

* remove old bindings from the libraryelements

* restore comments
2021-11-29 10:18:01 +01:00

110 lines
3.1 KiB
Go

package frontendlogging
import (
"encoding/json"
"fmt"
"strings"
"github.com/getsentry/sentry-go"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/inconshreveable/log15"
)
var logger = log.New("frontendlogging")
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 fmtLine(frame sentry.Frame) string {
module := ""
if len(frame.Module) > 0 {
module = frame.Module + "|"
}
return fmt.Sprintf("\n at %s (%s%s:%v:%v)", frame.Function, module, frame.Filename, frame.Lineno, frame.Colno)
}
func (value *FrontendSentryExceptionValue) FmtStacktrace(store *SourceMapStore) string {
var stacktrace = value.FmtMessage()
for _, frame := range value.Stacktrace.Frames {
mappedFrame, err := store.resolveSourceLocation(frame)
if err != nil {
logger.Error("Error resolving stack trace frame source location", "err", err)
stacktrace += fmtLine(frame) // even if reading source map fails for unexpected reason, still better to log compiled location than nothing at all
} else {
if mappedFrame != nil {
stacktrace += fmtLine(*mappedFrame)
} else {
stacktrace += fmtLine(frame)
}
}
}
return stacktrace
}
func (exception *FrontendSentryException) FmtStacktraces(store *SourceMapStore) string {
var stacktraces []string
for _, value := range exception.Values {
stacktraces = append(stacktraces, value.FmtStacktrace(store))
}
return strings.Join(stacktraces, "\n\n")
}
func addEventContextToLogContext(rootPrefix string, logCtx log15.Ctx, eventCtx map[string]interface{}) {
for key, element := range eventCtx {
prefix := fmt.Sprintf("%s_%s", rootPrefix, key)
switch v := element.(type) {
case map[string]interface{}:
addEventContextToLogContext(prefix, logCtx, v)
default:
logCtx[prefix] = fmt.Sprintf("%v", v)
}
}
}
func (event *FrontendSentryEvent) ToLogContext(store *SourceMapStore) 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(store)
}
addEventContextToLogContext("context", ctx, event.Contexts)
if len(event.User.Email) > 0 {
ctx["user_email"] = event.User.Email
ctx["user_id"] = event.User.ID
}
return ctx
}
func (event *FrontendSentryEvent) MarshalJSON() ([]byte, error) {
eventJSON, err := json.Marshal(event.Event)
if err != nil {
return nil, err
}
exceptionJSON, err := json.Marshal(map[string]interface{}{"exception": event.Exception})
if err != nil {
return nil, err
}
exceptionJSON[0] = ','
return append(eventJSON[:len(eventJSON)-1], exceptionJSON...), nil
}