diff --git a/README.md b/README.md index 1202d96..ac2e164 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ ## MG Transport Library -This library provides different functions like error-reporting, loggingm localization, etc. in order to make it easier to create transports +This library provides different functions like error-reporting, logging, localization, etc. in order to make it easier to create transports diff --git a/core/config.go b/core/config.go index c736162..bb0abe9 100644 --- a/core/config.go +++ b/core/config.go @@ -3,11 +3,23 @@ package core import ( "io/ioutil" "path/filepath" + "regexp" "github.com/op/go-logging" "gopkg.in/yaml.v2" ) +var ( + credentialsTransport = []string{ + "/api/integration-modules/{code}", + "/api/integration-modules/{code}/edit", + } + markdownSymbols = []string{"*", "_", "`", "["} + regCommandName = regexp.MustCompile(`^https://?[\da-z.-]+\.(retailcrm\.(ru|pro|es)|ecomlogic\.com|simlachat\.(com|ru))/?$`) + slashRegex = regexp.MustCompile(`/+$`) +) + + // ConfigInterface settings data structure type ConfigInterface interface { GetVersion() string diff --git a/core/sentry.go b/core/sentry.go index d62f897..0d3d7a7 100644 --- a/core/sentry.go +++ b/core/sentry.go @@ -1,14 +1,15 @@ package core import ( - "errors" "fmt" "net/http" "reflect" + "runtime" "runtime/debug" "strconv" - "github.com/Neur0toxine/mg-transport-lib/internal" + "github.com/pkg/errors" + "github.com/getsentry/raven-go" "github.com/gin-gonic/gin" "github.com/op/go-logging" @@ -227,7 +228,7 @@ func (s *Sentry) ErrorCaptureHandler() ErrorHandlerFunc { for _, err := range c.Errors { if s.Stacktrace { - stacktrace := internal.NewRavenStackTrace(s.Client, err.Err, 0) + stacktrace := newRavenStackTrace(s.Client, err.Err, 0) go s.Client.CaptureMessageAndWait( err.Error(), tags, @@ -380,3 +381,100 @@ func (t *SentryTaggedScalar) BuildTags(v interface{}) (items map[string]string, } return } + +// newRavenStackTrace generate stacktrace compatible with raven-go format +// It tries to extract better stacktrace from error from package "github.com/pkg/errors" +// In case of fail it will fallback to default stacktrace generation from raven-go. +// Default stacktrace highly likely will be useless, because it will not include call +// which returned error. This occurs because default stacktrace doesn't include any call +// before stacktrace generation, and raven-go will generate stacktrace here, which will end +// in trace to this file. But errors from "github.com/pkg/errors" will generate stacktrace +// immediately, it will include call which returned error, and we can fetch this trace. +// Also we can wrap default errors with error from this package, like this: +// errors.Wrap(err, err.Error) +func newRavenStackTrace(client *raven.Client, myerr error, skip int) *raven.Stacktrace { + st := getErrorStackTraceConverted(myerr, 3, client.IncludePaths()) + if st == nil { + st = raven.NewStacktrace(skip, 3, client.IncludePaths()) + } + return st +} + +// getErrorStackTraceConverted will return converted stacktrace from custom error, or nil in case of default error +func getErrorStackTraceConverted(err error, context int, appPackagePrefixes []string) *raven.Stacktrace { + st := getErrorCauseStackTrace(err) + if st == nil { + return nil + } + return convertStackTrace(st, context, appPackagePrefixes) +} + +// getErrorCauseStackTrace tries to extract stacktrace from custom error, returns nil in case of failure +func getErrorCauseStackTrace(err error) errors.StackTrace { + // This code is inspired by github.com/pkg/errors.Cause(). + var st errors.StackTrace + for err != nil { + s := getErrorStackTrace(err) + if s != nil { + st = s + } + err = getErrorCause(err) + } + return st +} + +// convertStackTrace converts github.com/pkg/errors.StackTrace to github.com/getsentry/raven-go.Stacktrace +func convertStackTrace(st errors.StackTrace, context int, appPackagePrefixes []string) *raven.Stacktrace { + // This code is borrowed from github.com/getsentry/raven-go.NewStacktrace(). + var frames []*raven.StacktraceFrame + for _, f := range st { + frame := convertFrame(f, context, appPackagePrefixes) + if frame != nil { + frames = append(frames, frame) + } + } + if len(frames) == 0 { + return nil + } + for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 { + frames[i], frames[j] = frames[j], frames[i] + } + return &raven.Stacktrace{Frames: frames} +} + +// convertFrame converts single frame from github.com/pkg/errors.Frame to github.com/pkg/errors.Frame +func convertFrame(f errors.Frame, context int, appPackagePrefixes []string) *raven.StacktraceFrame { + // This code is borrowed from github.com/pkg/errors.Frame. + pc := uintptr(f) - 1 + fn := runtime.FuncForPC(pc) + var file string + var line int + if fn != nil { + file, line = fn.FileLine(pc) + } else { + file = "unknown" + } + return raven.NewStacktraceFrame(pc, file, line, context, appPackagePrefixes) +} + +// getErrorStackTrace will try to extract stacktrace from error using StackTrace method (default errors doesn't have it) +func getErrorStackTrace(err error) errors.StackTrace { + ster, ok := err.(interface { + StackTrace() errors.StackTrace + }) + if !ok { + return nil + } + return ster.StackTrace() +} + +// getErrorCause will try to extract original error from wrapper - it is used only if stacktrace is not present +func getErrorCause(err error) error { + cer, ok := err.(interface { + Cause() error + }) + if !ok { + return nil + } + return cer.Cause() +} \ No newline at end of file diff --git a/core/utils.go b/core/utils.go index c66fd73..6412296 100644 --- a/core/utils.go +++ b/core/utils.go @@ -12,7 +12,6 @@ import ( "sync/atomic" "time" - "github.com/Neur0toxine/mg-transport-lib/internal" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" @@ -40,7 +39,7 @@ func NewUtils(awsConfig ConfigAWS, localizer *Localizer, logger *logging.Logger, Localizer: localizer, Logger: logger, TokenCounter: 0, - slashRegex: internal.SlashRegex, + slashRegex: slashRegex, } } @@ -49,7 +48,7 @@ func (u *Utils) resetUtils(awsConfig ConfigAWS, debug bool, tokenCounter uint32) u.TokenCounter = tokenCounter u.ConfigAWS = awsConfig u.IsDebug = debug - u.slashRegex = internal.SlashRegex + u.slashRegex = slashRegex } // GenerateToken will generate long pseudo-random string. @@ -73,29 +72,20 @@ func (u *Utils) GetAPIClient(url, key string) (*v5.Client, int, error) { if !cr.Success { u.Logger.Error(url, status, e.ApiErr, cr) - return nil, http.StatusBadRequest, errors.New(u.Localizer.GetLocalizedMessage("incorrect_url_key")) + return nil, http.StatusBadRequest, errors.New("invalid credentials") } if res := u.checkCredentials(cr.Credentials); len(res) != 0 { u.Logger.Error(url, status, res) - return nil, - http.StatusBadRequest, - errors.New( - u.Localizer.GetLocalizedTemplateMessage( - "missing_credentials", - map[string]interface{}{ - "Credentials": strings.Join(res, ", "), - }, - ), - ) + return nil, http.StatusBadRequest, errors.New("missing credentials") } return client, 0, nil } func (u *Utils) checkCredentials(credential []string) []string { - rc := make([]string, len(internal.CredentialsTransport)) - copy(rc, internal.CredentialsTransport) + rc := make([]string, len(credentialsTransport)) + copy(rc, credentialsTransport) for _, vc := range credential { for kn, vn := range rc { @@ -188,7 +178,7 @@ func GetEntitySHA1(v interface{}) (hash string, err error) { // ReplaceMarkdownSymbols will remove markdown symbols from text func ReplaceMarkdownSymbols(s string) string { - for _, v := range internal.MarkdownSymbols { + for _, v := range markdownSymbols { s = strings.Replace(s, v, "\\"+v, -1) } diff --git a/core/validator.go b/core/validator.go index e22602b..ea5d88f 100644 --- a/core/validator.go +++ b/core/validator.go @@ -4,7 +4,6 @@ import ( "reflect" "github.com/gin-gonic/gin/binding" - "github.com/Neur0toxine/mg-transport-lib/internal" "gopkg.in/go-playground/validator.v8" ) @@ -20,5 +19,5 @@ func validateCrmURL( v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string, ) bool { - return internal.RegCommandName.Match([]byte(field.Interface().(string))) + return regCommandName.Match([]byte(field.Interface().(string))) } diff --git a/internal/stacktrace.go b/internal/stacktrace.go deleted file mode 100644 index 4a9ce49..0000000 --- a/internal/stacktrace.go +++ /dev/null @@ -1,105 +0,0 @@ -package internal - -import ( - "runtime" - - "github.com/getsentry/raven-go" - "github.com/pkg/errors" -) - -// NewRavenStackTrace generate stacktrace compatible with raven-go format -// It tries to extract better stacktrace from error from package "github.com/pkg/errors" -// In case of fail it will fallback to default stacktrace generation from raven-go. -// Default stacktrace highly likely will be useless, because it will not include call -// which returned error. This occurs because default stacktrace doesn't include any call -// before stacktrace generation, and raven-go will generate stacktrace here, which will end -// in trace to this file. But errors from "github.com/pkg/errors" will generate stacktrace -// immediately, it will include call which returned error, and we can fetch this trace. -// Also we can wrap default errors with error from this package, like this: -// errors.Wrap(err, err.Error) -func NewRavenStackTrace(client *raven.Client, myerr error, skip int) *raven.Stacktrace { - st := getErrorStackTraceConverted(myerr, 3, client.IncludePaths()) - if st == nil { - st = raven.NewStacktrace(skip, 3, client.IncludePaths()) - } - return st -} - -// getErrorStackTraceConverted will return converted stacktrace from custom error, or nil in case of default error -func getErrorStackTraceConverted(err error, context int, appPackagePrefixes []string) *raven.Stacktrace { - st := getErrorCauseStackTrace(err) - if st == nil { - return nil - } - return convertStackTrace(st, context, appPackagePrefixes) -} - -// getErrorCauseStackTrace tries to extract stacktrace from custom error, returns nil in case of failure -func getErrorCauseStackTrace(err error) errors.StackTrace { - // This code is inspired by github.com/pkg/errors.Cause(). - var st errors.StackTrace - for err != nil { - s := getErrorStackTrace(err) - if s != nil { - st = s - } - err = getErrorCause(err) - } - return st -} - -// convertStackTrace converts github.com/pkg/errors.StackTrace to github.com/getsentry/raven-go.Stacktrace -func convertStackTrace(st errors.StackTrace, context int, appPackagePrefixes []string) *raven.Stacktrace { - // This code is borrowed from github.com/getsentry/raven-go.NewStacktrace(). - var frames []*raven.StacktraceFrame - for _, f := range st { - frame := convertFrame(f, context, appPackagePrefixes) - if frame != nil { - frames = append(frames, frame) - } - } - if len(frames) == 0 { - return nil - } - for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 { - frames[i], frames[j] = frames[j], frames[i] - } - return &raven.Stacktrace{Frames: frames} -} - -// convertFrame converts single frame from github.com/pkg/errors.Frame to github.com/pkg/errors.Frame -func convertFrame(f errors.Frame, context int, appPackagePrefixes []string) *raven.StacktraceFrame { - // This code is borrowed from github.com/pkg/errors.Frame. - pc := uintptr(f) - 1 - fn := runtime.FuncForPC(pc) - var file string - var line int - if fn != nil { - file, line = fn.FileLine(pc) - } else { - file = "unknown" - } - return raven.NewStacktraceFrame(pc, file, line, context, appPackagePrefixes) -} - -// getErrorStackTrace will try to extract stacktrace from error using StackTrace method (default errors doesn't have it) -func getErrorStackTrace(err error) errors.StackTrace { - ster, ok := err.(interface { - StackTrace() errors.StackTrace - }) - if !ok { - return nil - } - return ster.StackTrace() -} - -// getErrorCause will try to extract original error from wrapper - it is used only if stacktrace is not present -func getErrorCause(err error) error { - cer, ok := err.(interface { - Cause() error - }) - if !ok { - return nil - } - return cer.Cause() -} diff --git a/internal/variables.go b/internal/variables.go deleted file mode 100644 index de9bf97..0000000 --- a/internal/variables.go +++ /dev/null @@ -1,14 +0,0 @@ -package internal - -import "regexp" - -// CredentialsTransport set of API methods for transport registration -var ( - CredentialsTransport = []string{ - "/api/integration-modules/{code}", - "/api/integration-modules/{code}/edit", - } - MarkdownSymbols = []string{"*", "_", "`", "["} - RegCommandName = regexp.MustCompile(`^https://?[\da-z.-]+\.(retailcrm\.(ru|pro|es)|ecomlogic\.com|simlachat\.(com|ru))/?$`) - SlashRegex = regexp.MustCompile(`/+$`) -)