Add logrus, prometheus client and matrix-org/util
This commit is contained in:
parent
41c6a3737e
commit
63d1bcd66a
313 changed files with 53958 additions and 1 deletions
27
vendor/src/gopkg.in/airbrake/gobrake.v2/LICENSE
vendored
Normal file
27
vendor/src/gopkg.in/airbrake/gobrake.v2/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2014 The Gobrake Authors. 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.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
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
|
||||
OWNER 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.
|
||||
4
vendor/src/gopkg.in/airbrake/gobrake.v2/Makefile
vendored
Normal file
4
vendor/src/gopkg.in/airbrake/gobrake.v2/Makefile
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
all:
|
||||
go test ./...
|
||||
go test ./... -short -race
|
||||
go vet
|
||||
47
vendor/src/gopkg.in/airbrake/gobrake.v2/README.md
vendored
Normal file
47
vendor/src/gopkg.in/airbrake/gobrake.v2/README.md
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Airbrake Golang Notifier [](https://travis-ci.org/airbrake/gobrake)
|
||||
|
||||
<img src="http://f.cl.ly/items/3J3h1L05222X3o1w2l2L/golang.jpg" width=800px>
|
||||
|
||||
# Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"gopkg.in/airbrake/gobrake.v2"
|
||||
)
|
||||
|
||||
var airbrake = gobrake.NewNotifier(1234567, "FIXME")
|
||||
|
||||
func init() {
|
||||
airbrake.AddFilter(func(notice *gobrake.Notice) *gobrake.Notice {
|
||||
notice.Context["environment"] = "production"
|
||||
return notice
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer airbrake.Close()
|
||||
defer airbrake.NotifyOnPanic()
|
||||
|
||||
airbrake.Notify(errors.New("operation failed"), nil)
|
||||
}
|
||||
```
|
||||
|
||||
## Ignoring notices
|
||||
|
||||
```go
|
||||
airbrake.AddFilter(func(notice *gobrake.Notice) *gobrake.Notice {
|
||||
if notice.Context["environment"] == "development" {
|
||||
// Ignore notices in development environment.
|
||||
return nil
|
||||
}
|
||||
return notice
|
||||
})
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
You can use [glog fork](https://github.com/airbrake/glog) to send your logs to Airbrake.
|
||||
37
vendor/src/gopkg.in/airbrake/gobrake.v2/bench_test.go
vendored
Normal file
37
vendor/src/gopkg.in/airbrake/gobrake.v2/bench_test.go
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package gobrake_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/airbrake/gobrake.v2"
|
||||
)
|
||||
|
||||
func BenchmarkSendNotice(b *testing.B) {
|
||||
handler := func(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write([]byte(`{"id":"123"}`))
|
||||
}
|
||||
server := httptest.NewServer(http.HandlerFunc(handler))
|
||||
|
||||
notifier := gobrake.NewNotifier(1, "key")
|
||||
notifier.SetHost(server.URL)
|
||||
|
||||
notice := notifier.Notice(errors.New("benchmark"), nil, 0)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
id, err := notifier.SendNotice(notice)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if id != "123" {
|
||||
b.Fatalf("got %q, wanted 123", id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
16
vendor/src/gopkg.in/airbrake/gobrake.v2/gobrake.go
vendored
Normal file
16
vendor/src/gopkg.in/airbrake/gobrake.v2/gobrake.go
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package gobrake
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var logger *log.Logger
|
||||
|
||||
func init() {
|
||||
SetLogger(log.New(os.Stderr, "gobrake: ", log.LstdFlags))
|
||||
}
|
||||
|
||||
func SetLogger(l *log.Logger) {
|
||||
logger = l
|
||||
}
|
||||
105
vendor/src/gopkg.in/airbrake/gobrake.v2/notice.go
vendored
Normal file
105
vendor/src/gopkg.in/airbrake/gobrake.v2/notice.go
vendored
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
package gobrake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var defaultContext map[string]interface{}
|
||||
|
||||
func getDefaultContext() map[string]interface{} {
|
||||
if defaultContext != nil {
|
||||
return defaultContext
|
||||
}
|
||||
|
||||
defaultContext = map[string]interface{}{
|
||||
"notifier": map[string]interface{}{
|
||||
"name": "gobrake",
|
||||
"version": "2.0.4",
|
||||
"url": "https://github.com/airbrake/gobrake",
|
||||
},
|
||||
|
||||
"language": runtime.Version(),
|
||||
"os": runtime.GOOS,
|
||||
"architecture": runtime.GOARCH,
|
||||
}
|
||||
if s, err := os.Hostname(); err == nil {
|
||||
defaultContext["hostname"] = s
|
||||
}
|
||||
if s := os.Getenv("GOPATH"); s != "" {
|
||||
list := filepath.SplitList(s)
|
||||
// TODO: multiple root dirs?
|
||||
defaultContext["rootDirectory"] = list[0]
|
||||
}
|
||||
return defaultContext
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Type string `json:"type"`
|
||||
Message string `json:"message"`
|
||||
Backtrace []StackFrame `json:"backtrace"`
|
||||
}
|
||||
|
||||
type Notice struct {
|
||||
Errors []Error `json:"errors"`
|
||||
Context map[string]interface{} `json:"context"`
|
||||
Env map[string]interface{} `json:"environment"`
|
||||
Session map[string]interface{} `json:"session"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
}
|
||||
|
||||
func (n *Notice) String() string {
|
||||
if len(n.Errors) == 0 {
|
||||
return "Notice<no errors>"
|
||||
}
|
||||
e := n.Errors[0]
|
||||
return fmt.Sprintf("Notice<%s: %s>", e.Type, e.Message)
|
||||
}
|
||||
|
||||
func NewNotice(e interface{}, req *http.Request, depth int) *Notice {
|
||||
notice := &Notice{
|
||||
Errors: []Error{{
|
||||
Type: fmt.Sprintf("%T", e),
|
||||
Message: fmt.Sprint(e),
|
||||
Backtrace: stack(depth),
|
||||
}},
|
||||
Context: map[string]interface{}{},
|
||||
Env: map[string]interface{}{},
|
||||
Session: map[string]interface{}{},
|
||||
Params: map[string]interface{}{},
|
||||
}
|
||||
|
||||
for k, v := range getDefaultContext() {
|
||||
notice.Context[k] = v
|
||||
}
|
||||
|
||||
if req != nil {
|
||||
notice.Context["url"] = req.URL.String()
|
||||
if ua := req.Header.Get("User-Agent"); ua != "" {
|
||||
notice.Context["userAgent"] = ua
|
||||
}
|
||||
|
||||
for k, v := range req.Header {
|
||||
if len(v) == 1 {
|
||||
notice.Env[k] = v[0]
|
||||
} else {
|
||||
notice.Env[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if err := req.ParseForm(); err == nil {
|
||||
for k, v := range req.Form {
|
||||
if len(v) == 1 {
|
||||
notice.Params[k] = v[0]
|
||||
} else {
|
||||
notice.Params[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return notice
|
||||
}
|
||||
280
vendor/src/gopkg.in/airbrake/gobrake.v2/notifier.go
vendored
Normal file
280
vendor/src/gopkg.in/airbrake/gobrake.v2/notifier.go
vendored
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
package gobrake // import "gopkg.in/airbrake/gobrake.v2"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const defaultAirbrakeHost = "https://airbrake.io"
|
||||
const waitTimeout = 5 * time.Second
|
||||
const httpStatusTooManyRequests = 429
|
||||
|
||||
var (
|
||||
errClosed = errors.New("gobrake: notifier is closed")
|
||||
errRateLimited = errors.New("gobrake: rate limited")
|
||||
)
|
||||
|
||||
var httpClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 15 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
ClientSessionCache: tls.NewLRUClientSessionCache(1024),
|
||||
},
|
||||
MaxIdleConnsPerHost: 10,
|
||||
ResponseHeaderTimeout: 10 * time.Second,
|
||||
},
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
var buffers = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
|
||||
type filter func(*Notice) *Notice
|
||||
|
||||
type Notifier struct {
|
||||
// http.Client that is used to interact with Airbrake API.
|
||||
Client *http.Client
|
||||
|
||||
projectId int64
|
||||
projectKey string
|
||||
createNoticeURL string
|
||||
|
||||
filters []filter
|
||||
|
||||
wg sync.WaitGroup
|
||||
noticeCh chan *Notice
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
func NewNotifier(projectId int64, projectKey string) *Notifier {
|
||||
n := &Notifier{
|
||||
Client: httpClient,
|
||||
|
||||
projectId: projectId,
|
||||
projectKey: projectKey,
|
||||
createNoticeURL: getCreateNoticeURL(defaultAirbrakeHost, projectId, projectKey),
|
||||
|
||||
filters: []filter{noticeBacktraceFilter},
|
||||
|
||||
noticeCh: make(chan *Notice, 1000),
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
go n.worker()
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Sets Airbrake host name. Default is https://airbrake.io.
|
||||
func (n *Notifier) SetHost(h string) {
|
||||
n.createNoticeURL = getCreateNoticeURL(h, n.projectId, n.projectKey)
|
||||
}
|
||||
|
||||
// AddFilter adds filter that can modify or ignore notice.
|
||||
func (n *Notifier) AddFilter(fn filter) {
|
||||
n.filters = append(n.filters, fn)
|
||||
}
|
||||
|
||||
// Notify notifies Airbrake about the error.
|
||||
func (n *Notifier) Notify(e interface{}, req *http.Request) {
|
||||
notice := n.Notice(e, req, 1)
|
||||
n.SendNoticeAsync(notice)
|
||||
}
|
||||
|
||||
// Notice returns Aibrake notice created from error and request. depth
|
||||
// determines which call frame to use when constructing backtrace.
|
||||
func (n *Notifier) Notice(err interface{}, req *http.Request, depth int) *Notice {
|
||||
return NewNotice(err, req, depth+3)
|
||||
}
|
||||
|
||||
type sendResponse struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
// SendNotice sends notice to Airbrake.
|
||||
func (n *Notifier) SendNotice(notice *Notice) (string, error) {
|
||||
for _, fn := range n.filters {
|
||||
notice = fn(notice)
|
||||
if notice == nil {
|
||||
// Notice is ignored.
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
buf := buffers.Get().(*bytes.Buffer)
|
||||
defer buffers.Put(buf)
|
||||
|
||||
buf.Reset()
|
||||
if err := json.NewEncoder(buf).Encode(notice); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp, err := n.Client.Post(n.createNoticeURL, "application/json", buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
buf.Reset()
|
||||
_, err = buf.ReadFrom(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
if resp.StatusCode == httpStatusTooManyRequests {
|
||||
return "", errRateLimited
|
||||
}
|
||||
err := fmt.Errorf("gobrake: got response status=%q, wanted 201 CREATED", resp.Status)
|
||||
return "", err
|
||||
}
|
||||
|
||||
var sendResp sendResponse
|
||||
err = json.NewDecoder(buf).Decode(&sendResp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return sendResp.Id, nil
|
||||
}
|
||||
|
||||
func (n *Notifier) sendNotice(notice *Notice) {
|
||||
if _, err := n.SendNotice(notice); err != nil && err != errRateLimited {
|
||||
logger.Printf("gobrake failed reporting notice=%q: %s", notice, err)
|
||||
}
|
||||
n.wg.Done()
|
||||
}
|
||||
|
||||
// SendNoticeAsync acts as SendNotice, but sends notice asynchronously
|
||||
// and pending notices can be flushed with Flush.
|
||||
func (n *Notifier) SendNoticeAsync(notice *Notice) {
|
||||
select {
|
||||
case <-n.closed:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
n.wg.Add(1)
|
||||
select {
|
||||
case n.noticeCh <- notice:
|
||||
default:
|
||||
n.wg.Done()
|
||||
logger.Printf(
|
||||
"notice=%q is ignored, because queue is full (len=%d)",
|
||||
notice, len(n.noticeCh),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) worker() {
|
||||
for {
|
||||
select {
|
||||
case notice := <-n.noticeCh:
|
||||
n.sendNotice(notice)
|
||||
case <-n.closed:
|
||||
select {
|
||||
case notice := <-n.noticeCh:
|
||||
n.sendNotice(notice)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyOnPanic notifies Airbrake about the panic and should be used
|
||||
// with defer statement.
|
||||
func (n *Notifier) NotifyOnPanic() {
|
||||
if v := recover(); v != nil {
|
||||
notice := n.Notice(v, nil, 3)
|
||||
n.SendNotice(notice)
|
||||
panic(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Flush waits for pending requests to finish.
|
||||
func (n *Notifier) Flush() {
|
||||
n.waitTimeout(waitTimeout)
|
||||
}
|
||||
|
||||
// Deprecated. Use CloseTimeout instead.
|
||||
func (n *Notifier) WaitAndClose(timeout time.Duration) error {
|
||||
return n.CloseTimeout(timeout)
|
||||
}
|
||||
|
||||
// CloseTimeout waits for pending requests to finish and then closes the notifier.
|
||||
func (n *Notifier) CloseTimeout(timeout time.Duration) error {
|
||||
select {
|
||||
case <-n.closed:
|
||||
default:
|
||||
close(n.closed)
|
||||
}
|
||||
return n.waitTimeout(timeout)
|
||||
}
|
||||
|
||||
func (n *Notifier) waitTimeout(timeout time.Duration) error {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
n.wg.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
return nil
|
||||
case <-time.After(timeout):
|
||||
return fmt.Errorf("Wait timed out after %s", timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) Close() error {
|
||||
return n.CloseTimeout(waitTimeout)
|
||||
}
|
||||
|
||||
func getCreateNoticeURL(host string, projectId int64, key string) string {
|
||||
return fmt.Sprintf(
|
||||
"%s/api/v3/projects/%d/notices?key=%s",
|
||||
host, projectId, key,
|
||||
)
|
||||
}
|
||||
|
||||
func noticeBacktraceFilter(notice *Notice) *Notice {
|
||||
v, ok := notice.Context["rootDirectory"]
|
||||
if !ok {
|
||||
return notice
|
||||
}
|
||||
|
||||
dir, ok := v.(string)
|
||||
if !ok {
|
||||
return notice
|
||||
}
|
||||
|
||||
dir = filepath.Join(dir, "src")
|
||||
for i := range notice.Errors {
|
||||
replaceRootDirectory(notice.Errors[i].Backtrace, dir)
|
||||
}
|
||||
return notice
|
||||
}
|
||||
|
||||
func replaceRootDirectory(backtrace []StackFrame, rootDir string) {
|
||||
for i := range backtrace {
|
||||
backtrace[i].File = strings.Replace(backtrace[i].File, rootDir, "[PROJECT_ROOT]", 1)
|
||||
}
|
||||
}
|
||||
140
vendor/src/gopkg.in/airbrake/gobrake.v2/notifier_test.go
vendored
Normal file
140
vendor/src/gopkg.in/airbrake/gobrake.v2/notifier_test.go
vendored
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package gobrake_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"gopkg.in/airbrake/gobrake.v2"
|
||||
)
|
||||
|
||||
func TestGobrake(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "gobrake")
|
||||
}
|
||||
|
||||
var _ = Describe("Notifier", func() {
|
||||
var notifier *gobrake.Notifier
|
||||
var sentNotice *gobrake.Notice
|
||||
|
||||
notify := func(e interface{}, req *http.Request) {
|
||||
notifier.Notify(e, req)
|
||||
notifier.Flush()
|
||||
}
|
||||
|
||||
BeforeEach(func() {
|
||||
handler := func(w http.ResponseWriter, req *http.Request) {
|
||||
b, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sentNotice = new(gobrake.Notice)
|
||||
err = json.Unmarshal(b, sentNotice)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write([]byte(`{"id":"123"}`))
|
||||
}
|
||||
server := httptest.NewServer(http.HandlerFunc(handler))
|
||||
|
||||
notifier = gobrake.NewNotifier(1, "key")
|
||||
notifier.SetHost(server.URL)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(notifier.Close()).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("reports error and backtrace", func() {
|
||||
notify("hello", nil)
|
||||
|
||||
e := sentNotice.Errors[0]
|
||||
Expect(e.Type).To(Equal("string"))
|
||||
Expect(e.Message).To(Equal("hello"))
|
||||
Expect(e.Backtrace[0].File).To(Equal("[PROJECT_ROOT]/gopkg.in/airbrake/gobrake.v2/notifier_test.go"))
|
||||
})
|
||||
|
||||
It("reports context, env, session and params", func() {
|
||||
wanted := notifier.Notice("hello", nil, 3)
|
||||
wanted.Context["context1"] = "context1"
|
||||
wanted.Env["env1"] = "value1"
|
||||
wanted.Session["session1"] = "value1"
|
||||
wanted.Params["param1"] = "value1"
|
||||
|
||||
id, err := notifier.SendNotice(wanted)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(id).To(Equal("123"))
|
||||
|
||||
Expect(sentNotice).To(Equal(wanted))
|
||||
})
|
||||
|
||||
It("reports context using SetContext", func() {
|
||||
notifier.AddFilter(func(notice *gobrake.Notice) *gobrake.Notice {
|
||||
notice.Context["environment"] = "production"
|
||||
return notice
|
||||
})
|
||||
notify("hello", nil)
|
||||
|
||||
Expect(sentNotice.Context["environment"]).To(Equal("production"))
|
||||
})
|
||||
|
||||
It("reports request", func() {
|
||||
u, err := url.Parse("http://foo/bar")
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
req := &http.Request{
|
||||
URL: u,
|
||||
Header: http.Header{
|
||||
"h1": {"h1v1", "h1v2"},
|
||||
"h2": {"h2v1"},
|
||||
"User-Agent": {"my_user_agent"},
|
||||
},
|
||||
Form: url.Values{
|
||||
"f1": {"f1v1"},
|
||||
"f2": {"f2v1", "f2v2"},
|
||||
},
|
||||
}
|
||||
|
||||
notify("hello", req)
|
||||
|
||||
ctx := sentNotice.Context
|
||||
Expect(ctx["url"]).To(Equal("http://foo/bar"))
|
||||
Expect(ctx["userAgent"]).To(Equal("my_user_agent"))
|
||||
|
||||
params := sentNotice.Params
|
||||
Expect(params["f1"]).To(Equal("f1v1"))
|
||||
Expect(params["f2"]).To(Equal([]interface{}{"f2v1", "f2v2"}))
|
||||
|
||||
env := sentNotice.Env
|
||||
Expect(env["h1"]).To(Equal([]interface{}{"h1v1", "h1v2"}))
|
||||
Expect(env["h2"]).To(Equal("h2v1"))
|
||||
})
|
||||
|
||||
It("collects and reports some context", func() {
|
||||
notify("hello", nil)
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
gopath := os.Getenv("GOPATH")
|
||||
gopath = filepath.SplitList(gopath)[0]
|
||||
|
||||
Expect(sentNotice.Context["language"]).To(Equal(runtime.Version()))
|
||||
Expect(sentNotice.Context["os"]).To(Equal(runtime.GOOS))
|
||||
Expect(sentNotice.Context["architecture"]).To(Equal(runtime.GOARCH))
|
||||
Expect(sentNotice.Context["hostname"]).To(Equal(hostname))
|
||||
Expect(sentNotice.Context["rootDirectory"]).To(Equal(gopath))
|
||||
})
|
||||
|
||||
It("does not panic on double close", func() {
|
||||
Expect(notifier.Close()).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
59
vendor/src/gopkg.in/airbrake/gobrake.v2/util.go
vendored
Normal file
59
vendor/src/gopkg.in/airbrake/gobrake.v2/util.go
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package gobrake
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func stackFilter(packageName, funcName string, file string, line int) bool {
|
||||
return packageName == "runtime" && funcName == "panic"
|
||||
}
|
||||
|
||||
type StackFrame struct {
|
||||
File string `json:"file"`
|
||||
Line int `json:"line"`
|
||||
Func string `json:"function"`
|
||||
}
|
||||
|
||||
func stack(depth int) []StackFrame {
|
||||
stack := []StackFrame{}
|
||||
for i := depth; ; i++ {
|
||||
pc, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
packageName, funcName := packageFuncName(pc)
|
||||
if stackFilter(packageName, funcName, file, line) {
|
||||
stack = stack[:0]
|
||||
continue
|
||||
}
|
||||
stack = append(stack, StackFrame{
|
||||
File: file,
|
||||
Line: line,
|
||||
Func: funcName,
|
||||
})
|
||||
}
|
||||
|
||||
return stack
|
||||
}
|
||||
|
||||
func packageFuncName(pc uintptr) (string, string) {
|
||||
f := runtime.FuncForPC(pc)
|
||||
if f == nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
packageName := ""
|
||||
funcName := f.Name()
|
||||
|
||||
if ind := strings.LastIndex(funcName, "/"); ind > 0 {
|
||||
packageName += funcName[:ind+1]
|
||||
funcName = funcName[ind+1:]
|
||||
}
|
||||
if ind := strings.Index(funcName, "."); ind > 0 {
|
||||
packageName += funcName[:ind]
|
||||
funcName = funcName[ind+1:]
|
||||
}
|
||||
|
||||
return packageName, funcName
|
||||
}
|
||||
14
vendor/src/gopkg.in/gemnasium/logrus-airbrake-hook.v2/CHANGELOG.md
vendored
Normal file
14
vendor/src/gopkg.in/gemnasium/logrus-airbrake-hook.v2/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# logrus-airbrake-hook Changelog
|
||||
|
||||
v2.1.1 - 2016-09-22
|
||||
|
||||
* Fix request deletion from the log entry
|
||||
|
||||
v2.1.0 - 2016-09-22
|
||||
|
||||
* Support ***`*http.Request` error reporting
|
||||
|
||||
v2.0.0 - 2015-10-05
|
||||
|
||||
* Support gobrake v2 api
|
||||
|
||||
21
vendor/src/gopkg.in/gemnasium/logrus-airbrake-hook.v2/LICENSE
vendored
Normal file
21
vendor/src/gopkg.in/gemnasium/logrus-airbrake-hook.v2/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Gemnasium
|
||||
|
||||
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.
|
||||
55
vendor/src/gopkg.in/gemnasium/logrus-airbrake-hook.v2/README.md
vendored
Normal file
55
vendor/src/gopkg.in/gemnasium/logrus-airbrake-hook.v2/README.md
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Airbrake Hook for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:" /> [](https://travis-ci.org/gemnasium/logrus-airbrake-hook) [](https://godoc.org/gopkg.in/gemnasium/logrus-airbrake-hook.v2)
|
||||
|
||||
Use this hook to send your errors to [Airbrake](https://airbrake.io/).
|
||||
This hook is using the [official airbrake go package](https://github.com/airbrake/gobrake), and will hit the api V3.
|
||||
The hook is synchronous and will send the error for `log.Error`, `log.Fatal` and `log.Panic` levels.
|
||||
|
||||
All logrus fields will be sent as context fields on Airbrake.
|
||||
|
||||
## Usage
|
||||
|
||||
The hook must be configured with:
|
||||
|
||||
* A project ID (found in your your Airbrake project settings)
|
||||
* An API key ID (found in your your Airbrake project settings)
|
||||
* The name of the current environment ("development", "staging", "production", ...)
|
||||
|
||||
```go
|
||||
import (
|
||||
"log/syslog"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log := logrus.New()
|
||||
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
||||
log.Error("some logging message")
|
||||
}
|
||||
```
|
||||
|
||||
Note that if environment == "development", the hook will not send anything to airbrake.
|
||||
|
||||
### Reporting http request failure
|
||||
|
||||
|
||||
```go
|
||||
import (
|
||||
"log/syslog"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log := logrus.New()
|
||||
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
log.WithField("request", req).Error("some logging message")
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
* the req will be removed from the log entry
|
||||
* the name of the field doesn't matter, since it's not logged
|
||||
* if more than one request is sent, only the first will be taken into account (and the others will be logged as strings)
|
||||
71
vendor/src/gopkg.in/gemnasium/logrus-airbrake-hook.v2/airbrake.go
vendored
Normal file
71
vendor/src/gopkg.in/gemnasium/logrus-airbrake-hook.v2/airbrake.go
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
package airbrake // import "gopkg.in/gemnasium/logrus-airbrake-hook.v2"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"gopkg.in/airbrake/gobrake.v2"
|
||||
)
|
||||
|
||||
// AirbrakeHook to send exceptions to an exception-tracking service compatible
|
||||
// with the Airbrake API.
|
||||
type airbrakeHook struct {
|
||||
Airbrake *gobrake.Notifier
|
||||
}
|
||||
|
||||
func NewHook(projectID int64, apiKey, env string) *airbrakeHook {
|
||||
airbrake := gobrake.NewNotifier(projectID, apiKey)
|
||||
airbrake.AddFilter(func(notice *gobrake.Notice) *gobrake.Notice {
|
||||
if env == "development" {
|
||||
return nil
|
||||
}
|
||||
notice.Context["environment"] = env
|
||||
return notice
|
||||
})
|
||||
hook := &airbrakeHook{
|
||||
Airbrake: airbrake,
|
||||
}
|
||||
return hook
|
||||
}
|
||||
|
||||
func (hook *airbrakeHook) Fire(entry *logrus.Entry) error {
|
||||
var notifyErr error
|
||||
err, ok := entry.Data["error"].(error)
|
||||
if ok {
|
||||
notifyErr = err
|
||||
} else {
|
||||
notifyErr = errors.New(entry.Message)
|
||||
}
|
||||
var req *http.Request
|
||||
for k, v := range entry.Data {
|
||||
if r, ok := v.(*http.Request); ok {
|
||||
req = r
|
||||
delete(entry.Data, k)
|
||||
break
|
||||
}
|
||||
}
|
||||
notice := hook.Airbrake.Notice(notifyErr, req, 3)
|
||||
for k, v := range entry.Data {
|
||||
notice.Context[k] = fmt.Sprintf("%s", v)
|
||||
}
|
||||
|
||||
hook.sendNotice(notice)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *airbrakeHook) sendNotice(notice *gobrake.Notice) {
|
||||
if _, err := hook.Airbrake.SendNotice(notice); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to send error to Airbrake: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (hook *airbrakeHook) Levels() []logrus.Level {
|
||||
return []logrus.Level{
|
||||
logrus.ErrorLevel,
|
||||
logrus.FatalLevel,
|
||||
logrus.PanicLevel,
|
||||
}
|
||||
}
|
||||
203
vendor/src/gopkg.in/gemnasium/logrus-airbrake-hook.v2/airbrake_test.go
vendored
Normal file
203
vendor/src/gopkg.in/gemnasium/logrus-airbrake-hook.v2/airbrake_test.go
vendored
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
package airbrake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"gopkg.in/airbrake/gobrake.v2"
|
||||
)
|
||||
|
||||
type customErr struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *customErr) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
const (
|
||||
testAPIKey = "abcxyz"
|
||||
testEnv = "development"
|
||||
expectedClass = "*airbrake.customErr"
|
||||
expectedMsg = "foo"
|
||||
unintendedMsg = "Airbrake will not see this string"
|
||||
)
|
||||
|
||||
var (
|
||||
noticeChan = make(chan *gobrake.Notice, 1)
|
||||
)
|
||||
|
||||
// TestLogEntryMessageReceived checks if invoking Logrus' log.Error
|
||||
// method causes an XML payload containing the log entry message is received
|
||||
// by a HTTP server emulating an Airbrake-compatible endpoint.
|
||||
func TestLogEntryMessageReceived(t *testing.T) {
|
||||
log := logrus.New()
|
||||
hook := newTestHook()
|
||||
log.Hooks.Add(hook)
|
||||
|
||||
log.Error(expectedMsg)
|
||||
|
||||
select {
|
||||
case received := <-noticeChan:
|
||||
receivedErr := received.Errors[0]
|
||||
if receivedErr.Message != expectedMsg {
|
||||
t.Errorf("Unexpected message received: %s", receivedErr.Message)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Error("Timed out; no notice received by Airbrake API")
|
||||
}
|
||||
}
|
||||
|
||||
// TestLogEntryMessageReceived confirms that, when passing an error type using
|
||||
// logrus.Fields, a HTTP server emulating an Airbrake endpoint receives the
|
||||
// error message returned by the Error() method on the error interface
|
||||
// rather than the logrus.Entry.Message string.
|
||||
func TestLogEntryWithErrorReceived(t *testing.T) {
|
||||
log := logrus.New()
|
||||
hook := newTestHook()
|
||||
log.Hooks.Add(hook)
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"error": &customErr{expectedMsg},
|
||||
}).Error(unintendedMsg)
|
||||
|
||||
select {
|
||||
case received := <-noticeChan:
|
||||
receivedErr := received.Errors[0]
|
||||
if receivedErr.Message != expectedMsg {
|
||||
t.Errorf("Unexpected message received: %s", receivedErr.Message)
|
||||
}
|
||||
if receivedErr.Type != expectedClass {
|
||||
t.Errorf("Unexpected error class: %s", receivedErr.Type)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Error("Timed out; no notice received by Airbrake API")
|
||||
}
|
||||
}
|
||||
|
||||
// TestLogEntryWithNonErrorTypeNotReceived confirms that, when passing a
|
||||
// non-error type using logrus.Fields, a HTTP server emulating an Airbrake
|
||||
// endpoint receives the logrus.Entry.Message string.
|
||||
//
|
||||
// Only error types are supported when setting the 'error' field using
|
||||
// logrus.WithFields().
|
||||
func TestLogEntryWithNonErrorTypeNotReceived(t *testing.T) {
|
||||
log := logrus.New()
|
||||
hook := newTestHook()
|
||||
log.Hooks.Add(hook)
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"error": expectedMsg,
|
||||
}).Error(unintendedMsg)
|
||||
|
||||
select {
|
||||
case received := <-noticeChan:
|
||||
receivedErr := received.Errors[0]
|
||||
if receivedErr.Message != unintendedMsg {
|
||||
t.Errorf("Unexpected message received: %s", receivedErr.Message)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Error("Timed out; no notice received by Airbrake API")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogEntryWithCustomFields(t *testing.T) {
|
||||
log := logrus.New()
|
||||
hook := newTestHook()
|
||||
log.Hooks.Add(hook)
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"user_id": "123",
|
||||
}).Error(unintendedMsg)
|
||||
|
||||
select {
|
||||
case received := <-noticeChan:
|
||||
receivedErr := received.Errors[0]
|
||||
if receivedErr.Message != unintendedMsg {
|
||||
t.Errorf("Unexpected message received: %s", receivedErr.Message)
|
||||
}
|
||||
if received.Context["user_id"] != "123" {
|
||||
t.Errorf("Expected message to contain Context[\"user_id\"] == \"123\" got %q", received.Context["user_id"])
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Error("Timed out; no notice received by Airbrake API")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogEntryWithHTTPRequestFields(t *testing.T) {
|
||||
log := logrus.New()
|
||||
hook := newTestHook()
|
||||
log.Hooks.Add(hook)
|
||||
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"user_id": "123",
|
||||
"request": req,
|
||||
}).Error(unintendedMsg)
|
||||
|
||||
select {
|
||||
case received := <-noticeChan:
|
||||
receivedErr := received.Errors[0]
|
||||
if receivedErr.Message != unintendedMsg {
|
||||
t.Errorf("Unexpected message received: %s", receivedErr.Message)
|
||||
}
|
||||
if received.Context["user_id"] != "123" {
|
||||
t.Errorf("Expected message to contain Context[\"user_id\"] == \"123\" got %q", received.Context["user_id"])
|
||||
}
|
||||
if received.Context["url"] != "http://example.com" {
|
||||
t.Errorf("Expected message to contain Context[\"url\"] == \"http://example.com\" got %q", received.Context["url"])
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Error("Timed out; no notice received by Airbrake API")
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a new airbrakeHook with the test server proxied
|
||||
func newTestHook() *airbrakeHook {
|
||||
// Make a http.Client with the transport
|
||||
httpClient := &http.Client{Transport: &FakeRoundTripper{}}
|
||||
|
||||
hook := NewHook(123, testAPIKey, "production")
|
||||
hook.Airbrake.Client = httpClient
|
||||
return hook
|
||||
}
|
||||
|
||||
// gobrake API doesn't allow to override endpoint, we need a http.Roundtripper
|
||||
type FakeRoundTripper struct {
|
||||
}
|
||||
|
||||
func (rt *FakeRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
notice := &gobrake.Notice{}
|
||||
err = json.Unmarshal(b, notice)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
noticeChan <- notice
|
||||
|
||||
jsonResponse := struct {
|
||||
Id string `json:"id"`
|
||||
}{"1"}
|
||||
|
||||
sendResponse, _ := json.Marshal(jsonResponse)
|
||||
res := &http.Response{
|
||||
StatusCode: http.StatusCreated,
|
||||
Body: ioutil.NopCloser(bytes.NewReader(sendResponse)),
|
||||
Header: make(http.Header),
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue