pax_global_header00006660000000000000000000000064135303120460014507gustar00rootroot0000000000000052 comment=e4f98d5557013c6549e00e95d9d9e9b143ba7fa1 golang-github-jesseduffield-roll-0.0~git20190629.695be2e/000077500000000000000000000000001353031204600224745ustar00rootroot00000000000000golang-github-jesseduffield-roll-0.0~git20190629.695be2e/.gitignore000066400000000000000000000000151353031204600244600ustar00rootroot00000000000000rollbar.test golang-github-jesseduffield-roll-0.0~git20190629.695be2e/CHANGELOG000066400000000000000000000011201353031204600237000ustar00rootroot000000000000000.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 golang-github-jesseduffield-roll-0.0~git20190629.695be2e/LICENSE000066400000000000000000000020621353031204600235010ustar00rootroot00000000000000Copyright (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. golang-github-jesseduffield-roll-0.0~git20190629.695be2e/README.md000066400000000000000000000031061353031204600237530ustar00rootroot00000000000000roll ---- `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 golang-github-jesseduffield-roll-0.0~git20190629.695be2e/client.go000066400000000000000000000161561353031204600243120ustar00rootroot00000000000000package 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, "*") } } golang-github-jesseduffield-roll-0.0~git20190629.695be2e/client_test.go000066400000000000000000000116151353031204600253440ustar00rootroot00000000000000package roll import ( "errors" "fmt" "net/http" "net/http/httptest" "os" "testing" ) // -- Test helpers type CustomError struct { s string } func (e *CustomError) Error() string { return e.s } func setup() func() { Token = os.Getenv("TOKEN") Environment = "test" if Token == "" { Token = "test" originalEndpoint := Endpoint server := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(`{"result": {"uuid": "01234567890123456789012345678901"}}`)) }, )) Endpoint = server.URL return func() { Endpoint = originalEndpoint Token = "" server.Close() } } // Assume Token was provided and we want integration tests. return func() {} } // -- Tests func TestErrorClass(t *testing.T) { errors := map[string]error{ "{508e076d}": fmt.Errorf("Something is broken!"), "roll.CustomError": &CustomError{"Terrible mistakes were made."}, } for expected, err := range errors { if errorClass(err) != expected { t.Error("Got:", errorClass(err), "Expected:", expected) } } } func TestCritical(t *testing.T) { teardown := setup() defer teardown() uuid, err := Critical(errors.New("global critical"), map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestError(t *testing.T) { teardown := setup() defer teardown() uuid, err := Error(errors.New("global error"), map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestWarning(t *testing.T) { teardown := setup() defer teardown() uuid, err := Warning(errors.New("global warning"), map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestInfo(t *testing.T) { teardown := setup() defer teardown() uuid, err := Info("global info", map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestDebug(t *testing.T) { teardown := setup() defer teardown() uuid, err := Debug("global debug", map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestRollbarClientCritical(t *testing.T) { teardown := setup() defer teardown() client := New(Token, "test") uuid, err := client.Critical(errors.New("new client critical"), map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestRollbarClientError(t *testing.T) { teardown := setup() defer teardown() client := New(Token, "test") uuid, err := client.Error(errors.New("new client error"), map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestRollbarClientWarning(t *testing.T) { teardown := setup() defer teardown() client := New(Token, "test") uuid, err := client.Warning(errors.New("new client warning"), map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestRollbarClientInfo(t *testing.T) { teardown := setup() defer teardown() client := New(Token, "test") uuid, err := client.Info("new client info", map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestRollbarClientDebug(t *testing.T) { teardown := setup() defer teardown() client := New(Token, "test") uuid, err := client.Debug("new client debug", map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestRollbarClientCriticalStack(t *testing.T) { teardown := setup() defer teardown() client := New(Token, "test") uuid, err := client.CriticalStack(errors.New("new client critical"), getCallers(0), map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestRollbarClientErrorStack(t *testing.T) { teardown := setup() defer teardown() client := New(Token, "test") uuid, err := client.ErrorStack(errors.New("new client error"), getCallers(0), map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } func TestRollbarClientWarningStack(t *testing.T) { teardown := setup() defer teardown() client := New(Token, "test") uuid, err := client.WarningStack(errors.New("new client warning"), getCallers(0), map[string]string{"extras": "true"}) if err != nil { t.Error(err) } if len(uuid) != 32 { t.Errorf("expected UUID, got: %#v", uuid) } } golang-github-jesseduffield-roll-0.0~git20190629.695be2e/stack.go000066400000000000000000000045771353031204600241450ustar00rootroot00000000000000package 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)] } golang-github-jesseduffield-roll-0.0~git20190629.695be2e/stack_test.go000066400000000000000000000037171353031204600251770ustar00rootroot00000000000000package roll import ( "strings" "testing" ) func TestBuildRollbarFrames(t *testing.T) { frames := buildRollbarFrames(getCallers(0)) if len(frames) != 4 { t.Fatalf("expected 4 frames, got %d", len(frames)) } if !strings.Contains(frames[0].Filename, "github.com/stvp/roll/stack.go") { t.Errorf("expected %#v, got %#v", "github.com/stvp/roll/stack.go", frames[0].Filename) } if !strings.Contains(frames[0].Method, "roll.getCallers") { t.Errorf("expected %#v, got %#v", "roll.getCallers", frames[0].Method) } } func TestRollbarFramesFingerprint(t *testing.T) { tests := []struct { Fingerprint string Title string Frames rollbarFrames }{ { "9344290d", "broken", rollbarFrames{ {"foo.go", "Oops", 1}, }, }, { "9344290d", "very broken", rollbarFrames{ {"foo.go", "Oops", 1}, }, }, { "a4d78b7", "broken", rollbarFrames{ {"foo.go", "Oops", 2}, }, }, { "50e0fcb3", "broken", rollbarFrames{ {"foo.go", "Oops", 1}, {"foo.go", "Oops", 2}, }, }, } for i, test := range tests { fp := test.Frames.fingerprint() if fp != test.Fingerprint { t.Errorf("tests[%d]: got %s", i, fp) } } } func TestScrubFile(t *testing.T) { tests := []struct { Given string Expected string }{ {"", ""}, {"foo.go", "foo.go"}, {"/home/foo/go/src/github.com/stvp/rollbar.go", "github.com/stvp/rollbar.go"}, {"/home/foo/go/src/gopkg.in/yaml.v1/encode.go", "gopkg.in/yaml.v1/encode.go"}, } for i, test := range tests { got := scrubFile(test.Given) if got != test.Expected { t.Errorf("tests[%d]: got %s", i, got) } } } func TestScrubFunction(t *testing.T) { tests := []struct { Given string Expected string }{ {"", ""}, {"roll.getCallers", "roll.getCallers"}, {"github.com/stvp/roll.getCallers", "roll.getCallers"}, } for i, test := range tests { got := scrubFunction(test.Given) if got != test.Expected { t.Errorf("tests[%d]: got %s", i, got) } } }