remove rollbar
parent
fa6460b8ab
commit
6279b416bc
@ -1 +0,0 @@
|
||||
rollbar.test
|
@ -1,24 +0,0 @@
|
||||
0.2.0 - May 22nd, 2016
|
||||
====================
|
||||
|
||||
* Do not use title to determine fingerprint.
|
||||
|
||||
0.1.1 - August 24th, 2016
|
||||
=========================
|
||||
|
||||
* Fix Go 1.6 support by removing call to runtime.CallersFrames, which was added
|
||||
in Go 1.7.
|
||||
|
||||
0.1.0 - August 23rd, 2016
|
||||
=========================
|
||||
|
||||
* Allow passing in arbitrary function pointer stacks. (thanks @apg!)
|
||||
* Remove unneeded exported constants.
|
||||
* Make HTTP(S) endpoint configurable.
|
||||
* Remove unneeded debug print statement.
|
||||
|
||||
0.0.1 - January 19th, 2015
|
||||
==========================
|
||||
|
||||
* Initial release based on https://github.com/stvp/rollbar
|
||||
|
@ -1,22 +0,0 @@
|
||||
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.
|
@ -1,53 +0,0 @@
|
||||
roll
|
||||
----
|
||||
|
||||
`roll` is a basic Rollbar client for Go that reports errors and logs
|
||||
messages. It automatically builds stack traces and also supports
|
||||
arbitrary traces. All errors and messages are sent to Rollbar
|
||||
synchronously.
|
||||
|
||||
`roll` is intentionally simple. For more advanced functionality, check
|
||||
out [heroku/rollbar](https://github.com/heroku/rollbar).
|
||||
|
||||
[API docs on godoc.org](http://godoc.org/github.com/stvp/roll)
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
* Critical-, Error-, and Warning-level messages include a stack trace.
|
||||
However, Go's `error` type doesn't include stack information from the
|
||||
location the error was set or allocated. Instead, `roll` uses the
|
||||
stack information from where the error was reported.
|
||||
* Info- and Debug-level Rollbar messages do not include stack traces.
|
||||
* When calling `roll` away from where the error actually occurred,
|
||||
`roll`'s stack walking won't represent the actual stack trace at the
|
||||
time the error occurred. The `*Stack` variants of Critical, Error, and
|
||||
Warning take a `[]uintptr`, allowing the stack to be provided, rather
|
||||
than walked.
|
||||
|
||||
Running Tests
|
||||
=============
|
||||
|
||||
`go test` will run tests against a fake server by default.
|
||||
|
||||
If the environment variable `TOKEN` is a Rollbar access token, running
|
||||
`go test` will produce errors using an environment named `test`.
|
||||
|
||||
TOKEN=f0df01587b8f76b2c217af34c479f9ea go test
|
||||
|
||||
Verify the reported errors manually in the Rollbar dashboard.
|
||||
|
||||
Contributors
|
||||
============
|
||||
|
||||
* @challiwill
|
||||
* @tysonmote
|
||||
* @apg
|
||||
|
||||
This library was forked from [stvp/rollbar](https://github.com/stvp/rollbar),
|
||||
which had contributions from:
|
||||
|
||||
* @kjk
|
||||
* @Soulou
|
||||
* @paulmach
|
||||
|
@ -1,242 +0,0 @@
|
||||
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 {
|
||||
// If something goes wrong it really does not matter
|
||||
return "", nil
|
||||
}
|
||||
defer func() {
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
// If something goes wrong it really does not matter
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// 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, "*")
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
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)]
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.10.8
|
||||
- 1.11.6
|
||||
- 1.12.1
|
||||
script:
|
||||
- go test -v -race ./...
|
@ -1,22 +0,0 @@
|
||||
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.
|
@ -1,15 +0,0 @@
|
||||
[![Build Status](https://travis-ci.org/heroku/rollrus.svg?branch=master)](https://travis-ci.org/heroku/rollrus) [![GoDoc](https://godoc.org/github.com/heroku/rollrus?status.svg)](https://godoc.org/github.com/heroku/rollrus)
|
||||
|
||||
# What
|
||||
|
||||
Rollrus is what happens when [Logrus](https://github.com/sirupsen/logrus) meets [Roll](https://github.com/stvp/roll).
|
||||
|
||||
When a .Error, .Fatal or .Panic logging function is called, report the details to rollbar via a Logrus hook.
|
||||
|
||||
Delivery is synchronous to help ensure that logs are delivered.
|
||||
|
||||
If the error includes a [`StackTrace`](https://godoc.org/github.com/pkg/errors#StackTrace), that `StackTrace` is reported to rollbar.
|
||||
|
||||
# Usage
|
||||
|
||||
Examples available in the [tests](https://github.com/heroku/rollrus/blob/master/rollrus_test.go) or on [GoDoc](https://godoc.org/github.com/heroku/rollrus).
|
@ -1,7 +0,0 @@
|
||||
module github.com/jesseduffield/rollrus
|
||||
|
||||
require (
|
||||
github.com/jesseduffield/roll v0.0.0-20190629104057-695be2e62b00
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/sirupsen/logrus v1.3.0
|
||||
)
|
@ -1,19 +0,0 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/jesseduffield/roll v0.0.0-20190629104057-695be2e62b00 h1:+JaOkfBNYQYlGD7dgru8mCwYNEc5tRRI8mThlVANhSM=
|
||||
github.com/jesseduffield/roll v0.0.0-20190629104057-695be2e62b00/go.mod h1:cWNQljQAWYBp4wchyGfql4q2jRNZXxiE1KhVQgz+JaM=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
@ -1,287 +0,0 @@
|
||||
// Package rollrus combines github.com/jesseduffield/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/jesseduffield/roll"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
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 []error
|
||||
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. See https://golang.org/ref/spec#Comparison_operators
|
||||
func WithIgnoredErrors(errors ...error) OptionFunc {
|
||||
return func(h *Hook) {
|
||||
h.ignoredErrors = append(h.ignoredErrors, errors...)
|
||||
}
|
||||
}
|
||||
|
||||
// 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([]error, 0),
|
||||
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)
|
||||
for _, ie := range r.ignoredErrors {
|
||||
if ie == cause {
|
||||
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
|
||||
}
|
Loading…
Reference in New Issue