mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-01-22 05:29:44 +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"
|
pruneopts = "NUT"
|
||||||
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:d457d39e88f678ed14ac29517c3d74927a48dbc6a9f073fa241cf364a68cbe5c"
|
||||||
|
name = "github.com/heroku/rollrus"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "fc0cef2ff331aebb24cd4e9ded7e20650f3d7006"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:62fe3a7ea2050ecbd753a71889026f83d73329337ada66325cbafd5dea5f713d"
|
digest = "1:62fe3a7ea2050ecbd753a71889026f83d73329337ada66325cbafd5dea5f713d"
|
||||||
@ -191,6 +199,14 @@
|
|||||||
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
|
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
|
||||||
version = "v1.2.0"
|
version = "v1.2.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121"
|
||||||
|
name = "github.com/pkg/errors"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||||
|
version = "v0.8.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
|
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
|
||||||
name = "github.com/pmezard/go-difflib"
|
name = "github.com/pmezard/go-difflib"
|
||||||
@ -294,6 +310,14 @@
|
|||||||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
||||||
version = "v1.2.2"
|
version = "v1.2.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:e42372d3f4921ec35df07f9b23239631e9d28580f7c1edcca212bc6daddc68fe"
|
||||||
|
name = "github.com/stvp/roll"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "3627a5cbeaeaa68023abd02bb8687925265f2f63"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:cd5ffc5bda4e0296ab3e4de90dbb415259c78e45e7fab13694b14cde8ab74541"
|
digest = "1:cd5ffc5bda4e0296ab3e4de90dbb415259c78e45e7fab13694b14cde8ab74541"
|
||||||
name = "github.com/tcnksm/go-gitconfig"
|
name = "github.com/tcnksm/go-gitconfig"
|
||||||
@ -458,6 +482,7 @@
|
|||||||
"github.com/davecgh/go-spew/spew",
|
"github.com/davecgh/go-spew/spew",
|
||||||
"github.com/fatih/color",
|
"github.com/fatih/color",
|
||||||
"github.com/golang-collections/collections/stack",
|
"github.com/golang-collections/collections/stack",
|
||||||
|
"github.com/heroku/rollrus",
|
||||||
"github.com/jesseduffield/gocui",
|
"github.com/jesseduffield/gocui",
|
||||||
"github.com/mgutz/str",
|
"github.com/mgutz/str",
|
||||||
"github.com/nicksnyder/go-i18n/v2/i18n",
|
"github.com/nicksnyder/go-i18n/v2/i18n",
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
# stuff relating to git
|
# stuff relating to git
|
||||||
os:
|
os:
|
||||||
# stuff relating to the OS
|
# stuff relating to the OS
|
||||||
|
reporting: 'undetermined' # one of: 'on' | 'off' | 'undetermined'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Color Attributes:
|
## Color Attributes:
|
||||||
|
@ -5,11 +5,12 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/heroku/rollrus"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// App struct
|
// App struct
|
||||||
@ -17,19 +18,26 @@ type App struct {
|
|||||||
closers []io.Closer
|
closers []io.Closer
|
||||||
|
|
||||||
Config config.AppConfigurer
|
Config config.AppConfigurer
|
||||||
Log *logrus.Logger
|
Log *logrus.Entry
|
||||||
OSCommand *commands.OSCommand
|
OSCommand *commands.OSCommand
|
||||||
GitCommand *commands.GitCommand
|
GitCommand *commands.GitCommand
|
||||||
Gui *gui.Gui
|
Gui *gui.Gui
|
||||||
Tr *i18n.Localizer
|
Tr *i18n.Localizer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLogger(config config.AppConfigurer) *logrus.Logger {
|
func newProductionLogger(config config.AppConfigurer) *logrus.Logger {
|
||||||
log := logrus.New()
|
log := logrus.New()
|
||||||
if !config.GetDebug() {
|
|
||||||
log.Out = ioutil.Discard
|
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
|
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)
|
file, err := os.OpenFile("development.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("unable to log to file") // TODO: don't panic (also, remove this call to the `panic` function)
|
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
|
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
|
// NewApp retruns a new applications
|
||||||
func NewApp(config config.AppConfigurer) (*App, error) {
|
func NewApp(config config.AppConfigurer) (*App, error) {
|
||||||
app := &App{
|
app := &App{
|
||||||
|
@ -7,23 +7,23 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
gitconfig "github.com/tcnksm/go-gitconfig"
|
gitconfig "github.com/tcnksm/go-gitconfig"
|
||||||
gogit "gopkg.in/src-d/go-git.v4"
|
gogit "gopkg.in/src-d/go-git.v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GitCommand is our main git interface
|
// GitCommand is our main git interface
|
||||||
type GitCommand struct {
|
type GitCommand struct {
|
||||||
Log *logrus.Logger
|
Log *logrus.Entry
|
||||||
OSCommand *OSCommand
|
OSCommand *OSCommand
|
||||||
Worktree *gogit.Worktree
|
Worktree *gogit.Worktree
|
||||||
Repo *gogit.Repository
|
Repo *gogit.Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGitCommand it runs git commands
|
// 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{
|
gitCommand := &GitCommand{
|
||||||
Log: log,
|
Log: log,
|
||||||
OSCommand: osCommand,
|
OSCommand: osCommand,
|
||||||
|
@ -5,14 +5,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/test"
|
"github.com/jesseduffield/lazygit/pkg/test"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getDummyLog() *logrus.Logger {
|
func getDummyLog() *logrus.Entry {
|
||||||
log := logrus.New()
|
log := logrus.New()
|
||||||
log.Out = ioutil.Discard
|
log.Out = ioutil.Discard
|
||||||
return log
|
return log.WithField("test", "test")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDummyOSCommand() *OSCommand {
|
func getDummyOSCommand() *OSCommand {
|
||||||
|
@ -25,12 +25,12 @@ type Platform struct {
|
|||||||
|
|
||||||
// OSCommand holds all the os commands
|
// OSCommand holds all the os commands
|
||||||
type OSCommand struct {
|
type OSCommand struct {
|
||||||
Log *logrus.Logger
|
Log *logrus.Entry
|
||||||
Platform *Platform
|
Platform *Platform
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOSCommand os command runner
|
// NewOSCommand os command runner
|
||||||
func NewOSCommand(log *logrus.Logger) (*OSCommand, error) {
|
func NewOSCommand(log *logrus.Entry) (*OSCommand, error) {
|
||||||
osCommand := &OSCommand{
|
osCommand := &OSCommand{
|
||||||
Log: log,
|
Log: log,
|
||||||
Platform: getPlatform(),
|
Platform: getPlatform(),
|
||||||
|
@ -156,7 +156,7 @@ func getDefaultConfig() []byte {
|
|||||||
# stuff relating to git
|
# stuff relating to git
|
||||||
os:
|
os:
|
||||||
# stuff relating to the 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
|
// BranchListBuilder returns a list of Branch objects for the current repo
|
||||||
type BranchListBuilder struct {
|
type BranchListBuilder struct {
|
||||||
Log *logrus.Logger
|
Log *logrus.Entry
|
||||||
GitCommand *commands.GitCommand
|
GitCommand *commands.GitCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBranchListBuilder builds a new branch list builder
|
// 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{
|
return &BranchListBuilder{
|
||||||
Log: log,
|
Log: log,
|
||||||
GitCommand: gitCommand,
|
GitCommand: gitCommand,
|
||||||
|
@ -15,12 +15,12 @@ import (
|
|||||||
|
|
||||||
// "strings"
|
// "strings"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/golang-collections/collections/stack"
|
"github.com/golang-collections/collections/stack"
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OverlappingEdges determines if panel edges overlap
|
// 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
|
// Gui wraps the gocui Gui object which handles rendering and events
|
||||||
type Gui struct {
|
type Gui struct {
|
||||||
g *gocui.Gui
|
g *gocui.Gui
|
||||||
Log *logrus.Logger
|
Log *logrus.Entry
|
||||||
GitCommand *commands.GitCommand
|
GitCommand *commands.GitCommand
|
||||||
OSCommand *commands.OSCommand
|
OSCommand *commands.OSCommand
|
||||||
SubProcess *exec.Cmd
|
SubProcess *exec.Cmd
|
||||||
@ -81,7 +81,7 @@ type guiState struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewGui builds a new gui handler
|
// 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{
|
initialState := guiState{
|
||||||
Files: make([]commands.File, 0),
|
Files: make([]commands.File, 0),
|
||||||
PreviousView: "files",
|
PreviousView: "files",
|
||||||
@ -270,6 +270,12 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
if err := gui.switchFocus(g, nil, filesView); err != nil {
|
if err := gui.switchFocus(g, nil, filesView); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if gui.Config.GetUserConfig().GetString("reporting") == "undetermined" {
|
||||||
|
if err := gui.promptAnonymousReporting(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.resizePopupPanels(g)
|
gui.resizePopupPanels(g)
|
||||||
@ -277,6 +283,14 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
return nil
|
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 {
|
func (gui *Gui) fetch(g *gocui.Gui) error {
|
||||||
gui.GitCommand.Fetch()
|
gui.GitCommand.Fetch()
|
||||||
gui.refreshStatus(g)
|
gui.refreshStatus(g)
|
||||||
|
@ -312,6 +312,12 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "ForcePushPrompt",
|
ID: "ForcePushPrompt",
|
||||||
Other: "Your branch has diverged from the remote branch. Press 'esc' to cancel, or 'enter' to force push.",
|
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
|
package i18n
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/cloudfoundry/jibber_jabber"
|
"github.com/cloudfoundry/jibber_jabber"
|
||||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,11 +14,11 @@ type Teml map[string]interface{}
|
|||||||
type Localizer struct {
|
type Localizer struct {
|
||||||
i18nLocalizer *i18n.Localizer
|
i18nLocalizer *i18n.Localizer
|
||||||
language string
|
language string
|
||||||
Log *logrus.Logger
|
Log *logrus.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLocalizer creates a new Localizer
|
// NewLocalizer creates a new Localizer
|
||||||
func NewLocalizer(log *logrus.Logger) *Localizer {
|
func NewLocalizer(log *logrus.Entry) *Localizer {
|
||||||
userLang := detectLanguage(jibber_jabber.DetectLanguage)
|
userLang := detectLanguage(jibber_jabber.DetectLanguage)
|
||||||
|
|
||||||
log.Info("language: " + userLang)
|
log.Info("language: " + userLang)
|
||||||
@ -60,7 +60,7 @@ func (l *Localizer) GetLanguage() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add translation file(s)
|
// 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{
|
fs := []func(*i18n.Bundle) error{
|
||||||
addPolish,
|
addPolish,
|
||||||
addDutch,
|
addDutch,
|
||||||
@ -85,7 +85,7 @@ func detectLanguage(langDetector func() (string, error)) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setupLocalizer creates a new localizer using given userLang
|
// 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
|
// create a i18n bundle that can be used to add translations and other things
|
||||||
i18nBundle := &i18n.Bundle{DefaultLanguage: language.English}
|
i18nBundle := &i18n.Bundle{DefaultLanguage: language.English}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package i18n
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
@ -10,8 +11,13 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"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) {
|
func TestNewLocalizer(t *testing.T) {
|
||||||
assert.NotNil(t, NewLocalizer(logrus.New()))
|
assert.NotNil(t, NewLocalizer(getDummyLog()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDetectLanguage(t *testing.T) {
|
func TestDetectLanguage(t *testing.T) {
|
||||||
@ -76,6 +82,6 @@ func TestLocalizer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range scenarios {
|
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…
x
Reference in New Issue
Block a user