From 540edc0c35e97393d9a01c927f7ac962bcbf6d37 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sun, 26 Aug 2018 15:46:18 +1000 Subject: anonymous reporting data --- vendor/github.com/heroku/rollrus/LICENSE | 22 +++ vendor/github.com/heroku/rollrus/rollrus.go | 287 ++++++++++++++++++++++++++++ vendor/github.com/pkg/errors/LICENSE | 23 +++ vendor/github.com/pkg/errors/errors.go | 269 ++++++++++++++++++++++++++ vendor/github.com/pkg/errors/stack.go | 178 +++++++++++++++++ vendor/github.com/stvp/roll/LICENSE | 22 +++ vendor/github.com/stvp/roll/client.go | 240 +++++++++++++++++++++++ vendor/github.com/stvp/roll/stack.go | 98 ++++++++++ 8 files changed, 1139 insertions(+) create mode 100644 vendor/github.com/heroku/rollrus/LICENSE create mode 100644 vendor/github.com/heroku/rollrus/rollrus.go create mode 100644 vendor/github.com/pkg/errors/LICENSE create mode 100644 vendor/github.com/pkg/errors/errors.go create mode 100644 vendor/github.com/pkg/errors/stack.go create mode 100644 vendor/github.com/stvp/roll/LICENSE create mode 100644 vendor/github.com/stvp/roll/client.go create mode 100644 vendor/github.com/stvp/roll/stack.go (limited to 'vendor') diff --git a/vendor/github.com/heroku/rollrus/LICENSE b/vendor/github.com/heroku/rollrus/LICENSE new file mode 100644 index 000000000..8d4a5174d --- /dev/null +++ b/vendor/github.com/heroku/rollrus/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright © Heroku 2014 - 2015 + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/heroku/rollrus/rollrus.go b/vendor/github.com/heroku/rollrus/rollrus.go new file mode 100644 index 000000000..50dd225da --- /dev/null +++ b/vendor/github.com/heroku/rollrus/rollrus.go @@ -0,0 +1,287 @@ +// Package rollrus combines github.com/stvp/roll with github.com/sirupsen/logrus +// via logrus.Hook mechanism, so that whenever logrus' logger.Error/f(), +// logger.Fatal/f() or logger.Panic/f() are used the messages are +// intercepted and sent to rollbar. +// +// Using SetupLogging should suffice for basic use cases that use the logrus +// singleton logger. +// +// More custom uses are supported by creating a new Hook with NewHook and +// registering that hook with the logrus Logger of choice. +// +// The levels can be customized with the WithLevels OptionFunc. +// +// Specific errors can be ignored with the WithIgnoredErrors OptionFunc. This is +// useful for ignoring errors such as context.Canceled. +// +// See the Examples in the tests for more usage. +package rollrus + +import ( + "fmt" + "os" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/stvp/roll" +) + +var defaultTriggerLevels = []logrus.Level{ + logrus.ErrorLevel, + logrus.FatalLevel, + logrus.PanicLevel, +} + +// Hook is a wrapper for the rollbar Client and is usable as a logrus.Hook. +type Hook struct { + roll.Client + triggers []logrus.Level + ignoredErrors map[error]struct{} + ignoreErrorFunc func(error) bool + ignoreFunc func(error, map[string]string) bool + + // only used for tests to verify whether or not a report happened. + reported bool +} + +// OptionFunc that can be passed to NewHook. +type OptionFunc func(*Hook) + +// wellKnownErrorFields are the names of the fields to be checked for values of +// type `error`, in priority order. +var wellKnownErrorFields = []string{ + logrus.ErrorKey, "err", +} + +// WithLevels is an OptionFunc that customizes the log.Levels the hook will +// report on. +func WithLevels(levels ...logrus.Level) OptionFunc { + return func(h *Hook) { + h.triggers = levels + } +} + +// WithMinLevel is an OptionFunc that customizes the log.Levels the hook will +// report on by selecting all levels more severe than the one provided. +func WithMinLevel(level logrus.Level) OptionFunc { + var levels []logrus.Level + for _, l := range logrus.AllLevels { + if l <= level { + levels = append(levels, l) + } + } + + return func(h *Hook) { + h.triggers = levels + } +} + +// WithIgnoredErrors is an OptionFunc that whitelists certain errors to prevent +// them from firing. +func WithIgnoredErrors(errors ...error) OptionFunc { + return func(h *Hook) { + for _, e := range errors { + h.ignoredErrors[e] = struct{}{} + } + } +} + +// WithIgnoreErrorFunc is an OptionFunc that receives the error that is about +// to be logged and returns true/false if it wants to fire a rollbar alert for. +func WithIgnoreErrorFunc(fn func(error) bool) OptionFunc { + return func(h *Hook) { + h.ignoreErrorFunc = fn + } +} + +// WithIgnoreFunc is an OptionFunc that receives the error and custom fields that are about +// to be logged and returns true/false if it wants to fire a rollbar alert for. +func WithIgnoreFunc(fn func(err error, fields map[string]string) bool) OptionFunc { + return func(h *Hook) { + h.ignoreFunc = fn + } +} + +// NewHook creates a hook that is intended for use with your own logrus.Logger +// instance. Uses the defualt report levels defined in wellKnownErrorFields. +func NewHook(token string, env string, opts ...OptionFunc) *Hook { + h := NewHookForLevels(token, env, defaultTriggerLevels) + + for _, o := range opts { + o(h) + } + + return h +} + +// NewHookForLevels provided by the caller. Otherwise works like NewHook. +func NewHookForLevels(token string, env string, levels []logrus.Level) *Hook { + return &Hook{ + Client: roll.New(token, env), + triggers: levels, + ignoredErrors: make(map[error]struct{}), + ignoreErrorFunc: func(error) bool { return false }, + ignoreFunc: func(error, map[string]string) bool { return false }, + } +} + +// SetupLogging for use on Heroku. If token is not an empty string a rollbar +// hook is added with the environment set to env. The log formatter is set to a +// TextFormatter with timestamps disabled. +func SetupLogging(token, env string) { + setupLogging(token, env, defaultTriggerLevels) +} + +// SetupLoggingForLevels works like SetupLogging, but allows you to +// set the levels on which to trigger this hook. +func SetupLoggingForLevels(token, env string, levels []logrus.Level) { + setupLogging(token, env, levels) +} + +func setupLogging(token, env string, levels []logrus.Level) { + logrus.SetFormatter(&logrus.TextFormatter{DisableTimestamp: true}) + + if token != "" { + logrus.AddHook(NewHookForLevels(token, env, levels)) + } +} + +// ReportPanic attempts to report the panic to rollbar using the provided +// client and then re-panic. If it can't report the panic it will print an +// error to stderr. +func (r *Hook) ReportPanic() { + if p := recover(); p != nil { + if _, err := r.Client.Critical(fmt.Errorf("panic: %q", p), nil); err != nil { + fmt.Fprintf(os.Stderr, "reporting_panic=false err=%q\n", err) + } + panic(p) + } +} + +// ReportPanic attempts to report the panic to rollbar if the token is set +func ReportPanic(token, env string) { + if token != "" { + h := &Hook{Client: roll.New(token, env)} + h.ReportPanic() + } +} + +// Levels returns the logrus log.Levels that this hook handles +func (r *Hook) Levels() []logrus.Level { + if r.triggers == nil { + return defaultTriggerLevels + } + return r.triggers +} + +// Fire the hook. This is called by Logrus for entries that match the levels +// returned by Levels(). +func (r *Hook) Fire(entry *logrus.Entry) error { + trace, cause := extractError(entry) + if _, ok := r.ignoredErrors[cause]; ok { + return nil + } + + if r.ignoreErrorFunc(cause) { + return nil + } + + m := convertFields(entry.Data) + if _, exists := m["time"]; !exists { + m["time"] = entry.Time.Format(time.RFC3339) + } + + if r.ignoreFunc(cause, m) { + return nil + } + + return r.report(entry, cause, m, trace) +} + +func (r *Hook) report(entry *logrus.Entry, cause error, m map[string]string, trace []uintptr) (err error) { + hasTrace := len(trace) > 0 + level := entry.Level + + r.reported = true + + switch { + case hasTrace && level == logrus.FatalLevel: + _, err = r.Client.CriticalStack(cause, trace, m) + case hasTrace && level == logrus.PanicLevel: + _, err = r.Client.CriticalStack(cause, trace, m) + case hasTrace && level == logrus.ErrorLevel: + _, err = r.Client.ErrorStack(cause, trace, m) + case hasTrace && level == logrus.WarnLevel: + _, err = r.Client.WarningStack(cause, trace, m) + case level == logrus.FatalLevel || level == logrus.PanicLevel: + _, err = r.Client.Critical(cause, m) + case level == logrus.ErrorLevel: + _, err = r.Client.Error(cause, m) + case level == logrus.WarnLevel: + _, err = r.Client.Warning(cause, m) + case level == logrus.InfoLevel: + _, err = r.Client.Info(entry.Message, m) + case level == logrus.DebugLevel: + _, err = r.Client.Debug(entry.Message, m) + } + return err +} + +// convertFields converts from log.Fields to map[string]string so that we can +// report extra fields to Rollbar +func convertFields(fields logrus.Fields) map[string]string { + m := make(map[string]string) + for k, v := range fields { + switch t := v.(type) { + case time.Time: + m[k] = t.Format(time.RFC3339) + default: + if s, ok := v.(fmt.Stringer); ok { + m[k] = s.String() + } else { + m[k] = fmt.Sprintf("%+v", t) + } + } + } + + return m +} + +// extractError attempts to extract an error from a well known field, err or error +func extractError(entry *logrus.Entry) ([]uintptr, error) { + var trace []uintptr + fields := entry.Data + + type stackTracer interface { + StackTrace() errors.StackTrace + } + + for _, f := range wellKnownErrorFields { + e, ok := fields[f] + if !ok { + continue + } + err, ok := e.(error) + if !ok { + continue + } + + cause := errors.Cause(err) + tracer, ok := err.(stackTracer) + if ok { + return copyStackTrace(tracer.StackTrace()), cause + } + return trace, cause + } + + // when no error found, default to the logged message. + return trace, fmt.Errorf(entry.Message) +} + +func copyStackTrace(trace errors.StackTrace) (out []uintptr) { + for _, frame := range trace { + out = append(out, uintptr(frame)) + } + return +} diff --git a/vendor/github.com/pkg/errors/LICENSE b/vendor/github.com/pkg/errors/LICENSE new file mode 100644 index 000000000..835ba3e75 --- /dev/null +++ b/vendor/github.com/pkg/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go new file mode 100644 index 000000000..842ee8045 --- /dev/null +++ b/vendor/github.com/pkg/errors/errors.go @@ -0,0 +1,269 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Wrap function returns a new error that adds context to the +// original error by recording a stack trace at the point Wrap is called, +// and the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// If additional control is required the errors.WithStack and errors.WithMessage +// functions destructure errors.Wrap into its component operations of annotating +// an error with a stack trace and an a message, respectively. +// +// Retrieving the cause of an error +// +// Using errors.Wrap constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Wrap to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error which does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// causer interface is not exported by this package, but is considered a part +// of stable public API. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported +// +// %s print the error. If the error has a Cause it will be +// printed recursively +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are +// invoked. This information can be retrieved with the following interface. +// +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } +// +// Where errors.StackTrace is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// stackTracer interface is not exported by this package, but is considered a part +// of stable public API. +// +// See the documentation for Frame.Format for more details. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &withStack{ + err, + callers(), + } +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: message, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is call, and the format specifier. +// If err is nil, Wrapf returns nil. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + } +} + +type withMessage struct { + cause error + msg string +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + type causer interface { + Cause() error + } + + for err != nil { + cause, ok := err.(causer) + if !ok { + break + } + err = cause.Cause() + } + return err +} diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go new file mode 100644 index 000000000..6b1f2891a --- /dev/null +++ b/vendor/github.com/pkg/errors/stack.go @@ -0,0 +1,178 @@ +package errors + +import ( + "fmt" + "io" + "path" + "runtime" + "strings" +) + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s path of source file relative to the compile time GOPATH +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(s, "unknown") + } else { + file, _ := fn.FileLine(pc) + fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) + } + default: + io.WriteString(s, path.Base(f.file())) + } + case 'd': + fmt.Fprintf(s, "%d", f.line()) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(s, funcname(name)) + case 'v': + f.Format(s, 's') + io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + fmt.Fprintf(s, "\n%+v", f) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []Frame(st)) + default: + fmt.Fprintf(s, "%v", []Frame(st)) + } + case 's': + fmt.Fprintf(s, "%s", []Frame(st)) + } +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := Frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} + +func trimGOPATH(name, file string) string { + // Here we want to get the source file path relative to the compile time + // GOPATH. As of Go 1.6.x there is no direct way to know the compiled + // GOPATH at runtime, but we can infer the number of path segments in the + // GOPATH. We note that fn.Name() returns the function name qualified by + // the import path, which does not include the GOPATH. Thus we can trim + // segments from the beginning of the file path until the number of path + // separators remaining is one more than the number of path separators in + // the function name. For example, given: + // + // GOPATH /home/user + // file /home/user/src/pkg/sub/file.go + // fn.Name() pkg/sub.Type.Method + // + // We want to produce: + // + // pkg/sub/file.go + // + // From this we can easily see that fn.Name() has one less path separator + // than our desired output. We count separators from the end of the file + // path until it finds two more than in the function name and then move + // one character forward to preserve the initial path segment without a + // leading separator. + const sep = "/" + goal := strings.Count(name, sep) + 2 + i := len(file) + for n := 0; n < goal; n++ { + i = strings.LastIndex(file[:i], sep) + if i == -1 { + // not enough separators found, set i so that the slice expression + // below leaves file unmodified + i = -len(sep) + break + } + } + // get back to 0 or trim the leading separator + file = file[i+len(sep):] + return file +} diff --git a/vendor/github.com/stvp/roll/LICENSE b/vendor/github.com/stvp/roll/LICENSE new file mode 100644 index 000000000..1235e06b5 --- /dev/null +++ b/vendor/github.com/stvp/roll/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2016 Stovepipe Studios + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/stvp/roll/client.go b/vendor/github.com/stvp/roll/client.go new file mode 100644 index 000000000..687343f2a --- /dev/null +++ b/vendor/github.com/stvp/roll/client.go @@ -0,0 +1,240 @@ +package roll + +import ( + "bytes" + "encoding/json" + "fmt" + "hash/adler32" + "io" + "io/ioutil" + "net/http" + "os" + "reflect" + "runtime" + "strings" + "time" +) + +const ( + // By default, all Rollbar API requests are sent to this endpoint. + endpoint = "https://api.rollbar.com/api/1/item/" + + // Identify this Rollbar client library to the Rollbar API. + clientName = "go-roll" + clientVersion = "0.2.0" + clientLanguage = "go" +) + +var ( + // Endpoint is the default HTTP(S) endpoint that all Rollbar API requests + // will be sent to. By default, this is Rollbar's "Items" API endpoint. If + // this is blank, no items will be sent to Rollbar. + Endpoint = endpoint + + // Rollbar access token for the global client. If this is blank, no items + // will be sent to Rollbar. + Token = "" + + // Environment for all items reported with the global client. + Environment = "development" +) + +type rollbarSuccess struct { + Result map[string]string `json:"result"` +} + +// Client reports items to a single Rollbar project. +type Client interface { + Critical(err error, custom map[string]string) (uuid string, e error) + CriticalStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error) + Error(err error, custom map[string]string) (uuid string, e error) + ErrorStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error) + Warning(err error, custom map[string]string) (uuid string, e error) + WarningStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error) + Info(msg string, custom map[string]string) (uuid string, e error) + Debug(msg string, custom map[string]string) (uuid string, e error) +} + +type rollbarClient struct { + token string + env string +} + +// New creates a new Rollbar client that reports items to the given project +// token and with the given environment (eg. "production", "development", etc). +func New(token, env string) Client { + return &rollbarClient{token, env} +} + +func Critical(err error, custom map[string]string) (uuid string, e error) { + return CriticalStack(err, getCallers(2), custom) +} + +func CriticalStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error) { + return New(Token, Environment).CriticalStack(err, ptrs, custom) +} + +func Error(err error, custom map[string]string) (uuid string, e error) { + return ErrorStack(err, getCallers(2), custom) +} + +func ErrorStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error) { + return New(Token, Environment).ErrorStack(err, ptrs, custom) +} + +func Warning(err error, custom map[string]string) (uuid string, e error) { + return WarningStack(err, getCallers(2), custom) +} + +func WarningStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error) { + return New(Token, Environment).WarningStack(err, ptrs, custom) +} + +func Info(msg string, custom map[string]string) (uuid string, e error) { + return New(Token, Environment).Info(msg, custom) +} + +func Debug(msg string, custom map[string]string) (uuid string, e error) { + return New(Token, Environment).Debug(msg, custom) +} + +func (c *rollbarClient) Critical(err error, custom map[string]string) (uuid string, e error) { + return c.CriticalStack(err, getCallers(2), custom) +} + +func (c *rollbarClient) CriticalStack(err error, callers []uintptr, custom map[string]string) (uuid string, e error) { + item := c.buildTraceItem("critical", err, callers, custom) + return c.send(item) +} + +func (c *rollbarClient) Error(err error, custom map[string]string) (uuid string, e error) { + return c.ErrorStack(err, getCallers(2), custom) +} + +func (c *rollbarClient) ErrorStack(err error, callers []uintptr, custom map[string]string) (uuid string, e error) { + item := c.buildTraceItem("error", err, callers, custom) + return c.send(item) +} + +func (c *rollbarClient) Warning(err error, custom map[string]string) (uuid string, e error) { + return c.WarningStack(err, getCallers(2), custom) +} + +func (c *rollbarClient) WarningStack(err error, callers []uintptr, custom map[string]string) (uuid string, e error) { + item := c.buildTraceItem("warning", err, callers, custom) + return c.send(item) +} + +func (c *rollbarClient) Info(msg string, custom map[string]string) (uuid string, e error) { + item := c.buildMessageItem("info", msg, custom) + return c.send(item) +} + +func (c *rollbarClient) Debug(msg string, custom map[string]string) (uuid string, e error) { + item := c.buildMessageItem("debug", msg, custom) + return c.send(item) +} + +func (c *rollbarClient) buildTraceItem(level string, err error, callers []uintptr, custom map[string]string) (item map[string]interface{}) { + stack := buildRollbarFrames(callers) + item = c.buildItem(level, err.Error(), custom) + itemData := item["data"].(map[string]interface{}) + itemData["fingerprint"] = stack.fingerprint() + itemData["body"] = map[string]interface{}{ + "trace": map[string]interface{}{ + "frames": stack, + "exception": map[string]interface{}{ + "class": errorClass(err), + "message": err.Error(), + }, + }, + } + + return item +} + +func (c *rollbarClient) buildMessageItem(level string, msg string, custom map[string]string) (item map[string]interface{}) { + item = c.buildItem(level, msg, custom) + itemData := item["data"].(map[string]interface{}) + itemData["body"] = map[string]interface{}{ + "message": map[string]interface{}{ + "body": msg, + }, + } + + return item +} + +func (c *rollbarClient) buildItem(level, title string, custom map[string]string) map[string]interface{} { + hostname, _ := os.Hostname() + + return map[string]interface{}{ + "access_token": c.token, + "data": map[string]interface{}{ + "environment": c.env, + "title": title, + "level": level, + "timestamp": time.Now().Unix(), + "platform": runtime.GOOS, + "language": clientLanguage, + "server": map[string]interface{}{ + "host": hostname, + }, + "notifier": map[string]interface{}{ + "name": clientName, + "version": clientVersion, + }, + "custom": custom, + }, + } +} + +// send reports the given item to Rollbar and returns either a UUID for the +// reported item or an error. +func (c *rollbarClient) send(item map[string]interface{}) (uuid string, err error) { + if len(c.token) == 0 || len(Endpoint) == 0 { + return "", nil + } + + jsonBody, err := json.Marshal(item) + if err != nil { + return "", err + } + + resp, err := http.Post(Endpoint, "application/json", bytes.NewReader(jsonBody)) + if err != nil { + return "", err + } + defer func() { + io.Copy(ioutil.Discard, resp.Body) + resp.Body.Close() + }() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("Rollbar returned %s", resp.Status) + } + + // Extract UUID from JSON response + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", nil + } + success := rollbarSuccess{} + json.Unmarshal(body, &success) + + return success.Result["uuid"], nil +} + +// errorClass returns a class name for an error (eg. "ErrUnexpectedEOF"). For +// string errors, it returns an Adler-32 checksum of the error string. +func errorClass(err error) string { + class := reflect.TypeOf(err).String() + if class == "" { + return "panic" + } else if class == "*errors.errorString" { + checksum := adler32.Checksum([]byte(err.Error())) + return fmt.Sprintf("{%x}", checksum) + } else { + return strings.TrimPrefix(class, "*") + } +} diff --git a/vendor/github.com/stvp/roll/stack.go b/vendor/github.com/stvp/roll/stack.go new file mode 100644 index 000000000..c50425491 --- /dev/null +++ b/vendor/github.com/stvp/roll/stack.go @@ -0,0 +1,98 @@ +package roll + +import ( + "fmt" + "hash/crc32" + "os" + "runtime" + "strings" +) + +var ( + knownFilePathPatterns = []string{ + "github.com/", + "code.google.com/", + "bitbucket.org/", + "launchpad.net/", + "gopkg.in/", + } +) + +func getCallers(skip int) (pc []uintptr) { + pc = make([]uintptr, 1000) + i := runtime.Callers(skip+1, pc) + return pc[0:i] +} + +// -- rollbarFrames + +type rollbarFrame struct { + Filename string `json:"filename"` + Method string `json:"method"` + Line int `json:"lineno"` +} + +type rollbarFrames []rollbarFrame + +// buildRollbarFrames takes a slice of function pointers and returns a Rollbar +// API payload containing the filename, method name, and line number of each +// function. +func buildRollbarFrames(callers []uintptr) (frames rollbarFrames) { + frames = rollbarFrames{} + + // 2016-08-24 - runtime.CallersFrames was added in Go 1.7, which should + // replace the following code when roll is able to require Go 1.7+. + for _, caller := range callers { + frame := rollbarFrame{ + Filename: "???", + Method: "???", + } + if fn := runtime.FuncForPC(caller); fn != nil { + name, line := fn.FileLine(caller) + frame.Filename = scrubFile(name) + frame.Line = line + frame.Method = scrubFunction(fn.Name()) + } + frames = append(frames, frame) + } + + return frames +} + +// fingerprint returns a checksum that uniquely identifies a stacktrace by the +// filename, method name, and line number of every frame in the stack. +func (f rollbarFrames) fingerprint() string { + hash := crc32.NewIEEE() + for _, frame := range f { + fmt.Fprintf(hash, "%s%s%d", frame.Filename, frame.Method, frame.Line) + } + return fmt.Sprintf("%x", hash.Sum32()) +} + +// -- Helpers + +// scrubFile removes unneeded information from the path of a source file. This +// makes them shorter in Rollbar UI as well as making them the same, regardless +// of the machine the code was compiled on. +// +// Example: +// /home/foo/go/src/github.com/stvp/roll/rollbar.go -> github.com/stvp/roll/rollbar.go +func scrubFile(s string) string { + var i int + for _, pattern := range knownFilePathPatterns { + i = strings.Index(s, pattern) + if i != -1 { + return s[i:] + } + } + return s +} + +// scrubFunction removes unneeded information from the full name of a function. +// +// Example: +// github.com/stvp/roll.getCallers -> roll.getCallers +func scrubFunction(name string) string { + end := strings.LastIndex(name, string(os.PathSeparator)) + return name[end+1 : len(name)] +} -- cgit v1.2.3