mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-01-04 03:48:07 +02:00
anonymous reporting data
This commit is contained in:
parent
93ab892bdd
commit
540edc0c35
25
Gopkg.lock
generated
25
Gopkg.lock
generated
@ -75,6 +75,14 @@
|
||||
pruneopts = "NUT"
|
||||
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d457d39e88f678ed14ac29517c3d74927a48dbc6a9f073fa241cf364a68cbe5c"
|
||||
name = "github.com/heroku/rollrus"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "fc0cef2ff331aebb24cd4e9ded7e20650f3d7006"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:62fe3a7ea2050ecbd753a71889026f83d73329337ada66325cbafd5dea5f713d"
|
||||
@ -191,6 +199,14 @@
|
||||
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121"
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
@ -294,6 +310,14 @@
|
||||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
||||
version = "v1.2.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:e42372d3f4921ec35df07f9b23239631e9d28580f7c1edcca212bc6daddc68fe"
|
||||
name = "github.com/stvp/roll"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "3627a5cbeaeaa68023abd02bb8687925265f2f63"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:cd5ffc5bda4e0296ab3e4de90dbb415259c78e45e7fab13694b14cde8ab74541"
|
||||
name = "github.com/tcnksm/go-gitconfig"
|
||||
@ -458,6 +482,7 @@
|
||||
"github.com/davecgh/go-spew/spew",
|
||||
"github.com/fatih/color",
|
||||
"github.com/golang-collections/collections/stack",
|
||||
"github.com/heroku/rollrus",
|
||||
"github.com/jesseduffield/gocui",
|
||||
"github.com/mgutz/str",
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n",
|
||||
|
@ -18,6 +18,7 @@
|
||||
# stuff relating to git
|
||||
os:
|
||||
# stuff relating to the OS
|
||||
reporting: 'undetermined' # one of: 'on' | 'off' | 'undetermined'
|
||||
```
|
||||
|
||||
## Color Attributes:
|
||||
|
@ -5,11 +5,12 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/heroku/rollrus"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// App struct
|
||||
@ -17,19 +18,26 @@ type App struct {
|
||||
closers []io.Closer
|
||||
|
||||
Config config.AppConfigurer
|
||||
Log *logrus.Logger
|
||||
Log *logrus.Entry
|
||||
OSCommand *commands.OSCommand
|
||||
GitCommand *commands.GitCommand
|
||||
Gui *gui.Gui
|
||||
Tr *i18n.Localizer
|
||||
}
|
||||
|
||||
func newLogger(config config.AppConfigurer) *logrus.Logger {
|
||||
func newProductionLogger(config config.AppConfigurer) *logrus.Logger {
|
||||
log := logrus.New()
|
||||
if !config.GetDebug() {
|
||||
log.Out = ioutil.Discard
|
||||
return log
|
||||
log.Out = ioutil.Discard
|
||||
if config.GetUserConfig().GetString("reporting") == "on" {
|
||||
// this isn't really a secret token: it only has permission to push new rollbar items
|
||||
hook := rollrus.NewHook("23432119147a4367abf7c0de2aa99a2d", "production")
|
||||
log.Hooks.Add(hook)
|
||||
}
|
||||
return log
|
||||
}
|
||||
|
||||
func newDevelopmentLogger() *logrus.Logger {
|
||||
log := logrus.New()
|
||||
file, err := os.OpenFile("development.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
panic("unable to log to file") // TODO: don't panic (also, remove this call to the `panic` function)
|
||||
@ -38,6 +46,21 @@ func newLogger(config config.AppConfigurer) *logrus.Logger {
|
||||
return log
|
||||
}
|
||||
|
||||
func newLogger(config config.AppConfigurer) *logrus.Entry {
|
||||
var log *logrus.Logger
|
||||
if config.GetDebug() {
|
||||
log = newDevelopmentLogger()
|
||||
} else {
|
||||
log = newProductionLogger(config)
|
||||
}
|
||||
return log.WithFields(logrus.Fields{
|
||||
"debug": config.GetDebug(),
|
||||
"version": config.GetVersion(),
|
||||
"commit": config.GetCommit(),
|
||||
"buildDate": config.GetBuildDate(),
|
||||
})
|
||||
}
|
||||
|
||||
// NewApp retruns a new applications
|
||||
func NewApp(config config.AppConfigurer) (*App, error) {
|
||||
app := &App{
|
||||
|
@ -7,23 +7,23 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
gitconfig "github.com/tcnksm/go-gitconfig"
|
||||
gogit "gopkg.in/src-d/go-git.v4"
|
||||
)
|
||||
|
||||
// GitCommand is our main git interface
|
||||
type GitCommand struct {
|
||||
Log *logrus.Logger
|
||||
Log *logrus.Entry
|
||||
OSCommand *OSCommand
|
||||
Worktree *gogit.Worktree
|
||||
Repo *gogit.Repository
|
||||
}
|
||||
|
||||
// NewGitCommand it runs git commands
|
||||
func NewGitCommand(log *logrus.Logger, osCommand *OSCommand) (*GitCommand, error) {
|
||||
func NewGitCommand(log *logrus.Entry, osCommand *OSCommand) (*GitCommand, error) {
|
||||
gitCommand := &GitCommand{
|
||||
Log: log,
|
||||
OSCommand: osCommand,
|
||||
|
@ -5,14 +5,14 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/jesseduffield/lazygit/pkg/test"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func getDummyLog() *logrus.Logger {
|
||||
func getDummyLog() *logrus.Entry {
|
||||
log := logrus.New()
|
||||
log.Out = ioutil.Discard
|
||||
return log
|
||||
return log.WithField("test", "test")
|
||||
}
|
||||
|
||||
func getDummyOSCommand() *OSCommand {
|
||||
|
@ -25,12 +25,12 @@ type Platform struct {
|
||||
|
||||
// OSCommand holds all the os commands
|
||||
type OSCommand struct {
|
||||
Log *logrus.Logger
|
||||
Log *logrus.Entry
|
||||
Platform *Platform
|
||||
}
|
||||
|
||||
// NewOSCommand os command runner
|
||||
func NewOSCommand(log *logrus.Logger) (*OSCommand, error) {
|
||||
func NewOSCommand(log *logrus.Entry) (*OSCommand, error) {
|
||||
osCommand := &OSCommand{
|
||||
Log: log,
|
||||
Platform: getPlatform(),
|
||||
|
@ -141,22 +141,22 @@ func (c *AppConfig) InsertToUserConfig(key, value string) error {
|
||||
|
||||
func getDefaultConfig() []byte {
|
||||
return []byte(`
|
||||
gui:
|
||||
## stuff relating to the UI
|
||||
scrollHeight: 2
|
||||
theme:
|
||||
activeBorderColor:
|
||||
- white
|
||||
- bold
|
||||
inactiveBorderColor:
|
||||
- white
|
||||
optionsTextColor:
|
||||
- blue
|
||||
git:
|
||||
# stuff relating to git
|
||||
os:
|
||||
# stuff relating to the OS
|
||||
|
||||
gui:
|
||||
## stuff relating to the UI
|
||||
scrollHeight: 2
|
||||
theme:
|
||||
activeBorderColor:
|
||||
- white
|
||||
- bold
|
||||
inactiveBorderColor:
|
||||
- white
|
||||
optionsTextColor:
|
||||
- blue
|
||||
git:
|
||||
# stuff relating to git
|
||||
os:
|
||||
# stuff relating to the OS
|
||||
reporting: 'undetermined' # one of: 'on' | 'off' | 'undetermined'
|
||||
`)
|
||||
}
|
||||
|
||||
|
@ -22,12 +22,12 @@ import (
|
||||
|
||||
// BranchListBuilder returns a list of Branch objects for the current repo
|
||||
type BranchListBuilder struct {
|
||||
Log *logrus.Logger
|
||||
Log *logrus.Entry
|
||||
GitCommand *commands.GitCommand
|
||||
}
|
||||
|
||||
// NewBranchListBuilder builds a new branch list builder
|
||||
func NewBranchListBuilder(log *logrus.Logger, gitCommand *commands.GitCommand) (*BranchListBuilder, error) {
|
||||
func NewBranchListBuilder(log *logrus.Entry, gitCommand *commands.GitCommand) (*BranchListBuilder, error) {
|
||||
return &BranchListBuilder{
|
||||
Log: log,
|
||||
GitCommand: gitCommand,
|
||||
|
@ -15,12 +15,12 @@ import (
|
||||
|
||||
// "strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/golang-collections/collections/stack"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// OverlappingEdges determines if panel edges overlap
|
||||
@ -56,7 +56,7 @@ type Teml i18n.Teml
|
||||
// Gui wraps the gocui Gui object which handles rendering and events
|
||||
type Gui struct {
|
||||
g *gocui.Gui
|
||||
Log *logrus.Logger
|
||||
Log *logrus.Entry
|
||||
GitCommand *commands.GitCommand
|
||||
OSCommand *commands.OSCommand
|
||||
SubProcess *exec.Cmd
|
||||
@ -81,7 +81,7 @@ type guiState struct {
|
||||
}
|
||||
|
||||
// NewGui builds a new gui handler
|
||||
func NewGui(log *logrus.Logger, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer) (*Gui, error) {
|
||||
func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer) (*Gui, error) {
|
||||
initialState := guiState{
|
||||
Files: make([]commands.File, 0),
|
||||
PreviousView: "files",
|
||||
@ -270,6 +270,12 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
if err := gui.switchFocus(g, nil, filesView); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if gui.Config.GetUserConfig().GetString("reporting") == "undetermined" {
|
||||
if err := gui.promptAnonymousReporting(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gui.resizePopupPanels(g)
|
||||
@ -277,6 +283,14 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) promptAnonymousReporting() error {
|
||||
return gui.createConfirmationPanel(gui.g, nil, gui.Tr.SLocalize("AnonymousReportingTitle"), gui.Tr.SLocalize("AnonymousReportingPrompt"), func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.Config.InsertToUserConfig("reporting", "on")
|
||||
}, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.Config.InsertToUserConfig("reporting", "off")
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) fetch(g *gocui.Gui) error {
|
||||
gui.GitCommand.Fetch()
|
||||
gui.refreshStatus(g)
|
||||
|
@ -312,6 +312,12 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
||||
}, &i18n.Message{
|
||||
ID: "ForcePushPrompt",
|
||||
Other: "Your branch has diverged from the remote branch. Press 'esc' to cancel, or 'enter' to force push.",
|
||||
}, &i18n.Message{
|
||||
ID: "AnonymousReportingTitle",
|
||||
Other: "Help make lazygit better",
|
||||
}, &i18n.Message{
|
||||
ID: "AnonymousReportingPrompt",
|
||||
Other: "Would you like to enable anonymous reporting data to help improve lazygit? (enter/esc)",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/cloudfoundry/jibber_jabber"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
@ -14,11 +14,11 @@ type Teml map[string]interface{}
|
||||
type Localizer struct {
|
||||
i18nLocalizer *i18n.Localizer
|
||||
language string
|
||||
Log *logrus.Logger
|
||||
Log *logrus.Entry
|
||||
}
|
||||
|
||||
// NewLocalizer creates a new Localizer
|
||||
func NewLocalizer(log *logrus.Logger) *Localizer {
|
||||
func NewLocalizer(log *logrus.Entry) *Localizer {
|
||||
userLang := detectLanguage(jibber_jabber.DetectLanguage)
|
||||
|
||||
log.Info("language: " + userLang)
|
||||
@ -60,7 +60,7 @@ func (l *Localizer) GetLanguage() string {
|
||||
}
|
||||
|
||||
// add translation file(s)
|
||||
func addBundles(log *logrus.Logger, i18nBundle *i18n.Bundle) {
|
||||
func addBundles(log *logrus.Entry, i18nBundle *i18n.Bundle) {
|
||||
fs := []func(*i18n.Bundle) error{
|
||||
addPolish,
|
||||
addDutch,
|
||||
@ -85,7 +85,7 @@ func detectLanguage(langDetector func() (string, error)) string {
|
||||
}
|
||||
|
||||
// setupLocalizer creates a new localizer using given userLang
|
||||
func setupLocalizer(log *logrus.Logger, userLang string) *Localizer {
|
||||
func setupLocalizer(log *logrus.Entry, userLang string) *Localizer {
|
||||
// create a i18n bundle that can be used to add translations and other things
|
||||
i18nBundle := &i18n.Bundle{DefaultLanguage: language.English}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package i18n
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
@ -10,8 +11,13 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getDummyLog() *logrus.Entry {
|
||||
log := logrus.New()
|
||||
log.Out = ioutil.Discard
|
||||
return log.WithField("test", "test")
|
||||
}
|
||||
func TestNewLocalizer(t *testing.T) {
|
||||
assert.NotNil(t, NewLocalizer(logrus.New()))
|
||||
assert.NotNil(t, NewLocalizer(getDummyLog()))
|
||||
}
|
||||
|
||||
func TestDetectLanguage(t *testing.T) {
|
||||
@ -76,6 +82,6 @@ func TestLocalizer(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s.test(setupLocalizer(logrus.New(), s.userLang))
|
||||
s.test(setupLocalizer(getDummyLog(), s.userLang))
|
||||
}
|
||||
}
|
||||
|
22
vendor/github.com/heroku/rollrus/LICENSE
generated
vendored
Normal file
22
vendor/github.com/heroku/rollrus/LICENSE
generated
vendored
Normal file
@ -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.
|
287
vendor/github.com/heroku/rollrus/rollrus.go
generated
vendored
Normal file
287
vendor/github.com/heroku/rollrus/rollrus.go
generated
vendored
Normal file
@ -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
|
||||
}
|
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
Normal file
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
|
||||
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.
|
269
vendor/github.com/pkg/errors/errors.go
generated
vendored
Normal file
269
vendor/github.com/pkg/errors/errors.go
generated
vendored
Normal file
@ -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
|
||||
}
|
178
vendor/github.com/pkg/errors/stack.go
generated
vendored
Normal file
178
vendor/github.com/pkg/errors/stack.go
generated
vendored
Normal file
@ -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
|
||||
}
|
22
vendor/github.com/stvp/roll/LICENSE
generated
vendored
Normal file
22
vendor/github.com/stvp/roll/LICENSE
generated
vendored
Normal file
@ -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.
|
240
vendor/github.com/stvp/roll/client.go
generated
vendored
Normal file
240
vendor/github.com/stvp/roll/client.go
generated
vendored
Normal file
@ -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, "*")
|
||||
}
|
||||
}
|
98
vendor/github.com/stvp/roll/stack.go
generated
vendored
Normal file
98
vendor/github.com/stvp/roll/stack.go
generated
vendored
Normal file
@ -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)]
|
||||
}
|
Loading…
Reference in New Issue
Block a user