mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-02-02 11:34:20 +02:00
Forgotten vendor
This commit is contained in:
parent
12b59c3796
commit
f1f9a355c7
2
go.mod
2
go.mod
@ -11,7 +11,7 @@ require (
|
||||
github.com/bugsnag/bugsnag-go v1.4.0
|
||||
github.com/bugsnag/panicwrap v1.2.0 // indirect
|
||||
github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261 // indirect
|
||||
github.com/getsentry/raven-go v0.2.0
|
||||
github.com/getsentry/raven-go v0.2.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.2.1
|
||||
github.com/go-ole/go-ole v1.2.2 // indirect
|
||||
github.com/gofrs/uuid v3.2.0+incompatible // indirect
|
||||
|
3
vendor/github.com/certifi/gocertifi/LICENSE
generated
vendored
3
vendor/github.com/certifi/gocertifi/LICENSE
generated
vendored
@ -1,3 +0,0 @@
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain
|
||||
one at http://mozilla.org/MPL/2.0/.
|
60
vendor/github.com/certifi/gocertifi/README.md
generated
vendored
60
vendor/github.com/certifi/gocertifi/README.md
generated
vendored
@ -1,60 +0,0 @@
|
||||
# GoCertifi: SSL Certificates for Golang
|
||||
|
||||
This Go package contains a CA bundle that you can reference in your Go code.
|
||||
This is useful for systems that do not have CA bundles that Golang can find
|
||||
itself, or where a uniform set of CAs is valuable.
|
||||
|
||||
This is the same CA bundle that ships with the
|
||||
[Python Requests](https://github.com/kennethreitz/requests) library, and is a
|
||||
Golang specific port of [certifi](https://github.com/kennethreitz/certifi). The
|
||||
CA bundle is derived from Mozilla's canonical set.
|
||||
|
||||
## Usage
|
||||
|
||||
You can use the `gocertifi` package as follows:
|
||||
|
||||
```go
|
||||
import "github.com/certifi/gocertifi"
|
||||
|
||||
cert_pool, err := gocertifi.CACerts()
|
||||
```
|
||||
|
||||
You can use the returned `*x509.CertPool` as part of an HTTP transport, for example:
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
// Setup an HTTP client with a custom transport
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{RootCAs: cert_pool},
|
||||
}
|
||||
client := &http.Client{Transport: transport}
|
||||
|
||||
// Make an HTTP request using our custom transport
|
||||
resp, err := client.Get("https://example.com")
|
||||
```
|
||||
|
||||
## Detailed Documentation
|
||||
|
||||
Import as follows:
|
||||
|
||||
```go
|
||||
import "github.com/certifi/gocertifi"
|
||||
```
|
||||
|
||||
### Errors
|
||||
|
||||
```go
|
||||
var ErrParseFailed = errors.New("gocertifi: error when parsing certificates")
|
||||
```
|
||||
|
||||
### Functions
|
||||
|
||||
```go
|
||||
func CACerts() (*x509.CertPool, error)
|
||||
```
|
||||
CACerts builds an X.509 certificate pool containing the Mozilla CA Certificate
|
||||
bundle. Returns nil on error along with an appropriate error code.
|
4456
vendor/github.com/certifi/gocertifi/certifi.go
generated
vendored
4456
vendor/github.com/certifi/gocertifi/certifi.go
generated
vendored
File diff suppressed because it is too large
Load Diff
20
vendor/github.com/certifi/gocertifi/tasks.py
generated
vendored
20
vendor/github.com/certifi/gocertifi/tasks.py
generated
vendored
@ -1,20 +0,0 @@
|
||||
from invoke import task
|
||||
import requests
|
||||
|
||||
@task
|
||||
def update(ctx):
|
||||
r = requests.get('https://mkcert.org/generate/')
|
||||
r.raise_for_status()
|
||||
certs = r.content
|
||||
|
||||
with open('certifi.go', 'rb') as f:
|
||||
file = f.read()
|
||||
|
||||
file = file.split('`\n')
|
||||
assert len(file) == 3
|
||||
file[1] = certs
|
||||
|
||||
ctx.run("rm certifi.go")
|
||||
|
||||
with open('certifi.go', 'wb') as f:
|
||||
f.write('`\n'.join(file))
|
1
vendor/github.com/getsentry/raven-go/.dockerignore
generated
vendored
1
vendor/github.com/getsentry/raven-go/.dockerignore
generated
vendored
@ -1 +0,0 @@
|
||||
.git
|
5
vendor/github.com/getsentry/raven-go/.gitignore
generated
vendored
5
vendor/github.com/getsentry/raven-go/.gitignore
generated
vendored
@ -1,5 +0,0 @@
|
||||
*.test
|
||||
*.out
|
||||
example/example
|
||||
/xunit.xml
|
||||
/coverage.xml
|
0
vendor/github.com/getsentry/raven-go/.gitmodules
generated
vendored
0
vendor/github.com/getsentry/raven-go/.gitmodules
generated
vendored
41
vendor/github.com/getsentry/raven-go/.travis.yml
generated
vendored
41
vendor/github.com/getsentry/raven-go/.travis.yml
generated
vendored
@ -1,41 +0,0 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- tip
|
||||
|
||||
before_install:
|
||||
- go install -race std
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/tebeka/go2xunit
|
||||
- go get github.com/t-yuki/gocover-cobertura
|
||||
- go get -v ./...
|
||||
|
||||
script:
|
||||
- go test -v -race ./... | tee gotest.out
|
||||
- $GOPATH/bin/go2xunit -fail -input gotest.out -output xunit.xml
|
||||
- go test -v -coverprofile=coverage.txt -covermode count .
|
||||
- $GOPATH/bin/gocover-cobertura < coverage.txt > coverage.xml
|
||||
|
||||
after_script:
|
||||
- npm install -g @zeus-ci/cli
|
||||
- zeus upload -t "application/x-cobertura+xml" coverage.xml
|
||||
- zeus upload -t "application/x-xunit+xml" xunit.xml
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://zeus.ci/hooks/cd949996-d30a-11e8-ba53-0a580a28042d/public/provider/travis/webhook
|
||||
on_success: always
|
||||
on_failure: always
|
||||
on_start: always
|
||||
on_cancel: always
|
||||
on_error: always
|
28
vendor/github.com/getsentry/raven-go/LICENSE
generated
vendored
28
vendor/github.com/getsentry/raven-go/LICENSE
generated
vendored
@ -1,28 +0,0 @@
|
||||
Copyright (c) 2013 Apollic Software, LLC. All rights reserved.
|
||||
Copyright (c) 2015 Functional Software, Inc. 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 Apollic Software, LLC 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.
|
19
vendor/github.com/getsentry/raven-go/README.md
generated
vendored
19
vendor/github.com/getsentry/raven-go/README.md
generated
vendored
@ -1,19 +0,0 @@
|
||||
# raven
|
||||
|
||||
[![Build Status](https://api.travis-ci.org/getsentry/raven-go.svg?branch=master)](https://travis-ci.org/getsentry/raven-go)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/getsentry/raven-go)](https://goreportcard.com/report/github.com/getsentry/raven-go)
|
||||
[![GoDoc](https://godoc.org/github.com/getsentry/raven-go?status.svg)](https://godoc.org/github.com/getsentry/raven-go)
|
||||
|
||||
raven is the official Go SDK for the [Sentry](https://github.com/getsentry/sentry)
|
||||
event/error logging system.
|
||||
|
||||
- [**API Documentation**](https://godoc.org/github.com/getsentry/raven-go)
|
||||
- [**Usage and Examples**](https://docs.sentry.io/clients/go/)
|
||||
|
||||
## Installation
|
||||
|
||||
```text
|
||||
go get github.com/getsentry/raven-go
|
||||
```
|
||||
|
||||
Note: Go 1.7 and newer are supported.
|
977
vendor/github.com/getsentry/raven-go/client.go
generated
vendored
977
vendor/github.com/getsentry/raven-go/client.go
generated
vendored
@ -1,977 +0,0 @@
|
||||
// Package raven implements a client for the Sentry error logging service.
|
||||
package raven
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
mrand "math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/certifi/gocertifi"
|
||||
pkgErrors "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
userAgent = "raven-go/1.0"
|
||||
timestampFormat = `"2006-01-02T15:04:05.00"`
|
||||
)
|
||||
|
||||
var (
|
||||
ErrPacketDropped = errors.New("raven: packet dropped")
|
||||
ErrUnableToUnmarshalJSON = errors.New("raven: unable to unmarshal JSON")
|
||||
ErrMissingUser = errors.New("raven: dsn missing public key and/or password")
|
||||
ErrMissingProjectID = errors.New("raven: dsn missing project id")
|
||||
ErrInvalidSampleRate = errors.New("raven: sample rate should be between 0 and 1")
|
||||
)
|
||||
|
||||
type Severity string
|
||||
|
||||
// http://docs.python.org/2/howto/logging.html#logging-levels
|
||||
const (
|
||||
DEBUG = Severity("debug")
|
||||
INFO = Severity("info")
|
||||
WARNING = Severity("warning")
|
||||
ERROR = Severity("error")
|
||||
FATAL = Severity("fatal")
|
||||
)
|
||||
|
||||
type Timestamp time.Time
|
||||
|
||||
func (t Timestamp) MarshalJSON() ([]byte, error) {
|
||||
return []byte(time.Time(t).UTC().Format(timestampFormat)), nil
|
||||
}
|
||||
|
||||
func (timestamp *Timestamp) UnmarshalJSON(data []byte) error {
|
||||
t, err := time.Parse(timestampFormat, string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*timestamp = Timestamp(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (timestamp Timestamp) Format(format string) string {
|
||||
t := time.Time(timestamp)
|
||||
return t.Format(format)
|
||||
}
|
||||
|
||||
// An Interface is a Sentry interface that will be serialized as JSON.
|
||||
// It must implement json.Marshaler or use json struct tags.
|
||||
type Interface interface {
|
||||
// The Sentry class name. Example: sentry.interfaces.Stacktrace
|
||||
Class() string
|
||||
}
|
||||
|
||||
type Culpriter interface {
|
||||
Culprit() string
|
||||
}
|
||||
|
||||
type Transport interface {
|
||||
Send(url, authHeader string, packet *Packet) error
|
||||
}
|
||||
|
||||
type Extra map[string]interface{}
|
||||
|
||||
type outgoingPacket struct {
|
||||
packet *Packet
|
||||
ch chan error
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
type Tags []Tag
|
||||
|
||||
func (tag *Tag) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal([2]string{tag.Key, tag.Value})
|
||||
}
|
||||
|
||||
func (t *Tag) UnmarshalJSON(data []byte) error {
|
||||
var tag [2]string
|
||||
if err := json.Unmarshal(data, &tag); err != nil {
|
||||
return err
|
||||
}
|
||||
*t = Tag{tag[0], tag[1]}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tags) UnmarshalJSON(data []byte) error {
|
||||
var tags []Tag
|
||||
|
||||
switch data[0] {
|
||||
case '[':
|
||||
// Unmarshal into []Tag
|
||||
if err := json.Unmarshal(data, &tags); err != nil {
|
||||
return err
|
||||
}
|
||||
case '{':
|
||||
// Unmarshal into map[string]string
|
||||
tagMap := make(map[string]string)
|
||||
if err := json.Unmarshal(data, &tagMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Convert to []Tag
|
||||
for k, v := range tagMap {
|
||||
tags = append(tags, Tag{k, v})
|
||||
}
|
||||
default:
|
||||
return ErrUnableToUnmarshalJSON
|
||||
}
|
||||
|
||||
*t = tags
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://docs.getsentry.com/hosted/clientdev/#building-the-json-packet
|
||||
type Packet struct {
|
||||
// Required
|
||||
Message string `json:"message"`
|
||||
|
||||
// Required, set automatically by Client.Send/Report via Packet.Init if blank
|
||||
EventID string `json:"event_id"`
|
||||
Project string `json:"project"`
|
||||
Timestamp Timestamp `json:"timestamp"`
|
||||
Level Severity `json:"level"`
|
||||
Logger string `json:"logger"`
|
||||
|
||||
// Optional
|
||||
Platform string `json:"platform,omitempty"`
|
||||
Culprit string `json:"culprit,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
Release string `json:"release,omitempty"`
|
||||
Environment string `json:"environment,omitempty"`
|
||||
Tags Tags `json:"tags,omitempty"`
|
||||
Modules map[string]string `json:"modules,omitempty"`
|
||||
Fingerprint []string `json:"fingerprint,omitempty"`
|
||||
Extra Extra `json:"extra,omitempty"`
|
||||
|
||||
Interfaces []Interface `json:"-"`
|
||||
}
|
||||
|
||||
// NewPacket constructs a packet with the specified message and interfaces.
|
||||
func NewPacket(message string, interfaces ...Interface) *Packet {
|
||||
extra := Extra{}
|
||||
setExtraDefaults(extra)
|
||||
return &Packet{
|
||||
Message: message,
|
||||
Interfaces: interfaces,
|
||||
Extra: extra,
|
||||
}
|
||||
}
|
||||
|
||||
// NewPacketWithExtra constructs a packet with the specified message, extra information, and interfaces.
|
||||
func NewPacketWithExtra(message string, extra Extra, interfaces ...Interface) *Packet {
|
||||
if extra == nil {
|
||||
extra = Extra{}
|
||||
}
|
||||
setExtraDefaults(extra)
|
||||
|
||||
return &Packet{
|
||||
Message: message,
|
||||
Interfaces: interfaces,
|
||||
Extra: extra,
|
||||
}
|
||||
}
|
||||
|
||||
func setExtraDefaults(extra Extra) Extra {
|
||||
extra["runtime.Version"] = runtime.Version()
|
||||
extra["runtime.NumCPU"] = runtime.NumCPU()
|
||||
extra["runtime.GOMAXPROCS"] = runtime.GOMAXPROCS(0) // 0 just returns the current value
|
||||
extra["runtime.NumGoroutine"] = runtime.NumGoroutine()
|
||||
return extra
|
||||
}
|
||||
|
||||
// Init initializes required fields in a packet. It is typically called by
|
||||
// Client.Send/Report automatically.
|
||||
func (packet *Packet) Init(project string) error {
|
||||
if packet.Project == "" {
|
||||
packet.Project = project
|
||||
}
|
||||
if packet.EventID == "" {
|
||||
var err error
|
||||
packet.EventID, err = uuid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if time.Time(packet.Timestamp).IsZero() {
|
||||
packet.Timestamp = Timestamp(time.Now())
|
||||
}
|
||||
if packet.Level == "" {
|
||||
packet.Level = ERROR
|
||||
}
|
||||
if packet.Logger == "" {
|
||||
packet.Logger = "root"
|
||||
}
|
||||
if packet.ServerName == "" {
|
||||
packet.ServerName = hostname
|
||||
}
|
||||
if packet.Platform == "" {
|
||||
packet.Platform = "go"
|
||||
}
|
||||
|
||||
if packet.Culprit == "" {
|
||||
for _, inter := range packet.Interfaces {
|
||||
if c, ok := inter.(Culpriter); ok {
|
||||
packet.Culprit = c.Culprit()
|
||||
if packet.Culprit != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (packet *Packet) AddTags(tags map[string]string) {
|
||||
for k, v := range tags {
|
||||
packet.Tags = append(packet.Tags, Tag{k, v})
|
||||
}
|
||||
}
|
||||
|
||||
func uuid() (string, error) {
|
||||
id := make([]byte, 16)
|
||||
_, err := io.ReadFull(rand.Reader, id)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
id[6] &= 0x0F // clear version
|
||||
id[6] |= 0x40 // set version to 4 (random uuid)
|
||||
id[8] &= 0x3F // clear variant
|
||||
id[8] |= 0x80 // set to IETF variant
|
||||
return hex.EncodeToString(id), nil
|
||||
}
|
||||
|
||||
func (packet *Packet) JSON() ([]byte, error) {
|
||||
packetJSON, err := json.Marshal(packet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
interfaces := make(map[string]Interface, len(packet.Interfaces))
|
||||
for _, inter := range packet.Interfaces {
|
||||
if inter != nil {
|
||||
interfaces[inter.Class()] = inter
|
||||
}
|
||||
}
|
||||
|
||||
if len(interfaces) > 0 {
|
||||
interfaceJSON, err := json.Marshal(interfaces)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packetJSON[len(packetJSON)-1] = ','
|
||||
packetJSON = append(packetJSON, interfaceJSON[1:]...)
|
||||
}
|
||||
|
||||
return packetJSON, nil
|
||||
}
|
||||
|
||||
type context struct {
|
||||
user *User
|
||||
http *Http
|
||||
tags map[string]string
|
||||
}
|
||||
|
||||
func (c *context) setUser(u *User) { c.user = u }
|
||||
func (c *context) setHttp(h *Http) { c.http = h }
|
||||
func (c *context) setTags(t map[string]string) {
|
||||
if c.tags == nil {
|
||||
c.tags = make(map[string]string)
|
||||
}
|
||||
for k, v := range t {
|
||||
c.tags[k] = v
|
||||
}
|
||||
}
|
||||
func (c *context) clear() {
|
||||
c.user = nil
|
||||
c.http = nil
|
||||
c.tags = nil
|
||||
}
|
||||
|
||||
// Return a list of interfaces to be used in appending with the rest
|
||||
func (c *context) interfaces() []Interface {
|
||||
len, i := 0, 0
|
||||
if c.user != nil {
|
||||
len++
|
||||
}
|
||||
if c.http != nil {
|
||||
len++
|
||||
}
|
||||
interfaces := make([]Interface, len)
|
||||
if c.user != nil {
|
||||
interfaces[i] = c.user
|
||||
i++
|
||||
}
|
||||
if c.http != nil {
|
||||
interfaces[i] = c.http
|
||||
i++
|
||||
}
|
||||
return interfaces
|
||||
}
|
||||
|
||||
// The maximum number of packets that will be buffered waiting to be delivered.
|
||||
// Packets will be dropped if the buffer is full. Used by NewClient.
|
||||
var MaxQueueBuffer = 100
|
||||
|
||||
func newTransport() Transport {
|
||||
t := &HTTPTransport{}
|
||||
rootCAs, err := gocertifi.CACerts()
|
||||
if err != nil {
|
||||
log.Println("raven: failed to load root TLS certificates:", err)
|
||||
} else {
|
||||
t.Client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
TLSClientConfig: &tls.Config{RootCAs: rootCAs},
|
||||
},
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func newClient(tags map[string]string) *Client {
|
||||
client := &Client{
|
||||
Transport: newTransport(),
|
||||
Tags: tags,
|
||||
context: &context{},
|
||||
sampleRate: 1.0,
|
||||
queue: make(chan *outgoingPacket, MaxQueueBuffer),
|
||||
}
|
||||
client.SetDSN(os.Getenv("SENTRY_DSN"))
|
||||
client.SetRelease(os.Getenv("SENTRY_RELEASE"))
|
||||
client.SetEnvironment(os.Getenv("SENTRY_ENVIRONMENT"))
|
||||
return client
|
||||
}
|
||||
|
||||
// New constructs a new Sentry client instance
|
||||
func New(dsn string) (*Client, error) {
|
||||
client := newClient(nil)
|
||||
return client, client.SetDSN(dsn)
|
||||
}
|
||||
|
||||
// NewWithTags constructs a new Sentry client instance with default tags.
|
||||
func NewWithTags(dsn string, tags map[string]string) (*Client, error) {
|
||||
client := newClient(tags)
|
||||
return client, client.SetDSN(dsn)
|
||||
}
|
||||
|
||||
// NewClient constructs a Sentry client and spawns a background goroutine to
|
||||
// handle packets sent by Client.Report.
|
||||
//
|
||||
// Deprecated: use New and NewWithTags instead
|
||||
func NewClient(dsn string, tags map[string]string) (*Client, error) {
|
||||
client := newClient(tags)
|
||||
return client, client.SetDSN(dsn)
|
||||
}
|
||||
|
||||
// Client encapsulates a connection to a Sentry server. It must be initialized
|
||||
// by calling NewClient. Modification of fields concurrently with Send or after
|
||||
// calling Report for the first time is not thread-safe.
|
||||
type Client struct {
|
||||
Tags map[string]string
|
||||
|
||||
Transport Transport
|
||||
|
||||
// DropHandler is called when a packet is dropped because the buffer is full.
|
||||
DropHandler func(*Packet)
|
||||
|
||||
// Context that will get appending to all packets
|
||||
context *context
|
||||
|
||||
mu sync.RWMutex
|
||||
url string
|
||||
projectID string
|
||||
authHeader string
|
||||
release string
|
||||
environment string
|
||||
sampleRate float32
|
||||
|
||||
// default logger name (leave empty for 'root')
|
||||
defaultLoggerName string
|
||||
|
||||
includePaths []string
|
||||
ignoreErrorsRegexp *regexp.Regexp
|
||||
queue chan *outgoingPacket
|
||||
|
||||
// A WaitGroup to keep track of all currently in-progress captures
|
||||
// This is intended to be used with Client.Wait() to assure that
|
||||
// all messages have been transported before exiting the process.
|
||||
wg sync.WaitGroup
|
||||
|
||||
// A Once to track only starting up the background worker once
|
||||
start sync.Once
|
||||
}
|
||||
|
||||
// Initialize a default *Client instance
|
||||
var DefaultClient = newClient(nil)
|
||||
|
||||
func (c *Client) SetIgnoreErrors(errs []string) error {
|
||||
joinedRegexp := strings.Join(errs, "|")
|
||||
r, err := regexp.Compile(joinedRegexp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compile regexp %q for %q: %v", joinedRegexp, errs, err)
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
c.ignoreErrorsRegexp = r
|
||||
c.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) shouldExcludeErr(errStr string) bool {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.ignoreErrorsRegexp != nil && c.ignoreErrorsRegexp.MatchString(errStr)
|
||||
}
|
||||
|
||||
func SetIgnoreErrors(errs ...string) error {
|
||||
return DefaultClient.SetIgnoreErrors(errs)
|
||||
}
|
||||
|
||||
// SetDSN updates a client with a new DSN. It safe to call after and
|
||||
// concurrently with calls to Report and Send.
|
||||
func (client *Client) SetDSN(dsn string) error {
|
||||
if dsn == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
client.mu.Lock()
|
||||
defer client.mu.Unlock()
|
||||
|
||||
uri, err := url.Parse(dsn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if uri.User == nil {
|
||||
return ErrMissingUser
|
||||
}
|
||||
publicKey := uri.User.Username()
|
||||
secretKey, hasSecretKey := uri.User.Password()
|
||||
uri.User = nil
|
||||
|
||||
if idx := strings.LastIndex(uri.Path, "/"); idx != -1 {
|
||||
client.projectID = uri.Path[idx+1:]
|
||||
uri.Path = uri.Path[:idx+1] + "api/" + client.projectID + "/store/"
|
||||
}
|
||||
if client.projectID == "" {
|
||||
return ErrMissingProjectID
|
||||
}
|
||||
|
||||
client.url = uri.String()
|
||||
|
||||
if hasSecretKey {
|
||||
client.authHeader = fmt.Sprintf("Sentry sentry_version=4, sentry_key=%s, sentry_secret=%s", publicKey, secretKey)
|
||||
} else {
|
||||
client.authHeader = fmt.Sprintf("Sentry sentry_version=4, sentry_key=%s", publicKey)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sets the DSN for the default *Client instance
|
||||
func SetDSN(dsn string) error { return DefaultClient.SetDSN(dsn) }
|
||||
|
||||
// SetRelease sets the "release" tag.
|
||||
func (client *Client) SetRelease(release string) {
|
||||
client.mu.Lock()
|
||||
defer client.mu.Unlock()
|
||||
client.release = release
|
||||
}
|
||||
|
||||
// SetEnvironment sets the "environment" tag.
|
||||
func (client *Client) SetEnvironment(environment string) {
|
||||
client.mu.Lock()
|
||||
defer client.mu.Unlock()
|
||||
client.environment = environment
|
||||
}
|
||||
|
||||
// SetDefaultLoggerName sets the default logger name.
|
||||
func (client *Client) SetDefaultLoggerName(name string) {
|
||||
client.mu.Lock()
|
||||
defer client.mu.Unlock()
|
||||
client.defaultLoggerName = name
|
||||
}
|
||||
|
||||
// SetSampleRate sets how much sampling we want on client side
|
||||
func (client *Client) SetSampleRate(rate float32) error {
|
||||
client.mu.Lock()
|
||||
defer client.mu.Unlock()
|
||||
|
||||
if rate < 0 || rate > 1 {
|
||||
return ErrInvalidSampleRate
|
||||
}
|
||||
client.sampleRate = rate
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetRelease sets the "release" tag on the default *Client
|
||||
func SetRelease(release string) { DefaultClient.SetRelease(release) }
|
||||
|
||||
// SetEnvironment sets the "environment" tag on the default *Client
|
||||
func SetEnvironment(environment string) { DefaultClient.SetEnvironment(environment) }
|
||||
|
||||
// SetDefaultLoggerName sets the "defaultLoggerName" on the default *Client
|
||||
func SetDefaultLoggerName(name string) {
|
||||
DefaultClient.SetDefaultLoggerName(name)
|
||||
}
|
||||
|
||||
// SetSampleRate sets the "sample rate" on the degault *Client
|
||||
func SetSampleRate(rate float32) error { return DefaultClient.SetSampleRate(rate) }
|
||||
|
||||
func (client *Client) worker() {
|
||||
for outgoingPacket := range client.queue {
|
||||
|
||||
client.mu.RLock()
|
||||
url, authHeader := client.url, client.authHeader
|
||||
client.mu.RUnlock()
|
||||
|
||||
outgoingPacket.ch <- client.Transport.Send(url, authHeader, outgoingPacket.packet)
|
||||
client.wg.Done()
|
||||
}
|
||||
}
|
||||
|
||||
// Capture asynchronously delivers a packet to the Sentry server. It is a no-op
|
||||
// when client is nil. A channel is provided if it is important to check for a
|
||||
// send's success.
|
||||
func (client *Client) Capture(packet *Packet, captureTags map[string]string) (eventID string, ch chan error) {
|
||||
ch = make(chan error, 1)
|
||||
|
||||
if client == nil {
|
||||
// return a chan that always returns nil when the caller receives from it
|
||||
close(ch)
|
||||
return
|
||||
}
|
||||
|
||||
if client.sampleRate < 1.0 && mrand.Float32() > client.sampleRate {
|
||||
return
|
||||
}
|
||||
|
||||
if packet == nil {
|
||||
close(ch)
|
||||
return
|
||||
}
|
||||
|
||||
if client.shouldExcludeErr(packet.Message) {
|
||||
return
|
||||
}
|
||||
|
||||
// Keep track of all running Captures so that we can wait for them all to finish
|
||||
// *Must* call client.wg.Done() on any path that indicates that an event was
|
||||
// finished being acted upon, whether success or failure
|
||||
client.wg.Add(1)
|
||||
|
||||
// Merge capture tags and client tags
|
||||
packet.AddTags(captureTags)
|
||||
packet.AddTags(client.Tags)
|
||||
|
||||
// Initialize any required packet fields
|
||||
client.mu.RLock()
|
||||
packet.AddTags(client.context.tags)
|
||||
projectID := client.projectID
|
||||
release := client.release
|
||||
environment := client.environment
|
||||
defaultLoggerName := client.defaultLoggerName
|
||||
client.mu.RUnlock()
|
||||
|
||||
// set the global logger name on the packet if we must
|
||||
if packet.Logger == "" && defaultLoggerName != "" {
|
||||
packet.Logger = defaultLoggerName
|
||||
}
|
||||
|
||||
err := packet.Init(projectID)
|
||||
if err != nil {
|
||||
ch <- err
|
||||
client.wg.Done()
|
||||
return
|
||||
}
|
||||
|
||||
if packet.Release == "" {
|
||||
packet.Release = release
|
||||
}
|
||||
|
||||
if packet.Environment == "" {
|
||||
packet.Environment = environment
|
||||
}
|
||||
|
||||
outgoingPacket := &outgoingPacket{packet, ch}
|
||||
|
||||
// Lazily start background worker until we
|
||||
// do our first write into the queue.
|
||||
client.start.Do(func() {
|
||||
go client.worker()
|
||||
})
|
||||
|
||||
select {
|
||||
case client.queue <- outgoingPacket:
|
||||
default:
|
||||
// Send would block, drop the packet
|
||||
if client.DropHandler != nil {
|
||||
client.DropHandler(packet)
|
||||
}
|
||||
ch <- ErrPacketDropped
|
||||
client.wg.Done()
|
||||
}
|
||||
|
||||
return packet.EventID, ch
|
||||
}
|
||||
|
||||
// Capture asynchronously delivers a packet to the Sentry server with the default *Client.
|
||||
// It is a no-op when client is nil. A channel is provided if it is important to check for a
|
||||
// send's success.
|
||||
func Capture(packet *Packet, captureTags map[string]string) (eventID string, ch chan error) {
|
||||
return DefaultClient.Capture(packet, captureTags)
|
||||
}
|
||||
|
||||
// CaptureMessage formats and delivers a string message to the Sentry server.
|
||||
func (client *Client) CaptureMessage(message string, tags map[string]string, interfaces ...Interface) string {
|
||||
if client == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if client.shouldExcludeErr(message) {
|
||||
return ""
|
||||
}
|
||||
|
||||
packet := NewPacket(message, append(append(interfaces, client.context.interfaces()...), &Message{message, nil})...)
|
||||
eventID, _ := client.Capture(packet, tags)
|
||||
|
||||
return eventID
|
||||
}
|
||||
|
||||
// CaptureMessage formats and delivers a string message to the Sentry server with the default *Client
|
||||
func CaptureMessage(message string, tags map[string]string, interfaces ...Interface) string {
|
||||
return DefaultClient.CaptureMessage(message, tags, interfaces...)
|
||||
}
|
||||
|
||||
// CaptureMessageAndWait is identical to CaptureMessage except it blocks and waits for the message to be sent.
|
||||
func (client *Client) CaptureMessageAndWait(message string, tags map[string]string, interfaces ...Interface) string {
|
||||
if client == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if client.shouldExcludeErr(message) {
|
||||
return ""
|
||||
}
|
||||
|
||||
packet := NewPacket(message, append(append(interfaces, client.context.interfaces()...), &Message{message, nil})...)
|
||||
eventID, ch := client.Capture(packet, tags)
|
||||
if eventID != "" {
|
||||
<-ch
|
||||
}
|
||||
|
||||
return eventID
|
||||
}
|
||||
|
||||
// CaptureMessageAndWait is identical to CaptureMessage except it blocks and waits for the message to be sent.
|
||||
func CaptureMessageAndWait(message string, tags map[string]string, interfaces ...Interface) string {
|
||||
return DefaultClient.CaptureMessageAndWait(message, tags, interfaces...)
|
||||
}
|
||||
|
||||
// CaptureErrors formats and delivers an error to the Sentry server.
|
||||
// Adds a stacktrace to the packet, excluding the call to this method.
|
||||
func (client *Client) CaptureError(err error, tags map[string]string, interfaces ...Interface) string {
|
||||
if client == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if client.shouldExcludeErr(err.Error()) {
|
||||
return ""
|
||||
}
|
||||
|
||||
extra := extractExtra(err)
|
||||
cause := pkgErrors.Cause(err)
|
||||
|
||||
packet := NewPacketWithExtra(err.Error(), extra, append(append(interfaces, client.context.interfaces()...), NewException(cause, GetOrNewStacktrace(cause, 1, 3, client.includePaths)))...)
|
||||
eventID, _ := client.Capture(packet, tags)
|
||||
|
||||
return eventID
|
||||
}
|
||||
|
||||
// CaptureErrors formats and delivers an error to the Sentry server using the default *Client.
|
||||
// Adds a stacktrace to the packet, excluding the call to this method.
|
||||
func CaptureError(err error, tags map[string]string, interfaces ...Interface) string {
|
||||
return DefaultClient.CaptureError(err, tags, interfaces...)
|
||||
}
|
||||
|
||||
// CaptureErrorAndWait is identical to CaptureError, except it blocks and assures that the event was sent
|
||||
func (client *Client) CaptureErrorAndWait(err error, tags map[string]string, interfaces ...Interface) string {
|
||||
if client == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if client.shouldExcludeErr(err.Error()) {
|
||||
return ""
|
||||
}
|
||||
|
||||
extra := extractExtra(err)
|
||||
cause := pkgErrors.Cause(err)
|
||||
|
||||
packet := NewPacketWithExtra(err.Error(), extra, append(append(interfaces, client.context.interfaces()...), NewException(cause, GetOrNewStacktrace(cause, 1, 3, client.includePaths)))...)
|
||||
eventID, ch := client.Capture(packet, tags)
|
||||
if eventID != "" {
|
||||
<-ch
|
||||
}
|
||||
|
||||
return eventID
|
||||
}
|
||||
|
||||
// CaptureErrorAndWait is identical to CaptureError, except it blocks and assures that the event was sent
|
||||
func CaptureErrorAndWait(err error, tags map[string]string, interfaces ...Interface) string {
|
||||
return DefaultClient.CaptureErrorAndWait(err, tags, interfaces...)
|
||||
}
|
||||
|
||||
// CapturePanic calls f and then recovers and reports a panic to the Sentry server if it occurs.
|
||||
// If an error is captured, both the error and the reported Sentry error ID are returned.
|
||||
func (client *Client) CapturePanic(f func(), tags map[string]string, interfaces ...Interface) (err interface{}, errorID string) {
|
||||
// Note: This doesn't need to check for client, because we still want to go through the defer/recover path
|
||||
// Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the
|
||||
// *Packet just to be thrown away, this should not be the normal case. Could be refactored to
|
||||
// be completely noop though if we cared.
|
||||
defer func() {
|
||||
var packet *Packet
|
||||
err = recover()
|
||||
switch rval := err.(type) {
|
||||
case nil:
|
||||
return
|
||||
case error:
|
||||
if client.shouldExcludeErr(rval.Error()) {
|
||||
return
|
||||
}
|
||||
packet = NewPacket(rval.Error(), append(append(interfaces, client.context.interfaces()...), NewException(rval, NewStacktrace(2, 3, client.includePaths)))...)
|
||||
default:
|
||||
rvalStr := fmt.Sprint(rval)
|
||||
if client.shouldExcludeErr(rvalStr) {
|
||||
return
|
||||
}
|
||||
packet = NewPacket(rvalStr, append(append(interfaces, client.context.interfaces()...), NewException(errors.New(rvalStr), NewStacktrace(2, 3, client.includePaths)))...)
|
||||
}
|
||||
|
||||
errorID, _ = client.Capture(packet, tags)
|
||||
}()
|
||||
|
||||
f()
|
||||
return
|
||||
}
|
||||
|
||||
// CapturePanic calls f and then recovers and reports a panic to the Sentry server if it occurs.
|
||||
// If an error is captured, both the error and the reported Sentry error ID are returned.
|
||||
func CapturePanic(f func(), tags map[string]string, interfaces ...Interface) (interface{}, string) {
|
||||
return DefaultClient.CapturePanic(f, tags, interfaces...)
|
||||
}
|
||||
|
||||
// CapturePanicAndWait is identical to CaptureError, except it blocks and assures that the event was sent
|
||||
func (client *Client) CapturePanicAndWait(f func(), tags map[string]string, interfaces ...Interface) (err interface{}, errorID string) {
|
||||
// Note: This doesn't need to check for client, because we still want to go through the defer/recover path
|
||||
// Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the
|
||||
// *Packet just to be thrown away, this should not be the normal case. Could be refactored to
|
||||
// be completely noop though if we cared.
|
||||
defer func() {
|
||||
var packet *Packet
|
||||
err = recover()
|
||||
switch rval := err.(type) {
|
||||
case nil:
|
||||
return
|
||||
case error:
|
||||
if client.shouldExcludeErr(rval.Error()) {
|
||||
return
|
||||
}
|
||||
packet = NewPacket(rval.Error(), append(append(interfaces, client.context.interfaces()...), NewException(rval, NewStacktrace(2, 3, client.includePaths)))...)
|
||||
default:
|
||||
rvalStr := fmt.Sprint(rval)
|
||||
if client.shouldExcludeErr(rvalStr) {
|
||||
return
|
||||
}
|
||||
packet = NewPacket(rvalStr, append(append(interfaces, client.context.interfaces()...), NewException(errors.New(rvalStr), NewStacktrace(2, 3, client.includePaths)))...)
|
||||
}
|
||||
|
||||
var ch chan error
|
||||
errorID, ch = client.Capture(packet, tags)
|
||||
if errorID != "" {
|
||||
<-ch
|
||||
}
|
||||
}()
|
||||
|
||||
f()
|
||||
return
|
||||
}
|
||||
|
||||
// CapturePanicAndWait is identical to CaptureError, except it blocks and assures that the event was sent
|
||||
func CapturePanicAndWait(f func(), tags map[string]string, interfaces ...Interface) (interface{}, string) {
|
||||
return DefaultClient.CapturePanicAndWait(f, tags, interfaces...)
|
||||
}
|
||||
|
||||
func (client *Client) Close() {
|
||||
close(client.queue)
|
||||
}
|
||||
|
||||
func Close() { DefaultClient.Close() }
|
||||
|
||||
// Wait blocks and waits for all events to finish being sent to Sentry server
|
||||
func (client *Client) Wait() {
|
||||
client.wg.Wait()
|
||||
}
|
||||
|
||||
// Wait blocks and waits for all events to finish being sent to Sentry server
|
||||
func Wait() { DefaultClient.Wait() }
|
||||
|
||||
func (client *Client) URL() string {
|
||||
client.mu.RLock()
|
||||
defer client.mu.RUnlock()
|
||||
|
||||
return client.url
|
||||
}
|
||||
|
||||
func URL() string { return DefaultClient.URL() }
|
||||
|
||||
func (client *Client) ProjectID() string {
|
||||
client.mu.RLock()
|
||||
defer client.mu.RUnlock()
|
||||
|
||||
return client.projectID
|
||||
}
|
||||
|
||||
func ProjectID() string { return DefaultClient.ProjectID() }
|
||||
|
||||
func (client *Client) Release() string {
|
||||
client.mu.RLock()
|
||||
defer client.mu.RUnlock()
|
||||
|
||||
return client.release
|
||||
}
|
||||
|
||||
func Release() string { return DefaultClient.Release() }
|
||||
|
||||
func IncludePaths() []string { return DefaultClient.IncludePaths() }
|
||||
|
||||
func (client *Client) IncludePaths() []string {
|
||||
client.mu.RLock()
|
||||
defer client.mu.RUnlock()
|
||||
|
||||
return client.includePaths
|
||||
}
|
||||
|
||||
func SetIncludePaths(p []string) { DefaultClient.SetIncludePaths(p) }
|
||||
|
||||
func (client *Client) SetIncludePaths(p []string) {
|
||||
client.mu.Lock()
|
||||
defer client.mu.Unlock()
|
||||
|
||||
client.includePaths = p
|
||||
}
|
||||
|
||||
func (c *Client) SetUserContext(u *User) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.context.setUser(u)
|
||||
}
|
||||
|
||||
func (c *Client) SetHttpContext(h *Http) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.context.setHttp(h)
|
||||
}
|
||||
|
||||
func (c *Client) SetTagsContext(t map[string]string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.context.setTags(t)
|
||||
}
|
||||
|
||||
func (c *Client) ClearContext() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.context.clear()
|
||||
}
|
||||
|
||||
func SetUserContext(u *User) { DefaultClient.SetUserContext(u) }
|
||||
func SetHttpContext(h *Http) { DefaultClient.SetHttpContext(h) }
|
||||
func SetTagsContext(t map[string]string) { DefaultClient.SetTagsContext(t) }
|
||||
func ClearContext() { DefaultClient.ClearContext() }
|
||||
|
||||
// HTTPTransport is the default transport, delivering packets to Sentry via the
|
||||
// HTTP API.
|
||||
type HTTPTransport struct {
|
||||
*http.Client
|
||||
}
|
||||
|
||||
func (t *HTTPTransport) Send(url, authHeader string, packet *Packet) error {
|
||||
if url == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
body, contentType, err := serializedPacket(packet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error serializing packet: %v", err)
|
||||
}
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't create new request: %v", err)
|
||||
}
|
||||
req.Header.Set("X-Sentry-Auth", authHeader)
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
res, err := t.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
io.Copy(ioutil.Discard, res.Body)
|
||||
res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
return fmt.Errorf("raven: got http status %d - x-sentry-error: %s", res.StatusCode, res.Header.Get("X-Sentry-Error"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func serializedPacket(packet *Packet) (io.Reader, string, error) {
|
||||
packetJSON, err := packet.JSON()
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("error marshaling packet %+v to JSON: %v", packet, err)
|
||||
}
|
||||
|
||||
// Only deflate/base64 the packet if it is bigger than 1KB, as there is
|
||||
// overhead.
|
||||
if len(packetJSON) > 1000 {
|
||||
buf := &bytes.Buffer{}
|
||||
b64 := base64.NewEncoder(base64.StdEncoding, buf)
|
||||
deflate, _ := zlib.NewWriterLevel(b64, zlib.BestCompression)
|
||||
deflate.Write(packetJSON)
|
||||
deflate.Close()
|
||||
b64.Close()
|
||||
return buf, "application/octet-stream", nil
|
||||
}
|
||||
return bytes.NewReader(packetJSON), "application/json", nil
|
||||
}
|
||||
|
||||
var hostname string
|
||||
|
||||
func init() {
|
||||
hostname, _ = os.Hostname()
|
||||
}
|
60
vendor/github.com/getsentry/raven-go/errors.go
generated
vendored
60
vendor/github.com/getsentry/raven-go/errors.go
generated
vendored
@ -1,60 +0,0 @@
|
||||
package raven
|
||||
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
type errWrappedWithExtra struct {
|
||||
err error
|
||||
extraInfo map[string]interface{}
|
||||
}
|
||||
|
||||
func (ewx *errWrappedWithExtra) Error() string {
|
||||
return ewx.err.Error()
|
||||
}
|
||||
|
||||
func (ewx *errWrappedWithExtra) Cause() error {
|
||||
return ewx.err
|
||||
}
|
||||
|
||||
func (ewx *errWrappedWithExtra) ExtraInfo() Extra {
|
||||
return ewx.extraInfo
|
||||
}
|
||||
|
||||
// Adds extra data to an error before reporting to Sentry
|
||||
func WrapWithExtra(err error, extraInfo map[string]interface{}) error {
|
||||
return &errWrappedWithExtra{
|
||||
err: err,
|
||||
extraInfo: extraInfo,
|
||||
}
|
||||
}
|
||||
|
||||
type ErrWithExtra interface {
|
||||
Error() string
|
||||
Cause() error
|
||||
ExtraInfo() Extra
|
||||
}
|
||||
|
||||
// Iteratively fetches all the Extra data added to an error,
|
||||
// and it's underlying errors. Extra data defined first is
|
||||
// respected, and is not overridden when extracting.
|
||||
func extractExtra(err error) Extra {
|
||||
extra := Extra{}
|
||||
|
||||
currentErr := err
|
||||
for currentErr != nil {
|
||||
if errWithExtra, ok := currentErr.(ErrWithExtra); ok {
|
||||
for k, v := range errWithExtra.ExtraInfo() {
|
||||
extra[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if errWithCause, ok := currentErr.(causer); ok {
|
||||
currentErr = errWithCause.Cause()
|
||||
} else {
|
||||
currentErr = nil
|
||||
}
|
||||
}
|
||||
|
||||
return extra
|
||||
}
|
50
vendor/github.com/getsentry/raven-go/exception.go
generated
vendored
50
vendor/github.com/getsentry/raven-go/exception.go
generated
vendored
@ -1,50 +0,0 @@
|
||||
package raven
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var errorMsgPattern = regexp.MustCompile(`\A(\w+): (.+)\z`)
|
||||
|
||||
func NewException(err error, stacktrace *Stacktrace) *Exception {
|
||||
msg := err.Error()
|
||||
ex := &Exception{
|
||||
Stacktrace: stacktrace,
|
||||
Value: msg,
|
||||
Type: reflect.TypeOf(err).String(),
|
||||
}
|
||||
if m := errorMsgPattern.FindStringSubmatch(msg); m != nil {
|
||||
ex.Module, ex.Value = m[1], m[2]
|
||||
}
|
||||
return ex
|
||||
}
|
||||
|
||||
// https://docs.getsentry.com/hosted/clientdev/interfaces/#failure-interfaces
|
||||
type Exception struct {
|
||||
// Required
|
||||
Value string `json:"value"`
|
||||
|
||||
// Optional
|
||||
Type string `json:"type,omitempty"`
|
||||
Module string `json:"module,omitempty"`
|
||||
Stacktrace *Stacktrace `json:"stacktrace,omitempty"`
|
||||
}
|
||||
|
||||
func (e *Exception) Class() string { return "exception" }
|
||||
|
||||
func (e *Exception) Culprit() string {
|
||||
if e.Stacktrace == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Stacktrace.Culprit()
|
||||
}
|
||||
|
||||
// Exceptions allows for chained errors
|
||||
// https://docs.sentry.io/clientdev/interfaces/exception/
|
||||
type Exceptions struct {
|
||||
// Required
|
||||
Values []*Exception `json:"values"`
|
||||
}
|
||||
|
||||
func (es Exceptions) Class() string { return "exception" }
|
99
vendor/github.com/getsentry/raven-go/http.go
generated
vendored
99
vendor/github.com/getsentry/raven-go/http.go
generated
vendored
@ -1,99 +0,0 @@
|
||||
package raven
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewHttp(req *http.Request) *Http {
|
||||
proto := "http"
|
||||
if req.TLS != nil || req.Header.Get("X-Forwarded-Proto") == "https" {
|
||||
proto = "https"
|
||||
}
|
||||
h := &Http{
|
||||
Method: req.Method,
|
||||
Cookies: req.Header.Get("Cookie"),
|
||||
Query: sanitizeQuery(req.URL.Query()).Encode(),
|
||||
URL: proto + "://" + req.Host + req.URL.Path,
|
||||
Headers: make(map[string]string, len(req.Header)),
|
||||
}
|
||||
if addr, port, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
||||
h.Env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port}
|
||||
}
|
||||
for k, v := range req.Header {
|
||||
h.Headers[k] = strings.Join(v, ",")
|
||||
}
|
||||
h.Headers["Host"] = req.Host
|
||||
return h
|
||||
}
|
||||
|
||||
var querySecretFields = []string{"password", "passphrase", "passwd", "secret"}
|
||||
|
||||
func sanitizeQuery(query url.Values) url.Values {
|
||||
for _, keyword := range querySecretFields {
|
||||
for field := range query {
|
||||
if strings.Contains(field, keyword) {
|
||||
query[field] = []string{"********"}
|
||||
}
|
||||
}
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// https://docs.getsentry.com/hosted/clientdev/interfaces/#context-interfaces
|
||||
type Http struct {
|
||||
// Required
|
||||
URL string `json:"url"`
|
||||
Method string `json:"method"`
|
||||
Query string `json:"query_string,omitempty"`
|
||||
|
||||
// Optional
|
||||
Cookies string `json:"cookies,omitempty"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
Env map[string]string `json:"env,omitempty"`
|
||||
|
||||
// Must be either a string or map[string]string
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (h *Http) Class() string { return "request" }
|
||||
|
||||
// Recovery handler to wrap the stdlib net/http Mux.
|
||||
// Example:
|
||||
// http.HandleFunc("/", raven.RecoveryHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||
// ...
|
||||
// }))
|
||||
func RecoveryHandler(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
|
||||
return Recoverer(http.HandlerFunc(handler)).ServeHTTP
|
||||
}
|
||||
|
||||
// Recovery handler to wrap the stdlib net/http Mux.
|
||||
// Example:
|
||||
// mux := http.NewServeMux
|
||||
// ...
|
||||
// http.Handle("/", raven.Recoverer(mux))
|
||||
func Recoverer(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if rval := recover(); rval != nil {
|
||||
debug.PrintStack()
|
||||
rvalStr := fmt.Sprint(rval)
|
||||
var packet *Packet
|
||||
if err, ok := rval.(error); ok {
|
||||
packet = NewPacket(rvalStr, NewException(errors.New(rvalStr), GetOrNewStacktrace(err, 2, 3, nil)), NewHttp(r))
|
||||
} else {
|
||||
packet = NewPacket(rvalStr, NewException(errors.New(rvalStr), NewStacktrace(2, 3, nil)), NewHttp(r))
|
||||
}
|
||||
Capture(packet, nil)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
|
||||
handler.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
49
vendor/github.com/getsentry/raven-go/interfaces.go
generated
vendored
49
vendor/github.com/getsentry/raven-go/interfaces.go
generated
vendored
@ -1,49 +0,0 @@
|
||||
package raven
|
||||
|
||||
// https://docs.getsentry.com/hosted/clientdev/interfaces/#message-interface
|
||||
type Message struct {
|
||||
// Required
|
||||
Message string `json:"message"`
|
||||
|
||||
// Optional
|
||||
Params []interface{} `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Message) Class() string { return "logentry" }
|
||||
|
||||
// https://docs.getsentry.com/hosted/clientdev/interfaces/#template-interface
|
||||
type Template struct {
|
||||
// Required
|
||||
Filename string `json:"filename"`
|
||||
Lineno int `json:"lineno"`
|
||||
ContextLine string `json:"context_line"`
|
||||
|
||||
// Optional
|
||||
PreContext []string `json:"pre_context,omitempty"`
|
||||
PostContext []string `json:"post_context,omitempty"`
|
||||
AbsolutePath string `json:"abs_path,omitempty"`
|
||||
}
|
||||
|
||||
func (t *Template) Class() string { return "template" }
|
||||
|
||||
// https://docs.getsentry.com/hosted/clientdev/interfaces/#context-interfaces
|
||||
type User struct {
|
||||
// All fields are optional
|
||||
ID string `json:"id,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
IP string `json:"ip_address,omitempty"`
|
||||
}
|
||||
|
||||
func (h *User) Class() string { return "user" }
|
||||
|
||||
// https://docs.getsentry.com/hosted/clientdev/interfaces/#context-interfaces
|
||||
type Query struct {
|
||||
// Required
|
||||
Query string `json:"query"`
|
||||
|
||||
// Optional
|
||||
Engine string `json:"engine,omitempty"`
|
||||
}
|
||||
|
||||
func (q *Query) Class() string { return "query" }
|
4
vendor/github.com/getsentry/raven-go/runtests.sh
generated
vendored
4
vendor/github.com/getsentry/raven-go/runtests.sh
generated
vendored
@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
go test -race ./...
|
||||
go test -cover ./...
|
||||
go test -v ./...
|
277
vendor/github.com/getsentry/raven-go/stacktrace.go
generated
vendored
277
vendor/github.com/getsentry/raven-go/stacktrace.go
generated
vendored
@ -1,277 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// Some code from the runtime/debug package of the Go standard library.
|
||||
|
||||
package raven
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// https://docs.getsentry.com/hosted/clientdev/interfaces/#failure-interfaces
|
||||
type Stacktrace struct {
|
||||
// Required
|
||||
Frames []*StacktraceFrame `json:"frames"`
|
||||
}
|
||||
|
||||
func (s *Stacktrace) Class() string { return "stacktrace" }
|
||||
|
||||
func (s *Stacktrace) Culprit() string {
|
||||
for i := len(s.Frames) - 1; i >= 0; i-- {
|
||||
frame := s.Frames[i]
|
||||
if frame.InApp == true && frame.Module != "" && frame.Function != "" {
|
||||
return frame.Module + "." + frame.Function
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type StacktraceFrame struct {
|
||||
// At least one required
|
||||
Filename string `json:"filename,omitempty"`
|
||||
Function string `json:"function,omitempty"`
|
||||
Module string `json:"module,omitempty"`
|
||||
|
||||
// Optional
|
||||
Lineno int `json:"lineno,omitempty"`
|
||||
Colno int `json:"colno,omitempty"`
|
||||
AbsolutePath string `json:"abs_path,omitempty"`
|
||||
ContextLine string `json:"context_line,omitempty"`
|
||||
PreContext []string `json:"pre_context,omitempty"`
|
||||
PostContext []string `json:"post_context,omitempty"`
|
||||
InApp bool `json:"in_app"`
|
||||
}
|
||||
|
||||
// Try to get stacktrace from err as an interface of github.com/pkg/errors, or else NewStacktrace()
|
||||
func GetOrNewStacktrace(err error, skip int, context int, appPackagePrefixes []string) *Stacktrace {
|
||||
stacktracer, errHasStacktrace := err.(interface {
|
||||
StackTrace() errors.StackTrace
|
||||
})
|
||||
if errHasStacktrace {
|
||||
var frames []*StacktraceFrame
|
||||
for _, f := range stacktracer.StackTrace() {
|
||||
pc := uintptr(f) - 1
|
||||
fn := runtime.FuncForPC(pc)
|
||||
var fName string
|
||||
var file string
|
||||
var line int
|
||||
if fn != nil {
|
||||
file, line = fn.FileLine(pc)
|
||||
fName = fn.Name()
|
||||
} else {
|
||||
file = "unknown"
|
||||
fName = "unknown"
|
||||
}
|
||||
frame := NewStacktraceFrame(pc, fName, file, line, context, appPackagePrefixes)
|
||||
if frame != nil {
|
||||
frames = append([]*StacktraceFrame{frame}, frames...)
|
||||
}
|
||||
}
|
||||
return &Stacktrace{Frames: frames}
|
||||
} else {
|
||||
return NewStacktrace(skip+1, context, appPackagePrefixes)
|
||||
}
|
||||
}
|
||||
|
||||
// Intialize and populate a new stacktrace, skipping skip frames.
|
||||
//
|
||||
// context is the number of surrounding lines that should be included for context.
|
||||
// Setting context to 3 would try to get seven lines. Setting context to -1 returns
|
||||
// one line with no surrounding context, and 0 returns no context.
|
||||
//
|
||||
// appPackagePrefixes is a list of prefixes used to check whether a package should
|
||||
// be considered "in app".
|
||||
func NewStacktrace(skip int, context int, appPackagePrefixes []string) *Stacktrace {
|
||||
var frames []*StacktraceFrame
|
||||
|
||||
callerPcs := make([]uintptr, 100)
|
||||
numCallers := runtime.Callers(skip+2, callerPcs)
|
||||
|
||||
// If there are no callers, the entire stacktrace is nil
|
||||
if numCallers == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
callersFrames := runtime.CallersFrames(callerPcs)
|
||||
|
||||
for {
|
||||
fr, more := callersFrames.Next()
|
||||
if fr.Func != nil {
|
||||
frame := NewStacktraceFrame(fr.PC, fr.Function, fr.File, fr.Line, context, appPackagePrefixes)
|
||||
if frame != nil {
|
||||
frames = append(frames, frame)
|
||||
}
|
||||
}
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
// If there are no frames, the entire stacktrace is nil
|
||||
if len(frames) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Optimize the path where there's only 1 frame
|
||||
if len(frames) == 1 {
|
||||
return &Stacktrace{frames}
|
||||
}
|
||||
// Sentry wants the frames with the oldest first, so reverse them
|
||||
for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 {
|
||||
frames[i], frames[j] = frames[j], frames[i]
|
||||
}
|
||||
return &Stacktrace{frames}
|
||||
}
|
||||
|
||||
// Build a single frame using data returned from runtime.Caller.
|
||||
//
|
||||
// context is the number of surrounding lines that should be included for context.
|
||||
// Setting context to 3 would try to get seven lines. Setting context to -1 returns
|
||||
// one line with no surrounding context, and 0 returns no context.
|
||||
//
|
||||
// appPackagePrefixes is a list of prefixes used to check whether a package should
|
||||
// be considered "in app".
|
||||
func NewStacktraceFrame(pc uintptr, fName, file string, line, context int, appPackagePrefixes []string) *StacktraceFrame {
|
||||
frame := &StacktraceFrame{AbsolutePath: file, Filename: trimPath(file), Lineno: line, InApp: false}
|
||||
frame.Module, frame.Function = functionName(fName)
|
||||
|
||||
// `runtime.goexit` is effectively a placeholder that comes from
|
||||
// runtime/asm_amd64.s and is meaningless.
|
||||
if frame.Module == "runtime" && frame.Function == "goexit" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if frame.Module == "main" {
|
||||
frame.InApp = true
|
||||
} else {
|
||||
for _, prefix := range appPackagePrefixes {
|
||||
if strings.HasPrefix(frame.Module, prefix) && !strings.Contains(frame.Module, "vendor") && !strings.Contains(frame.Module, "third_party") {
|
||||
frame.InApp = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if context > 0 {
|
||||
contextLines, lineIdx := sourceCodeLoader.Load(file, line, context)
|
||||
if len(contextLines) > 0 {
|
||||
for i, line := range contextLines {
|
||||
switch {
|
||||
case i < lineIdx:
|
||||
frame.PreContext = append(frame.PreContext, string(line))
|
||||
case i == lineIdx:
|
||||
frame.ContextLine = string(line)
|
||||
default:
|
||||
frame.PostContext = append(frame.PostContext, string(line))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if context == -1 {
|
||||
contextLine, _ := sourceCodeLoader.Load(file, line, 0)
|
||||
if len(contextLine) > 0 {
|
||||
frame.ContextLine = string(contextLine[0])
|
||||
}
|
||||
}
|
||||
return frame
|
||||
}
|
||||
|
||||
// Retrieve the name of the package and function containing the PC.
|
||||
func functionName(fName string) (pack string, name string) {
|
||||
name = fName
|
||||
// We get this:
|
||||
// runtime/debug.*T·ptrmethod
|
||||
// and want this:
|
||||
// pack = runtime/debug
|
||||
// name = *T.ptrmethod
|
||||
if idx := strings.LastIndex(name, "."); idx != -1 {
|
||||
pack = name[:idx]
|
||||
name = name[idx+1:]
|
||||
}
|
||||
name = strings.Replace(name, "·", ".", -1)
|
||||
return
|
||||
}
|
||||
|
||||
type SourceCodeLoader interface {
|
||||
Load(filename string, line, context int) ([][]byte, int)
|
||||
}
|
||||
|
||||
var sourceCodeLoader SourceCodeLoader = &fsLoader{cache: make(map[string][][]byte)}
|
||||
|
||||
func SetSourceCodeLoader(loader SourceCodeLoader) {
|
||||
sourceCodeLoader = loader
|
||||
}
|
||||
|
||||
type fsLoader struct {
|
||||
mu sync.Mutex
|
||||
cache map[string][][]byte
|
||||
}
|
||||
|
||||
func (fs *fsLoader) Load(filename string, line, context int) ([][]byte, int) {
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
lines, ok := fs.cache[filename]
|
||||
if !ok {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
// cache errors as nil slice: code below handles it correctly
|
||||
// otherwise when missing the source or running as a different user, we try
|
||||
// reading the file on each error which is unnecessary
|
||||
fs.cache[filename] = nil
|
||||
return nil, 0
|
||||
}
|
||||
lines = bytes.Split(data, []byte{'\n'})
|
||||
fs.cache[filename] = lines
|
||||
}
|
||||
|
||||
if lines == nil {
|
||||
// cached error from ReadFile: return no lines
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
line-- // stack trace lines are 1-indexed
|
||||
start := line - context
|
||||
var idx int
|
||||
if start < 0 {
|
||||
start = 0
|
||||
idx = line
|
||||
} else {
|
||||
idx = context
|
||||
}
|
||||
end := line + context + 1
|
||||
if line >= len(lines) {
|
||||
return nil, 0
|
||||
}
|
||||
if end > len(lines) {
|
||||
end = len(lines)
|
||||
}
|
||||
return lines[start:end], idx
|
||||
}
|
||||
|
||||
var trimPaths []string
|
||||
|
||||
// Try to trim the GOROOT or GOPATH prefix off of a filename
|
||||
func trimPath(filename string) string {
|
||||
for _, prefix := range trimPaths {
|
||||
if trimmed := strings.TrimPrefix(filename, prefix); len(trimmed) < len(filename) {
|
||||
return trimmed
|
||||
}
|
||||
}
|
||||
return filename
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Collect all source directories, and make sure they
|
||||
// end in a trailing "separator"
|
||||
for _, prefix := range build.Default.SrcDirs() {
|
||||
if prefix[len(prefix)-1] != filepath.Separator {
|
||||
prefix += string(filepath.Separator)
|
||||
}
|
||||
trimPaths = append(trimPaths, prefix)
|
||||
}
|
||||
}
|
20
vendor/github.com/getsentry/raven-go/writer.go
generated
vendored
20
vendor/github.com/getsentry/raven-go/writer.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
package raven
|
||||
|
||||
type Writer struct {
|
||||
Client *Client
|
||||
Level Severity
|
||||
Logger string // Logger name reported to Sentry
|
||||
}
|
||||
|
||||
// Write formats the byte slice p into a string, and sends a message to
|
||||
// Sentry at the severity level indicated by the Writer w.
|
||||
func (w *Writer) Write(p []byte) (int, error) {
|
||||
message := string(p)
|
||||
|
||||
packet := NewPacket(message, &Message{message, nil})
|
||||
packet.Level = w.Level
|
||||
packet.Logger = w.Logger
|
||||
w.Client.Capture(packet, nil)
|
||||
|
||||
return len(p), nil
|
||||
}
|
12
vendor/github.com/getsentry/sentry-go/.craft.yml
generated
vendored
Normal file
12
vendor/github.com/getsentry/sentry-go/.craft.yml
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
github:
|
||||
owner: getsentry
|
||||
repo: sentry-go
|
||||
preReleaseCommand: bash scripts/craft-pre-release.sh
|
||||
changelogPolicy: simple
|
||||
targets:
|
||||
- name: github
|
||||
tagPrefix: v
|
||||
- name: registry
|
||||
type: sdk
|
||||
config:
|
||||
canonical: "github:getsentry/sentry-go"
|
6
vendor/github.com/getsentry/sentry-go/.gitignore
generated
vendored
Normal file
6
vendor/github.com/getsentry/sentry-go/.gitignore
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
coverage.txt
|
||||
|
||||
# Just my personal way of tracking stuff — Kamil
|
||||
FIXME.md
|
||||
TODO.md
|
||||
!NOTES.md
|
9
vendor/github.com/getsentry/sentry-go/.golangci.yml
generated
vendored
Normal file
9
vendor/github.com/getsentry/sentry-go/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- megacheck
|
||||
- stylecheck
|
||||
run:
|
||||
skip-dirs:
|
||||
- echo
|
||||
- example/echo
|
29
vendor/github.com/getsentry/sentry-go/.travis.yml
generated
vendored
Normal file
29
vendor/github.com/getsentry/sentry-go/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
sudo: false
|
||||
|
||||
language: go
|
||||
go:
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
before_install:
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.15.0
|
||||
|
||||
script:
|
||||
- golangci-lint run
|
||||
- go build
|
||||
- go test
|
||||
- go test -race
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://zeus.ci/hooks/befe9810-9285-11e9-b01a-0a580a281808/public/provider/travis/webhook
|
||||
on_success: always
|
||||
on_failure: always
|
||||
on_start: always
|
||||
on_cancel: always
|
||||
on_error: always
|
104
vendor/github.com/getsentry/sentry-go/CHANGELOG.md
generated
vendored
Normal file
104
vendor/github.com/getsentry/sentry-go/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
# Changelog
|
||||
|
||||
## v0.2.1
|
||||
|
||||
- fix: Run `Contextify` integration on `Threads` as well
|
||||
|
||||
## v0.2.0
|
||||
|
||||
- feat: Add `SetTransaction()` method on the `Scope`
|
||||
- feat: `fasthttp` framework support with `sentryfasthttp` package
|
||||
- fix: Add `RWMutex` locks to internal `Hub` and `Scope` changes
|
||||
|
||||
## v0.1.3
|
||||
|
||||
- feat: Move frames context reading into `contextifyFramesIntegration` (#28)
|
||||
|
||||
_NOTE:_
|
||||
In case of any performance isues due to source contexts IO, you can let us know and turn off the integration in the meantime with:
|
||||
|
||||
```go
|
||||
sentry.Init(sentry.ClientOptions{
|
||||
Integrations: func(integrations []sentry.Integration) []sentry.Integration {
|
||||
var filteredIntegrations []sentry.Integration
|
||||
for _, integration := range integrations {
|
||||
if integration.Name() == "ContextifyFrames" {
|
||||
continue
|
||||
}
|
||||
filteredIntegrations = append(filteredIntegrations, integration)
|
||||
}
|
||||
return filteredIntegrations
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## v0.1.2
|
||||
|
||||
- feat: Better source code location resolution and more useful inapp frames (#26)
|
||||
- feat: Use `noopTransport` when no `Dsn` provided (#27)
|
||||
- ref: Allow empty `Dsn` instead of returning an error (#22)
|
||||
- fix: Use `NewScope` instead of literal struct inside a `scope.Clear` call (#24)
|
||||
- fix: Add to `WaitGroup` before the request is put inside a buffer (#25)
|
||||
|
||||
## v0.1.1
|
||||
|
||||
- fix: Check for initialized `Client` in `AddBreadcrumbs` (#20)
|
||||
- build: Bump version when releasing with Craft (#19)
|
||||
|
||||
## v0.1.0
|
||||
|
||||
- First stable release! \o/
|
||||
|
||||
## v0.0.1-beta.5
|
||||
|
||||
- feat: **[breaking]** Add `NewHTTPTransport` and `NewHTTPSyncTransport` which accepts all transport options
|
||||
- feat: New `HTTPSyncTransport` that blocks after each call
|
||||
- feat: New `Echo` integration
|
||||
- ref: **[breaking]** Remove `BufferSize` option from `ClientOptions` and move it to `HTTPTransport` instead
|
||||
- ref: Export default `HTTPTransport`
|
||||
- ref: Export `net/http` integration handler
|
||||
- ref: Set `Request` instantly in the package handlers, not in `recoverWithSentry` so it can be accessed later on
|
||||
- ci: Add craft config
|
||||
|
||||
## v0.0.1-beta.4
|
||||
|
||||
- feat: `IgnoreErrors` client option and corresponding integration
|
||||
- ref: Reworked `net/http` integration, wrote better example and complete readme
|
||||
- ref: Reworked `Gin` integration, wrote better example and complete readme
|
||||
- ref: Reworked `Iris` integration, wrote better example and complete readme
|
||||
- ref: Reworked `Negroni` integration, wrote better example and complete readme
|
||||
- ref: Reworked `Martini` integration, wrote better example and complete readme
|
||||
- ref: Remove `Handle()` from frameworks handlers and return it directly from New
|
||||
|
||||
## v0.0.1-beta.3
|
||||
|
||||
- feat: `Iris` framework support with `sentryiris` package
|
||||
- feat: `Gin` framework support with `sentrygin` package
|
||||
- feat: `Martini` framework support with `sentrymartini` package
|
||||
- feat: `Negroni` framework support with `sentrynegroni` package
|
||||
- feat: Add `Hub.Clone()` for easier frameworks integration
|
||||
- feat: Return `EventID` from `Recovery` methods
|
||||
- feat: Add `NewScope` and `NewEvent` functions and use them in the whole codebase
|
||||
- feat: Add `AddEventProcessor` to the `Client`
|
||||
- fix: Operate on requests body copy instead of the original
|
||||
- ref: Try to read source files from the root directory, based on the filename as well, to make it work on AWS Lambda
|
||||
- ref: Remove `gocertifi` dependence and document how to provide your own certificates
|
||||
- ref: **[breaking]** Remove `Decorate` and `DecorateFunc` methods in favor of `sentryhttp` package
|
||||
- ref: **[breaking]** Allow for integrations to live on the client, by passing client instance in `SetupOnce` method
|
||||
- ref: **[breaking]** Remove `GetIntegration` from the `Hub`
|
||||
- ref: **[breaking]** Remove `GlobalEventProcessors` getter from the public API
|
||||
|
||||
## v0.0.1-beta.2
|
||||
|
||||
- feat: Add `AttachStacktrace` client option to include stacktrace for messages
|
||||
- feat: Add `BufferSize` client option to configure transport buffer size
|
||||
- feat: Add `SetRequest` method on a `Scope` to control `Request` context data
|
||||
- feat: Add `FromHTTPRequest` for `Request` type for easier extraction
|
||||
- ref: Extract `Request` information more accurately
|
||||
- fix: Attach `ServerName`, `Release`, `Dist`, `Environment` options to the event
|
||||
- fix: Don't log events dropped due to full transport buffer as sent
|
||||
- fix: Don't panic and create an appropriate event when called `CaptureException` or `Recover` with `nil` value
|
||||
|
||||
## v0.0.1-beta
|
||||
|
||||
- Initial release
|
41
vendor/github.com/getsentry/sentry-go/CONTRIBUTION.md
generated
vendored
Normal file
41
vendor/github.com/getsentry/sentry-go/CONTRIBUTION.md
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
$ go test
|
||||
```
|
||||
|
||||
### Watch mode
|
||||
|
||||
Use: https://github.com/cespare/reflex
|
||||
|
||||
```bash
|
||||
$ reflex -g '*.go' -d "none" -- sh -c 'printf "\n"; go test'
|
||||
```
|
||||
|
||||
### With data race detection
|
||||
|
||||
```bash
|
||||
$ go test -race
|
||||
```
|
||||
|
||||
### Coverage
|
||||
```bash
|
||||
$ go test -race -coverprofile=coverage.txt -covermode=atomic && go tool cover -html coverage.txt
|
||||
```
|
||||
|
||||
## Linting
|
||||
|
||||
```bash
|
||||
$ golangci-lint run
|
||||
```
|
||||
|
||||
## Release
|
||||
|
||||
1. Update changelog with new version in `vX.X.X` format title and list of changes
|
||||
2. Commit with `misc: vX.X.X changelog` commit message and push to `master`
|
||||
3. Let `craft` do the rest
|
||||
|
||||
```bash
|
||||
$ craft prepare X.X.X
|
||||
$ craft publish X.X.X --skip-status-check
|
||||
```
|
9
vendor/github.com/getsentry/sentry-go/LICENSE
generated
vendored
Normal file
9
vendor/github.com/getsentry/sentry-go/LICENSE
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
Copyright (c) 2019 Sentry (https://sentry.io) and individual contributors.
|
||||
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.
|
392
vendor/github.com/getsentry/sentry-go/MIGRATION.md
generated
vendored
Normal file
392
vendor/github.com/getsentry/sentry-go/MIGRATION.md
generated
vendored
Normal file
@ -0,0 +1,392 @@
|
||||
# `raven-go` to `sentry-go` Migration Guide
|
||||
|
||||
## Installation
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
go get github.com/getsentry/raven-go
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
go get github.com/getsentry/sentry-go@v0.0.1
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
import "github.com/getsentry/raven-go"
|
||||
|
||||
func main() {
|
||||
raven.SetDSN("https://16427b2f210046b585ee51fd8a1ac54f@sentry.io/1")
|
||||
}
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: "https://16427b2f210046b585ee51fd8a1ac54f@sentry.io/1",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Sentry initialization failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
SetDSN()
|
||||
SetDefaultLoggerName()
|
||||
SetDebug()
|
||||
SetEnvironment()
|
||||
SetRelease()
|
||||
SetSampleRate()
|
||||
SetIgnoreErrors()
|
||||
SetIncludePaths()
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
sentry.Init(sentry.ClientOptions{
|
||||
Dsn: "https://16427b2f210046b585ee51fd8a1ac54f@sentry.io/1",
|
||||
DebugWriter: os.Stderr,
|
||||
Debug: true,
|
||||
Environment: "environment",
|
||||
Release: "release",
|
||||
SampleRate: 0.5,
|
||||
// IgnoreErrors: TBD,
|
||||
// IncludePaths: TBD
|
||||
})
|
||||
```
|
||||
|
||||
Available options: see [Configuration](https://docs.sentry.io/platforms/go/config/) section.
|
||||
|
||||
### Providing SSL Certificates
|
||||
|
||||
By default, TLS uses the host's root CA set. If you don't have `ca-certificates` (which should be your go-to way of fixing the issue of missing ceritificates) and want to use `gocertifi` instead, you can provide pre-loaded cert files as one of the options to the `sentry.Init` call:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/certifi/gocertifi"
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
sentryClientOptions := sentry.ClientOptions{
|
||||
Dsn: "https://16427b2f210046b585ee51fd8a1ac54f@sentry.io/1",
|
||||
}
|
||||
|
||||
rootCAs, err := gocertifi.CACerts()
|
||||
if err != nil {
|
||||
log.Println("Coudnt load CA Certificates: %v\n", err)
|
||||
} else {
|
||||
sentryClientOptions.CaCerts = rootCAs
|
||||
}
|
||||
|
||||
sentry.Init(sentryClientOptions)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Capturing Errors
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
f, err := os.Open("filename.ext")
|
||||
if err != nil {
|
||||
raven.CaptureError(err, nil)
|
||||
}
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
f, err := os.Open("filename.ext")
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
}
|
||||
```
|
||||
|
||||
### Capturing Panics
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
raven.CapturePanic(func() {
|
||||
// do all of the scary things here
|
||||
}, nil)
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
func() {
|
||||
defer sentry.Recover()
|
||||
// do all of the scary things here
|
||||
}()
|
||||
```
|
||||
|
||||
### Capturing Messages
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
raven.CaptureMessage("Something bad happened and I would like to know about that")
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
sentry.CaptureMessage("Something bad happened and I would like to know about that")
|
||||
```
|
||||
|
||||
### Capturing Events
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
packet := &raven.Packet{
|
||||
Message: "Hand-crafted event",
|
||||
Extra: &raven.Extra{
|
||||
"runtime.Version": runtime.Version(),
|
||||
"runtime.NumCPU": runtime.NumCPU(),
|
||||
},
|
||||
}
|
||||
raven.Capture(packet)
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
event := &sentry.NewEvent()
|
||||
event.Message = "Hand-crafted event"
|
||||
event.Extra["runtime.Version"] = runtime.Version()
|
||||
event.Extra["runtime.NumCPU"] = runtime.NumCPU()
|
||||
|
||||
sentry.CaptureEvent(event)
|
||||
```
|
||||
|
||||
### Additional Data
|
||||
|
||||
See Context section.
|
||||
|
||||
### Event Sampling
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
raven.SetSampleRate(0.25)
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
sentry.Init(sentry.ClientOptions{
|
||||
SampleRate: 0.25,
|
||||
})
|
||||
```
|
||||
|
||||
### Awaiting the response (not recommended)
|
||||
|
||||
```go
|
||||
raven.CaptureMessageAndWait("Something bad happened and I would like to know about that")
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
sentry.CaptureMessage("Something bad happened and I would like to know about that")
|
||||
|
||||
if sentry.Flush(time.Second * 2) {
|
||||
// event delivered
|
||||
} else {
|
||||
// timeout reached
|
||||
}
|
||||
```
|
||||
|
||||
## Context
|
||||
|
||||
### Per-event
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
raven.CaptureError(err, map[string]string{"browser": "Firefox"}, &raven.Http{
|
||||
Method: "GET",
|
||||
URL: "https://example.com/raven-go"
|
||||
})
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
sentry.WithScope(func(scope *sentry.Scope) {
|
||||
scope.SetTag("browser", "Firefox")
|
||||
scope.SetContext("Request", map[string]string{
|
||||
"Method": "GET",
|
||||
"URL": "https://example.com/raven-go",
|
||||
})
|
||||
sentry.CaptureException(err)
|
||||
})
|
||||
```
|
||||
|
||||
### Globally
|
||||
|
||||
#### SetHttpContext
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
raven.SetHttpContext(&raven.Http{
|
||||
Method: "GET",
|
||||
URL: "https://example.com/raven-go",
|
||||
})
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
||||
scope.SetContext("Request", map[string]string{
|
||||
"Method": "GET",
|
||||
"URL": "https://example.com/raven-go",
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
#### SetTagsContext
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
t := map[string]string{"day": "Friday", "sport": "Weightlifting"}
|
||||
raven.SetTagsContext(map[string]string{"day": "Friday", "sport": "Weightlifting"})
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
||||
scope.SetTags(map[string]string{"day": "Friday", "sport": "Weightlifting"})
|
||||
})
|
||||
```
|
||||
|
||||
#### SetUserContext
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
raven.SetUserContext(&raven.User{
|
||||
ID: "1337",
|
||||
Username: "kamilogorek",
|
||||
Email: "kamil@sentry.io",
|
||||
IP: "127.0.0.1",
|
||||
})
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
||||
scope.SetUser(sentry.User{
|
||||
ID: "1337",
|
||||
Username: "kamilogorek",
|
||||
Email: "kamil@sentry.io",
|
||||
IPAddress: "127.0.0.1",
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
#### ClearContext
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
raven.ClearContext()
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
||||
scope.Clear()
|
||||
})
|
||||
```
|
||||
|
||||
#### WrapWithExtra
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
path := "filename.ext"
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
err = raven.WrapWithExtra(err, map[string]string{"path": path, "cwd": os.Getwd()}
|
||||
raven.CaptureError(err, nil)
|
||||
}
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
// use `sentry.WithScope`, see "Context / Per-event Section"
|
||||
path := "filename.ext"
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
sentry.WithScope(func(scope *sentry.Scope) {
|
||||
sentry.SetExtras(map[string]interface{}{"path": path, "cwd": os.Getwd())
|
||||
sentry.CaptureException(err)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Integrations
|
||||
|
||||
### net/http
|
||||
|
||||
raven-go
|
||||
|
||||
```go
|
||||
mux := http.NewServeMux
|
||||
http.Handle("/", raven.Recoverer(mux))
|
||||
|
||||
// or
|
||||
|
||||
func root(w http.ResponseWriter, r *http.Request) {}
|
||||
http.HandleFunc("/", raven.RecoveryHandler(root))
|
||||
```
|
||||
|
||||
sentry-go
|
||||
|
||||
```go
|
||||
sentryHandler := sentryhttp.New(sentryhttp.Options{
|
||||
Repanic: false,
|
||||
WaitForDelivery: true,
|
||||
})
|
||||
|
||||
mux := http.NewServeMux
|
||||
http.Handle("/", sentryHandler.Handle(mux))
|
||||
|
||||
// or
|
||||
|
||||
func root(w http.ResponseWriter, r *http.Request) {}
|
||||
http.HandleFunc("/", sentryHandler.HandleFunc(root))
|
||||
```
|
108
vendor/github.com/getsentry/sentry-go/README.md
generated
vendored
Normal file
108
vendor/github.com/getsentry/sentry-go/README.md
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
<p align="center">
|
||||
<a href="https://sentry.io" target="_blank" align="center">
|
||||
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" width="280">
|
||||
</a>
|
||||
<br />
|
||||
</p>
|
||||
|
||||
# Official Sentry SDK for Go
|
||||
|
||||
[![Build Status](https://travis-ci.com/getsentry/sentry-go.svg?branch=master)](https://travis-ci.com/getsentry/sentry-go)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/getsentry/sentry-go)](https://goreportcard.com/report/github.com/getsentry/sentry-go)
|
||||
|
||||
`sentry-go` provides a Sentry client implementation for the Go programming language. This is the next line of the Go SDK for [Sentry](https://sentry.io/), intended to replace the `raven-go` package.
|
||||
|
||||
> Looking for the old `raven-go` SDK documentation? See the Legacy client section [here](https://docs.sentry.io/clients/go/).
|
||||
> If you want to start using sentry-go instead, check out the [migration guide](https://docs.sentry.io/platforms/go/migration/).
|
||||
|
||||
## Requirements
|
||||
|
||||
We verify this package against N-2 recent versions of Go compiler. As of June 2019, those versions are:
|
||||
|
||||
* 1.10
|
||||
* 1.11
|
||||
* 1.12
|
||||
|
||||
## Installation
|
||||
|
||||
`sentry-go` can be installed like any other Go library through `go get`:
|
||||
|
||||
```bash
|
||||
$ go get github.com/getsentry/sentry-go
|
||||
```
|
||||
|
||||
Or, if you are already using Go Modules, specify a version number as well:
|
||||
|
||||
```bash
|
||||
$ go get github.com/getsentry/sentry-go@v0.1.0
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
To use `sentry-go`, you’ll need to import the `sentry-go` package and initialize it with the client options that will include your DSN. If you specify the `SENTRY_DSN` environment variable, you can omit this value from options and it will be picked up automatically for you. The release and environment can also be specified in the environment variables `SENTRY_RELEASE` and `SENTRY_ENVIRONMENT` respectively.
|
||||
|
||||
More on this in [Configuration](https://docs.sentry.io/platforms/go/config/) section.
|
||||
|
||||
## Usage
|
||||
|
||||
By default, Sentry Go SDK uses asynchronous transport, which in the code example below requires an explicit awaiting for event delivery to be finished using `sentry.Flush` method. It is necessary, because otherwise the program would not wait for the async HTTP calls to return a response, and exit the process immediately when it reached the end of the `main` function. It would not be required inside a running goroutine or if you would use `HTTPSyncTransport`, which you can read about in `Transports` section.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: "___DSN___",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Sentry initialization failed: %v\n", err)
|
||||
}
|
||||
|
||||
f, err := os.Open("filename.ext")
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
sentry.Flush(time.Second * 5)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more detailed information about how to get the most out of `sentry-go` there is additional documentation available:
|
||||
|
||||
- [Configuration](https://docs.sentry.io/platforms/go/config)
|
||||
- [Error Reporting](https://docs.sentry.io/error-reporting/quickstart?platform=go)
|
||||
- [Enriching Error Data](https://docs.sentry.io/enriching-error-data/context?platform=go)
|
||||
- [Transports](https://docs.sentry.io/platforms/go/transports)
|
||||
- [Integrations](https://docs.sentry.io/platforms/go/integrations)
|
||||
- [net/http](https://docs.sentry.io/platforms/go/http)
|
||||
- [echo](https://docs.sentry.io/platforms/go/echo)
|
||||
- [fasthttp](https://docs.sentry.io/platforms/go/fasthttp)
|
||||
- [gin](https://docs.sentry.io/platforms/go/gin)
|
||||
- [iris](https://docs.sentry.io/platforms/go/iris)
|
||||
- [martini](https://docs.sentry.io/platforms/go/martini)
|
||||
- [negroni](https://docs.sentry.io/platforms/go/negroni)
|
||||
|
||||
## Resources:
|
||||
|
||||
- [Bug Tracker](https://github.com/getsentry/sentry-go/issues)
|
||||
- [GitHub Project](https://github.com/getsentry/sentry-go)
|
||||
- [Godocs](https://godoc.org/github.com/getsentry/sentry-go)
|
||||
- [@getsentry](https://twitter.com/getsentry) on Twitter for updates
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the BSD license, see `LICENSE`
|
||||
|
||||
## Community
|
||||
|
||||
Want to join our Sentry's `community-golang` channel, get involved and help us improve the SDK?
|
||||
|
||||
Do not hesistate to shoot me up an email at [kamil@sentry.io](mailto:kamil@sentry.io) for Slack invite!
|
429
vendor/github.com/getsentry/sentry-go/client.go
generated
vendored
Normal file
429
vendor/github.com/getsentry/sentry-go/client.go
generated
vendored
Normal file
@ -0,0 +1,429 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Logger is an instance of log.Logger that is use to provide debug information about running Sentry Client
|
||||
// can be enabled by either using `Logger.SetOutput` directly or with `Debug` client option
|
||||
var Logger = log.New(ioutil.Discard, "[Sentry] ", log.LstdFlags) // nolint: gochecknoglobals
|
||||
|
||||
type EventProcessor func(event *Event, hint *EventHint) *Event
|
||||
|
||||
type EventModifier interface {
|
||||
ApplyToEvent(event *Event, hint *EventHint) *Event
|
||||
}
|
||||
|
||||
var globalEventProcessors []EventProcessor // nolint: gochecknoglobals
|
||||
|
||||
func AddGlobalEventProcessor(processor EventProcessor) {
|
||||
globalEventProcessors = append(globalEventProcessors, processor)
|
||||
}
|
||||
|
||||
// Integration allows for registering a functions that modify or discard captured events.
|
||||
type Integration interface {
|
||||
Name() string
|
||||
SetupOnce(client *Client)
|
||||
}
|
||||
|
||||
// ClientOptions that configures a SDK Client
|
||||
type ClientOptions struct {
|
||||
// The DSN to use. If the DSN is not set, the client is effectively disabled.
|
||||
Dsn string
|
||||
// In debug mode, the debug information is printed to stdout to help you understand what
|
||||
// sentry is doing.
|
||||
Debug bool
|
||||
// Configures whether SDK should generate and attach stacktraces to pure capture message calls.
|
||||
AttachStacktrace bool
|
||||
// The sample rate for event submission (0.0 - 1.0, defaults to 1.0).
|
||||
SampleRate float32
|
||||
// List of regexp strings that will be used to match against event's message
|
||||
// and if applicable, caught errors type and value.
|
||||
// If the match is found, then a whole event will be dropped.
|
||||
IgnoreErrors []string
|
||||
// Before send callback.
|
||||
BeforeSend func(event *Event, hint *EventHint) *Event
|
||||
// Before breadcrumb add callback.
|
||||
BeforeBreadcrumb func(breadcrumb *Breadcrumb, hint *BreadcrumbHint) *Breadcrumb
|
||||
// Integrations to be installed on the current Client, receives default integrations
|
||||
Integrations func([]Integration) []Integration
|
||||
// io.Writer implementation that should be used with the `Debug` mode
|
||||
DebugWriter io.Writer
|
||||
// The transport to use.
|
||||
// This is an instance of a struct implementing `Transport` interface.
|
||||
// Defaults to `httpTransport` from `transport.go`
|
||||
Transport Transport
|
||||
// The server name to be reported.
|
||||
ServerName string
|
||||
// The release to be sent with events.
|
||||
Release string
|
||||
// The dist to be sent with events.
|
||||
Dist string
|
||||
// The environment to be sent with events.
|
||||
Environment string
|
||||
// Maximum number of breadcrumbs.
|
||||
MaxBreadcrumbs int
|
||||
// An optional pointer to `http.Transport` that will be used with a default HTTPTransport.
|
||||
HTTPTransport *http.Transport
|
||||
// An optional HTTP proxy to use.
|
||||
// This will default to the `http_proxy` environment variable.
|
||||
// or `https_proxy` if that one exists.
|
||||
HTTPProxy string
|
||||
// An optional HTTPS proxy to use.
|
||||
// This will default to the `HTTPS_PROXY` environment variable
|
||||
// or `http_proxy` if that one exists.
|
||||
HTTPSProxy string
|
||||
// An optionsl CaCerts to use.
|
||||
// Defaults to `gocertifi.CACerts()`.
|
||||
CaCerts *x509.CertPool
|
||||
}
|
||||
|
||||
// Client is the underlying processor that's used by the main API and `Hub` instances.
|
||||
type Client struct {
|
||||
options ClientOptions
|
||||
dsn *Dsn
|
||||
eventProcessors []EventProcessor
|
||||
integrations []Integration
|
||||
Transport Transport
|
||||
}
|
||||
|
||||
// NewClient creates and returns an instance of `Client` configured using `ClientOptions`.
|
||||
func NewClient(options ClientOptions) (*Client, error) {
|
||||
if options.Debug {
|
||||
debugWriter := options.DebugWriter
|
||||
if debugWriter == nil {
|
||||
debugWriter = os.Stdout
|
||||
}
|
||||
Logger.SetOutput(debugWriter)
|
||||
}
|
||||
|
||||
if options.Dsn == "" {
|
||||
options.Dsn = os.Getenv("SENTRY_DSN")
|
||||
}
|
||||
|
||||
if options.Release == "" {
|
||||
options.Release = os.Getenv("SENTRY_RELEASE")
|
||||
}
|
||||
|
||||
if options.Environment == "" {
|
||||
options.Environment = os.Getenv("SENTRY_ENVIRONMENT")
|
||||
}
|
||||
|
||||
var dsn *Dsn
|
||||
if options.Dsn != "" {
|
||||
var err error
|
||||
dsn, err = NewDsn(options.Dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
client := Client{
|
||||
options: options,
|
||||
dsn: dsn,
|
||||
}
|
||||
|
||||
client.setupTransport()
|
||||
client.setupIntegrations()
|
||||
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
func (client *Client) setupTransport() {
|
||||
transport := client.options.Transport
|
||||
|
||||
if transport == nil {
|
||||
if client.options.Dsn == "" {
|
||||
transport = new(noopTransport)
|
||||
} else {
|
||||
transport = NewHTTPTransport()
|
||||
}
|
||||
}
|
||||
|
||||
transport.Configure(client.options)
|
||||
client.Transport = transport
|
||||
}
|
||||
|
||||
func (client *Client) setupIntegrations() {
|
||||
integrations := []Integration{
|
||||
new(contextifyFramesIntegration),
|
||||
new(environmentIntegration),
|
||||
new(modulesIntegration),
|
||||
new(ignoreErrorsIntegration),
|
||||
}
|
||||
|
||||
if client.options.Integrations != nil {
|
||||
integrations = client.options.Integrations(integrations)
|
||||
}
|
||||
|
||||
for _, integration := range integrations {
|
||||
if client.integrationAlreadyInstalled(integration.Name()) {
|
||||
Logger.Printf("Integration %s is already installed\n", integration.Name())
|
||||
continue
|
||||
}
|
||||
client.integrations = append(client.integrations, integration)
|
||||
integration.SetupOnce(client)
|
||||
Logger.Printf("Integration installed: %s\n", integration.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// AddEventProcessor adds an event processor to the client.
|
||||
func (client *Client) AddEventProcessor(processor EventProcessor) {
|
||||
client.eventProcessors = append(client.eventProcessors, processor)
|
||||
}
|
||||
|
||||
// Options return `ClientOptions` for the current `Client`.
|
||||
func (client Client) Options() ClientOptions {
|
||||
return client.options
|
||||
}
|
||||
|
||||
// CaptureMessage captures an arbitrary message.
|
||||
func (client *Client) CaptureMessage(message string, hint *EventHint, scope EventModifier) *EventID {
|
||||
event := client.eventFromMessage(message, LevelInfo)
|
||||
return client.CaptureEvent(event, hint, scope)
|
||||
}
|
||||
|
||||
// CaptureException captures an error.
|
||||
func (client *Client) CaptureException(exception error, hint *EventHint, scope EventModifier) *EventID {
|
||||
event := client.eventFromException(exception, LevelError)
|
||||
return client.CaptureEvent(event, hint, scope)
|
||||
}
|
||||
|
||||
// CaptureEvent captures an event on the currently active client if any.
|
||||
//
|
||||
// The event must already be assembled. Typically code would instead use
|
||||
// the utility methods like `CaptureException`. The return value is the
|
||||
// event ID. In case Sentry is disabled or event was dropped, the return value will be nil.
|
||||
func (client *Client) CaptureEvent(event *Event, hint *EventHint, scope EventModifier) *EventID {
|
||||
return client.processEvent(event, hint, scope)
|
||||
}
|
||||
|
||||
// Recover captures a panic.
|
||||
// Returns `EventID` if successfully, or `nil` if there's no error to recover from.
|
||||
func (client *Client) Recover(err interface{}, hint *EventHint, scope EventModifier) *EventID {
|
||||
if err == nil {
|
||||
err = recover()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err, ok := err.(error); ok {
|
||||
event := client.eventFromException(err, LevelFatal)
|
||||
return client.CaptureEvent(event, hint, scope)
|
||||
}
|
||||
|
||||
if err, ok := err.(string); ok {
|
||||
event := client.eventFromMessage(err, LevelFatal)
|
||||
return client.CaptureEvent(event, hint, scope)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Recover captures a panic and passes relevant context object.
|
||||
// Returns `EventID` if successfully, or `nil` if there's no error to recover from.
|
||||
func (client *Client) RecoverWithContext(
|
||||
ctx context.Context,
|
||||
err interface{},
|
||||
hint *EventHint,
|
||||
scope EventModifier,
|
||||
) *EventID {
|
||||
if err == nil {
|
||||
err = recover()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if hint.Context == nil && ctx != nil {
|
||||
hint.Context = ctx
|
||||
}
|
||||
|
||||
if err, ok := err.(error); ok {
|
||||
event := client.eventFromException(err, LevelFatal)
|
||||
return client.CaptureEvent(event, hint, scope)
|
||||
}
|
||||
|
||||
if err, ok := err.(string); ok {
|
||||
event := client.eventFromMessage(err, LevelFatal)
|
||||
return client.CaptureEvent(event, hint, scope)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush notifies when all the buffered events have been sent by returning `true`
|
||||
// or `false` if timeout was reached. It calls `Flush` method of the configured `Transport`.
|
||||
func (client *Client) Flush(timeout time.Duration) bool {
|
||||
return client.Transport.Flush(timeout)
|
||||
}
|
||||
|
||||
func (client *Client) eventFromMessage(message string, level Level) *Event {
|
||||
event := NewEvent()
|
||||
event.Level = level
|
||||
event.Message = message
|
||||
|
||||
if client.Options().AttachStacktrace {
|
||||
event.Threads = []Thread{{
|
||||
Stacktrace: NewStacktrace(),
|
||||
Crashed: false,
|
||||
Current: true,
|
||||
}}
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func (client *Client) eventFromException(exception error, level Level) *Event {
|
||||
if exception == nil {
|
||||
event := NewEvent()
|
||||
event.Level = level
|
||||
event.Message = fmt.Sprintf("Called %s with nil value", callerFunctionName())
|
||||
return event
|
||||
}
|
||||
|
||||
stacktrace := ExtractStacktrace(exception)
|
||||
|
||||
if stacktrace == nil {
|
||||
stacktrace = NewStacktrace()
|
||||
}
|
||||
|
||||
event := NewEvent()
|
||||
event.Level = level
|
||||
event.Exception = []Exception{{
|
||||
Value: exception.Error(),
|
||||
Type: reflect.TypeOf(exception).String(),
|
||||
Stacktrace: stacktrace,
|
||||
}}
|
||||
return event
|
||||
}
|
||||
|
||||
func (client *Client) processEvent(event *Event, hint *EventHint, scope EventModifier) *EventID {
|
||||
options := client.Options()
|
||||
|
||||
// TODO: Reconsider if its worth going away from default implementation
|
||||
// of other SDKs. In Go zero value (default) for float32 is 0.0,
|
||||
// which means that if someone uses ClientOptions{} struct directly
|
||||
// and we would not check for 0 here, we'd skip all events by default
|
||||
if options.SampleRate != 0.0 {
|
||||
randomFloat := rand.New(rand.NewSource(time.Now().UnixNano())).Float32()
|
||||
if randomFloat > options.SampleRate {
|
||||
Logger.Println("Event dropped due to SampleRate hit.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if event = client.prepareEvent(event, hint, scope); event == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if options.BeforeSend != nil {
|
||||
h := &EventHint{}
|
||||
if hint != nil {
|
||||
h = hint
|
||||
}
|
||||
if event = options.BeforeSend(event, h); event == nil {
|
||||
Logger.Println("Event dropped due to BeforeSend callback.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
client.Transport.SendEvent(event)
|
||||
|
||||
return &event.EventID
|
||||
}
|
||||
|
||||
func (client *Client) prepareEvent(event *Event, hint *EventHint, scope EventModifier) *Event {
|
||||
if event.EventID == "" {
|
||||
event.EventID = EventID(uuid())
|
||||
}
|
||||
|
||||
if event.Timestamp == 0 {
|
||||
event.Timestamp = time.Now().Unix()
|
||||
}
|
||||
|
||||
if event.Level == "" {
|
||||
event.Level = LevelInfo
|
||||
}
|
||||
|
||||
if event.ServerName == "" {
|
||||
if client.Options().ServerName != "" {
|
||||
event.ServerName = client.Options().ServerName
|
||||
} else if hostname, err := os.Hostname(); err == nil {
|
||||
event.ServerName = hostname
|
||||
}
|
||||
}
|
||||
|
||||
if event.Release == "" && client.Options().Release != "" {
|
||||
event.Release = client.Options().Release
|
||||
}
|
||||
|
||||
if event.Dist == "" && client.Options().Dist != "" {
|
||||
event.Dist = client.Options().Dist
|
||||
}
|
||||
|
||||
if event.Environment == "" && client.Options().Environment != "" {
|
||||
event.Environment = client.Options().Environment
|
||||
}
|
||||
|
||||
event.Platform = "go"
|
||||
event.Sdk = SdkInfo{
|
||||
Name: "sentry.go",
|
||||
Version: Version,
|
||||
Integrations: client.listIntegrations(),
|
||||
Packages: []SdkPackage{{
|
||||
Name: "sentry-go",
|
||||
Version: Version,
|
||||
}},
|
||||
}
|
||||
|
||||
event = scope.ApplyToEvent(event, hint)
|
||||
|
||||
for _, processor := range client.eventProcessors {
|
||||
id := event.EventID
|
||||
event = processor(event, hint)
|
||||
if event == nil {
|
||||
Logger.Printf("Event dropped by one of the Client EventProcessors: %s\n", id)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, processor := range globalEventProcessors {
|
||||
id := event.EventID
|
||||
event = processor(event, hint)
|
||||
if event == nil {
|
||||
Logger.Printf("Event dropped by one of the Global EventProcessors: %s\n", id)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func (client Client) listIntegrations() []string {
|
||||
integrations := make([]string, 0, len(client.integrations))
|
||||
for _, integration := range client.integrations {
|
||||
integrations = append(integrations, integration.Name())
|
||||
}
|
||||
sort.Strings(integrations)
|
||||
return integrations
|
||||
}
|
||||
|
||||
func (client Client) integrationAlreadyInstalled(name string) bool {
|
||||
for _, integration := range client.integrations {
|
||||
if integration.Name() == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
185
vendor/github.com/getsentry/sentry-go/dsn.go
generated
vendored
Normal file
185
vendor/github.com/getsentry/sentry-go/dsn.go
generated
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type scheme string
|
||||
|
||||
const (
|
||||
schemeHTTP scheme = "http"
|
||||
schemeHTTPS scheme = "https"
|
||||
)
|
||||
|
||||
func (scheme scheme) defaultPort() int {
|
||||
switch scheme {
|
||||
case schemeHTTPS:
|
||||
return 443
|
||||
case schemeHTTP:
|
||||
return 80
|
||||
default:
|
||||
return 80
|
||||
}
|
||||
}
|
||||
|
||||
type DsnParseError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e DsnParseError) Error() string {
|
||||
return "[Sentry] DsnParseError: " + e.Message
|
||||
}
|
||||
|
||||
// Dsn is used as the remote address source to client transport.
|
||||
type Dsn struct {
|
||||
scheme scheme
|
||||
publicKey string
|
||||
secretKey string
|
||||
host string
|
||||
port int
|
||||
path string
|
||||
projectID int
|
||||
}
|
||||
|
||||
// NewDsn creates an instance od `Dsn` by parsing provided url in a `string` format.
|
||||
// If Dsn is not set the client is effectively disabled.
|
||||
func NewDsn(rawURL string) (*Dsn, error) {
|
||||
// Parse
|
||||
parsedURL, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return nil, &DsnParseError{"invalid url"}
|
||||
}
|
||||
|
||||
// Scheme
|
||||
var scheme scheme
|
||||
switch parsedURL.Scheme {
|
||||
case "http":
|
||||
scheme = schemeHTTP
|
||||
case "https":
|
||||
scheme = schemeHTTPS
|
||||
default:
|
||||
return nil, &DsnParseError{"invalid scheme"}
|
||||
}
|
||||
|
||||
// PublicKey
|
||||
publicKey := parsedURL.User.Username()
|
||||
if publicKey == "" {
|
||||
return nil, &DsnParseError{"empty username"}
|
||||
}
|
||||
|
||||
// SecretKey
|
||||
var secretKey string
|
||||
if parsedSecretKey, ok := parsedURL.User.Password(); ok {
|
||||
secretKey = parsedSecretKey
|
||||
}
|
||||
|
||||
// Host
|
||||
host := parsedURL.Hostname()
|
||||
if host == "" {
|
||||
return nil, &DsnParseError{"empty host"}
|
||||
}
|
||||
|
||||
// Port
|
||||
var port int
|
||||
if parsedURL.Port() != "" {
|
||||
parsedPort, err := strconv.Atoi(parsedURL.Port())
|
||||
if err != nil {
|
||||
return nil, &DsnParseError{"invalid port"}
|
||||
}
|
||||
port = parsedPort
|
||||
} else {
|
||||
port = scheme.defaultPort()
|
||||
}
|
||||
|
||||
// ProjectID
|
||||
if len(parsedURL.Path) == 0 || parsedURL.Path == "/" {
|
||||
return nil, &DsnParseError{"empty project id"}
|
||||
}
|
||||
pathSegments := strings.Split(parsedURL.Path[1:], "/")
|
||||
projectID, err := strconv.Atoi(pathSegments[len(pathSegments)-1])
|
||||
if err != nil {
|
||||
return nil, &DsnParseError{"invalid project id"}
|
||||
}
|
||||
|
||||
// Path
|
||||
var path string
|
||||
if len(pathSegments) > 1 {
|
||||
path = "/" + strings.Join(pathSegments[0:len(pathSegments)-1], "/")
|
||||
}
|
||||
|
||||
return &Dsn{
|
||||
scheme: scheme,
|
||||
publicKey: publicKey,
|
||||
secretKey: secretKey,
|
||||
host: host,
|
||||
port: port,
|
||||
path: path,
|
||||
projectID: projectID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String formats Dsn struct into a valid string url
|
||||
func (dsn Dsn) String() string {
|
||||
var url string
|
||||
url += fmt.Sprintf("%s://%s", dsn.scheme, dsn.publicKey)
|
||||
if dsn.secretKey != "" {
|
||||
url += fmt.Sprintf(":%s", dsn.secretKey)
|
||||
}
|
||||
url += fmt.Sprintf("@%s", dsn.host)
|
||||
if dsn.port != dsn.scheme.defaultPort() {
|
||||
url += fmt.Sprintf(":%d", dsn.port)
|
||||
}
|
||||
if dsn.path != "" {
|
||||
url += dsn.path
|
||||
}
|
||||
url += fmt.Sprintf("/%d", dsn.projectID)
|
||||
return url
|
||||
}
|
||||
|
||||
// StoreAPIURL returns assembled url to be used in the transport.
|
||||
// It points to configures Sentry instance.
|
||||
func (dsn Dsn) StoreAPIURL() *url.URL {
|
||||
var rawURL string
|
||||
rawURL += fmt.Sprintf("%s://%s", dsn.scheme, dsn.host)
|
||||
if dsn.port != dsn.scheme.defaultPort() {
|
||||
rawURL += fmt.Sprintf(":%d", dsn.port)
|
||||
}
|
||||
rawURL += fmt.Sprintf("/api/%d/store/", dsn.projectID)
|
||||
parsedURL, _ := url.Parse(rawURL)
|
||||
return parsedURL
|
||||
}
|
||||
|
||||
// RequestHeaders returns all the necessary headers that have to be used in the transport.
|
||||
func (dsn Dsn) RequestHeaders() map[string]string {
|
||||
auth := fmt.Sprintf("Sentry sentry_version=%d, sentry_timestamp=%d, "+
|
||||
"sentry_client=sentry.go/%s, sentry_key=%s", 7, time.Now().Unix(), Version, dsn.publicKey)
|
||||
|
||||
if dsn.secretKey != "" {
|
||||
auth = fmt.Sprintf("%s, sentry_secret=%s", auth, dsn.secretKey)
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
"X-Sentry-Auth": auth,
|
||||
}
|
||||
}
|
||||
|
||||
func (dsn Dsn) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(dsn.String())
|
||||
}
|
||||
|
||||
func (dsn *Dsn) UnmarshalJSON(data []byte) error {
|
||||
var str string
|
||||
_ = json.Unmarshal(data, &str)
|
||||
newDsn, err := NewDsn(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dsn = *newDsn
|
||||
return nil
|
||||
}
|
7
vendor/github.com/getsentry/sentry-go/go.mod
generated
vendored
Normal file
7
vendor/github.com/getsentry/sentry-go/go.mod
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
module github.com/getsentry/sentry-go
|
||||
|
||||
require (
|
||||
github.com/go-errors/errors v1.0.1
|
||||
github.com/pingcap/errors v0.11.1
|
||||
github.com/pkg/errors v0.8.1
|
||||
)
|
6
vendor/github.com/getsentry/sentry-go/go.sum
generated
vendored
Normal file
6
vendor/github.com/getsentry/sentry-go/go.sum
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/pingcap/errors v0.11.1 h1:BXFZ6MdDd2U1uJUa2sRAWTmm+nieEzuyYM0R4aUTcC8=
|
||||
github.com/pingcap/errors v0.11.1/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
319
vendor/github.com/getsentry/sentry-go/hub.go
generated
vendored
Normal file
319
vendor/github.com/getsentry/sentry-go/hub.go
generated
vendored
Normal file
@ -0,0 +1,319 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type contextKey int
|
||||
|
||||
// HubContextKey is a context key used to store Hub on any context.Context type
|
||||
const HubContextKey = contextKey(1)
|
||||
|
||||
// RequestContextKey is a context key used to store http.Request on the context passed to RecoverWithContext
|
||||
const RequestContextKey = contextKey(2)
|
||||
|
||||
// Default maximum number of breadcrumbs added to an event. Can be overwritten `maxBreadcrumbs` option.
|
||||
const defaultMaxBreadcrumbs = 30
|
||||
|
||||
// Absolute maximum number of breadcrumbs added to an event.
|
||||
// The `maxBreadcrumbs` option cannot be higher than this value.
|
||||
const maxBreadcrumbs = 100
|
||||
|
||||
// Initial instance of the Hub that has no `Client` bound and an empty `Scope`
|
||||
var currentHub = NewHub(nil, NewScope()) // nolint: gochecknoglobals
|
||||
|
||||
// Hub is the central object that can manages scopes and clients.
|
||||
//
|
||||
// This can be used to capture events and manage the scope.
|
||||
// The default hub that is available automatically.
|
||||
//
|
||||
// In most situations developers do not need to interface the hub. Instead
|
||||
// toplevel convenience functions are exposed that will automatically dispatch
|
||||
// to global (`CurrentHub`) hub. In some situations this might not be
|
||||
// possible in which case it might become necessary to manually work with the
|
||||
// hub. This is for instance the case when working with async code.
|
||||
type Hub struct {
|
||||
sync.RWMutex
|
||||
stack *stack
|
||||
lastEventID EventID
|
||||
}
|
||||
|
||||
type layer struct {
|
||||
client *Client
|
||||
scope *Scope
|
||||
}
|
||||
|
||||
type stack []*layer
|
||||
|
||||
// NewHub returns an instance of a `Hub` with provided `Client` and `Scope` bound.
|
||||
func NewHub(client *Client, scope *Scope) *Hub {
|
||||
hub := Hub{
|
||||
stack: &stack{{
|
||||
client: client,
|
||||
scope: scope,
|
||||
}},
|
||||
}
|
||||
return &hub
|
||||
}
|
||||
|
||||
// CurrentHub returns an instance of previously initialized `Hub` stored in the global namespace.
|
||||
func CurrentHub() *Hub {
|
||||
return currentHub
|
||||
}
|
||||
|
||||
// LastEventID returns an ID of last captured event for the current `Hub`.
|
||||
func (hub *Hub) LastEventID() EventID {
|
||||
return hub.lastEventID
|
||||
}
|
||||
|
||||
func (hub *Hub) stackTop() *layer {
|
||||
hub.RLock()
|
||||
defer hub.RUnlock()
|
||||
|
||||
stack := hub.stack
|
||||
if stack == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
stackLen := len(*stack)
|
||||
if stackLen == 0 {
|
||||
return nil
|
||||
}
|
||||
top := (*stack)[stackLen-1]
|
||||
|
||||
return top
|
||||
}
|
||||
|
||||
// Clone returns a copy of the current Hub with top-most scope and client copied over.
|
||||
func (hub *Hub) Clone() *Hub {
|
||||
top := hub.stackTop()
|
||||
if top == nil {
|
||||
return nil
|
||||
}
|
||||
scope := top.scope
|
||||
if scope != nil {
|
||||
scope = scope.Clone()
|
||||
}
|
||||
return NewHub(top.client, scope)
|
||||
}
|
||||
|
||||
// Scope returns top-level `Scope` of the current `Hub` or `nil` if no `Scope` is bound.
|
||||
func (hub *Hub) Scope() *Scope {
|
||||
top := hub.stackTop()
|
||||
if top == nil {
|
||||
return nil
|
||||
}
|
||||
return top.scope
|
||||
}
|
||||
|
||||
// Scope returns top-level `Client` of the current `Hub` or `nil` if no `Client` is bound.
|
||||
func (hub *Hub) Client() *Client {
|
||||
top := hub.stackTop()
|
||||
if top == nil {
|
||||
return nil
|
||||
}
|
||||
return top.client
|
||||
}
|
||||
|
||||
// PushScope pushes a new scope for the current `Hub` and reuses previously bound `Client`.
|
||||
func (hub *Hub) PushScope() *Scope {
|
||||
top := hub.stackTop()
|
||||
|
||||
var client *Client
|
||||
if top != nil {
|
||||
client = top.client
|
||||
}
|
||||
|
||||
var scope *Scope
|
||||
if top != nil && top.scope != nil {
|
||||
scope = top.scope.Clone()
|
||||
} else {
|
||||
scope = NewScope()
|
||||
}
|
||||
|
||||
hub.Lock()
|
||||
defer hub.Unlock()
|
||||
|
||||
*hub.stack = append(*hub.stack, &layer{
|
||||
client: client,
|
||||
scope: scope,
|
||||
})
|
||||
|
||||
return scope
|
||||
}
|
||||
|
||||
// PushScope pops the most recent scope for the current `Hub`.
|
||||
func (hub *Hub) PopScope() {
|
||||
hub.Lock()
|
||||
defer hub.Unlock()
|
||||
|
||||
stack := *hub.stack
|
||||
stackLen := len(stack)
|
||||
if stackLen > 0 {
|
||||
*hub.stack = stack[0 : stackLen-1]
|
||||
}
|
||||
}
|
||||
|
||||
// BindClient binds a new `Client` for the current `Hub`.
|
||||
func (hub *Hub) BindClient(client *Client) {
|
||||
top := hub.stackTop()
|
||||
if top != nil {
|
||||
top.client = client
|
||||
}
|
||||
}
|
||||
|
||||
// WithScope temporarily pushes a scope for a single call.
|
||||
//
|
||||
// A shorthand for:
|
||||
// PushScope()
|
||||
// f(scope)
|
||||
// PopScope()
|
||||
func (hub *Hub) WithScope(f func(scope *Scope)) {
|
||||
scope := hub.PushScope()
|
||||
defer hub.PopScope()
|
||||
f(scope)
|
||||
}
|
||||
|
||||
// ConfigureScope invokes a function that can modify the current scope.
|
||||
//
|
||||
// The function is passed a mutable reference to the `Scope` so that modifications
|
||||
// can be performed.
|
||||
func (hub *Hub) ConfigureScope(f func(scope *Scope)) {
|
||||
scope := hub.Scope()
|
||||
f(scope)
|
||||
}
|
||||
|
||||
// CaptureEvent calls the method of a same name on currently bound `Client` instance
|
||||
// passing it a top-level `Scope`.
|
||||
// Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available.
|
||||
func (hub *Hub) CaptureEvent(event *Event) *EventID {
|
||||
client, scope := hub.Client(), hub.Scope()
|
||||
if client == nil || scope == nil {
|
||||
return nil
|
||||
}
|
||||
return client.CaptureEvent(event, nil, scope)
|
||||
}
|
||||
|
||||
// CaptureMessage calls the method of a same name on currently bound `Client` instance
|
||||
// passing it a top-level `Scope`.
|
||||
// Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available.
|
||||
func (hub *Hub) CaptureMessage(message string) *EventID {
|
||||
client, scope := hub.Client(), hub.Scope()
|
||||
if client == nil || scope == nil {
|
||||
return nil
|
||||
}
|
||||
return client.CaptureMessage(message, nil, scope)
|
||||
}
|
||||
|
||||
// CaptureException calls the method of a same name on currently bound `Client` instance
|
||||
// passing it a top-level `Scope`.
|
||||
// Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available.
|
||||
func (hub *Hub) CaptureException(exception error) *EventID {
|
||||
client, scope := hub.Client(), hub.Scope()
|
||||
if client == nil || scope == nil {
|
||||
return nil
|
||||
}
|
||||
return client.CaptureException(exception, &EventHint{OriginalException: exception}, scope)
|
||||
}
|
||||
|
||||
// AddBreadcrumb records a new breadcrumb.
|
||||
//
|
||||
// The total number of breadcrumbs that can be recorded are limited by the
|
||||
// configuration on the client.
|
||||
func (hub *Hub) AddBreadcrumb(breadcrumb *Breadcrumb, hint *BreadcrumbHint) {
|
||||
client := hub.Client()
|
||||
|
||||
// If there's no client, just store it on the scope straight away
|
||||
if client == nil {
|
||||
hub.Scope().AddBreadcrumb(breadcrumb, maxBreadcrumbs)
|
||||
return
|
||||
}
|
||||
|
||||
options := client.Options()
|
||||
max := defaultMaxBreadcrumbs
|
||||
|
||||
if options.MaxBreadcrumbs != 0 {
|
||||
max = options.MaxBreadcrumbs
|
||||
}
|
||||
|
||||
if max < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if options.BeforeBreadcrumb != nil {
|
||||
h := &BreadcrumbHint{}
|
||||
if hint != nil {
|
||||
h = hint
|
||||
}
|
||||
if breadcrumb = options.BeforeBreadcrumb(breadcrumb, h); breadcrumb == nil {
|
||||
Logger.Println("breadcrumb dropped due to BeforeBreadcrumb callback.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if max > maxBreadcrumbs {
|
||||
max = maxBreadcrumbs
|
||||
}
|
||||
hub.Scope().AddBreadcrumb(breadcrumb, max)
|
||||
}
|
||||
|
||||
// Recover calls the method of a same name on currently bound `Client` instance
|
||||
// passing it a top-level `Scope`.
|
||||
// Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available.
|
||||
func (hub *Hub) Recover(err interface{}) *EventID {
|
||||
if err == nil {
|
||||
err = recover()
|
||||
}
|
||||
client, scope := hub.Client(), hub.Scope()
|
||||
if client == nil || scope == nil {
|
||||
return nil
|
||||
}
|
||||
return client.Recover(err, &EventHint{RecoveredException: err}, scope)
|
||||
}
|
||||
|
||||
// RecoverWithContext calls the method of a same name on currently bound `Client` instance
|
||||
// passing it a top-level `Scope`.
|
||||
// Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available.
|
||||
func (hub *Hub) RecoverWithContext(ctx context.Context, err interface{}) *EventID {
|
||||
if err == nil {
|
||||
err = recover()
|
||||
}
|
||||
client, scope := hub.Client(), hub.Scope()
|
||||
if client == nil || scope == nil {
|
||||
return nil
|
||||
}
|
||||
return client.RecoverWithContext(ctx, err, &EventHint{RecoveredException: err}, scope)
|
||||
}
|
||||
|
||||
// Flush calls the method of a same name on currently bound `Client` instance.
|
||||
func (hub *Hub) Flush(timeout time.Duration) bool {
|
||||
client := hub.Client()
|
||||
|
||||
if client == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return client.Flush(timeout)
|
||||
}
|
||||
|
||||
// HasHubOnContext checks whether `Hub` instance is bound to a given `Context` struct.
|
||||
func HasHubOnContext(ctx context.Context) bool {
|
||||
_, ok := ctx.Value(HubContextKey).(*Hub)
|
||||
return ok
|
||||
}
|
||||
|
||||
// GetHubFromContext tries to retrieve `Hub` instance from the given `Context` struct
|
||||
// or return `nil` if one is not found.
|
||||
func GetHubFromContext(ctx context.Context) *Hub {
|
||||
if hub, ok := ctx.Value(HubContextKey).(*Hub); ok {
|
||||
return hub
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetHubOnContext stores given `Hub` instance on the `Context` struct and returns a new `Context`.
|
||||
func SetHubOnContext(ctx context.Context, hub *Hub) context.Context {
|
||||
return context.WithValue(ctx, HubContextKey, hub)
|
||||
}
|
376
vendor/github.com/getsentry/sentry-go/integrations.go
generated
vendored
Normal file
376
vendor/github.com/getsentry/sentry-go/integrations.go
generated
vendored
Normal file
@ -0,0 +1,376 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ================================
|
||||
// Modules Integration
|
||||
// ================================
|
||||
|
||||
type modulesIntegration struct{}
|
||||
|
||||
var _modulesCache map[string]string // nolint: gochecknoglobals
|
||||
|
||||
func (mi *modulesIntegration) Name() string {
|
||||
return "Modules"
|
||||
}
|
||||
|
||||
func (mi *modulesIntegration) SetupOnce(client *Client) {
|
||||
client.AddEventProcessor(mi.processor)
|
||||
}
|
||||
|
||||
func (mi *modulesIntegration) processor(event *Event, hint *EventHint) *Event {
|
||||
if event.Modules == nil {
|
||||
event.Modules = extractModules()
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func extractModules() map[string]string {
|
||||
if _modulesCache != nil {
|
||||
return _modulesCache
|
||||
}
|
||||
|
||||
extractedModules, err := getModules()
|
||||
if err != nil {
|
||||
Logger.Printf("ModuleIntegration wasn't able to extract modules: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
_modulesCache = extractedModules
|
||||
|
||||
return extractedModules
|
||||
}
|
||||
|
||||
func getModules() (map[string]string, error) {
|
||||
if fileExists("go.mod") {
|
||||
return getModulesFromMod()
|
||||
}
|
||||
|
||||
if fileExists("vendor") {
|
||||
// Priority given to vendor created by modules
|
||||
if fileExists("vendor/modules.txt") {
|
||||
return getModulesFromVendorTxt()
|
||||
}
|
||||
|
||||
if fileExists("vendor/vendor.json") {
|
||||
return getModulesFromVendorJSON()
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("module integration failed")
|
||||
}
|
||||
|
||||
func getModulesFromMod() (map[string]string, error) {
|
||||
modules := make(map[string]string)
|
||||
|
||||
file, err := os.Open("go.mod")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open mod file")
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
areModulesPresent := false
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
splits := strings.Split(scanner.Text(), " ")
|
||||
|
||||
if splits[0] == "require" {
|
||||
areModulesPresent = true
|
||||
|
||||
// Mod file has only 1 dependency
|
||||
if len(splits) > 2 {
|
||||
modules[strings.TrimSpace(splits[1])] = splits[2]
|
||||
return modules, nil
|
||||
}
|
||||
} else if areModulesPresent && splits[0] != ")" {
|
||||
modules[strings.TrimSpace(splits[0])] = splits[1]
|
||||
}
|
||||
}
|
||||
|
||||
if scannerErr := scanner.Err(); scannerErr != nil {
|
||||
return nil, scannerErr
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
func getModulesFromVendorTxt() (map[string]string, error) {
|
||||
modules := make(map[string]string)
|
||||
|
||||
file, err := os.Open("vendor/modules.txt")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open vendor/modules.txt")
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
splits := strings.Split(scanner.Text(), " ")
|
||||
|
||||
if splits[0] == "#" {
|
||||
modules[splits[1]] = splits[2]
|
||||
}
|
||||
}
|
||||
|
||||
if scannerErr := scanner.Err(); scannerErr != nil {
|
||||
return nil, scannerErr
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
func getModulesFromVendorJSON() (map[string]string, error) {
|
||||
modules := make(map[string]string)
|
||||
|
||||
file, err := ioutil.ReadFile("vendor/vendor.json")
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open vendor/vendor.json")
|
||||
}
|
||||
|
||||
var vendor map[string]interface{}
|
||||
if unmarshalErr := json.Unmarshal(file, &vendor); unmarshalErr != nil {
|
||||
return nil, unmarshalErr
|
||||
}
|
||||
|
||||
packages := vendor["package"].([]interface{})
|
||||
|
||||
// To avoid iterative dependencies, TODO: Change of default value
|
||||
lastPath := "\n"
|
||||
|
||||
for _, value := range packages {
|
||||
path := value.(map[string]interface{})["path"].(string)
|
||||
|
||||
if !strings.Contains(path, lastPath) {
|
||||
// No versions are available through vendor.json
|
||||
modules[path] = ""
|
||||
lastPath = path
|
||||
}
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Environment Integration
|
||||
// ================================
|
||||
|
||||
type environmentIntegration struct{}
|
||||
|
||||
func (ei *environmentIntegration) Name() string {
|
||||
return "Environment"
|
||||
}
|
||||
|
||||
func (ei *environmentIntegration) SetupOnce(client *Client) {
|
||||
client.AddEventProcessor(ei.processor)
|
||||
}
|
||||
|
||||
func (ei *environmentIntegration) processor(event *Event, hint *EventHint) *Event {
|
||||
if event.Contexts == nil {
|
||||
event.Contexts = make(map[string]interface{})
|
||||
}
|
||||
|
||||
event.Contexts["device"] = map[string]interface{}{
|
||||
"arch": runtime.GOARCH,
|
||||
"num_cpu": runtime.NumCPU(),
|
||||
}
|
||||
|
||||
event.Contexts["os"] = map[string]interface{}{
|
||||
"name": runtime.GOOS,
|
||||
}
|
||||
|
||||
event.Contexts["runtime"] = map[string]interface{}{
|
||||
"name": "go",
|
||||
"version": runtime.Version(),
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Ignore Errors Integration
|
||||
// ================================
|
||||
|
||||
type ignoreErrorsIntegration struct {
|
||||
ignoreErrors []*regexp.Regexp
|
||||
}
|
||||
|
||||
func (iei *ignoreErrorsIntegration) Name() string {
|
||||
return "IgnoreErrors"
|
||||
}
|
||||
|
||||
func (iei *ignoreErrorsIntegration) SetupOnce(client *Client) {
|
||||
iei.ignoreErrors = transformStringsIntoRegexps(client.Options().IgnoreErrors)
|
||||
client.AddEventProcessor(iei.processor)
|
||||
}
|
||||
|
||||
func (iei *ignoreErrorsIntegration) processor(event *Event, hint *EventHint) *Event {
|
||||
suspects := getIgnoreErrorsSuspects(event)
|
||||
|
||||
for _, suspect := range suspects {
|
||||
for _, pattern := range iei.ignoreErrors {
|
||||
if pattern.Match([]byte(suspect)) {
|
||||
Logger.Printf("Event dropped due to being matched by `IgnoreErrors` option."+
|
||||
"| Value matched: %s | Filter used: %s", suspect, pattern)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func transformStringsIntoRegexps(strings []string) []*regexp.Regexp {
|
||||
var exprs []*regexp.Regexp
|
||||
|
||||
for _, s := range strings {
|
||||
r, err := regexp.Compile(s)
|
||||
if err == nil {
|
||||
exprs = append(exprs, r)
|
||||
}
|
||||
}
|
||||
|
||||
return exprs
|
||||
}
|
||||
|
||||
func getIgnoreErrorsSuspects(event *Event) []string {
|
||||
suspects := []string{}
|
||||
|
||||
if event.Message != "" {
|
||||
suspects = append(suspects, event.Message)
|
||||
}
|
||||
|
||||
for _, ex := range event.Exception {
|
||||
suspects = append(suspects, ex.Type)
|
||||
suspects = append(suspects, ex.Value)
|
||||
}
|
||||
|
||||
return suspects
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Contextify Frames Integration
|
||||
// ================================
|
||||
|
||||
type contextifyFramesIntegration struct {
|
||||
sr sourceReader
|
||||
contextLines int
|
||||
cachedLocations map[string]string
|
||||
}
|
||||
|
||||
func (cfi *contextifyFramesIntegration) Name() string {
|
||||
return "ContextifyFrames"
|
||||
}
|
||||
|
||||
func (cfi *contextifyFramesIntegration) SetupOnce(client *Client) {
|
||||
cfi.sr = newSourceReader()
|
||||
cfi.contextLines = 5
|
||||
cfi.cachedLocations = make(map[string]string)
|
||||
|
||||
client.AddEventProcessor(cfi.processor)
|
||||
}
|
||||
|
||||
func (cfi *contextifyFramesIntegration) processor(event *Event, hint *EventHint) *Event {
|
||||
// Range over all exceptions
|
||||
for _, ex := range event.Exception {
|
||||
// If it has no stacktrace, just bail out
|
||||
if ex.Stacktrace == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// If it does, it should have frames, so try to contextify them
|
||||
ex.Stacktrace.Frames = cfi.contextify(ex.Stacktrace.Frames)
|
||||
}
|
||||
|
||||
// Range over all threads
|
||||
for _, th := range event.Threads {
|
||||
// If it has no stacktrace, just bail out
|
||||
if th.Stacktrace == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// If it does, it should have frames, so try to contextify them
|
||||
th.Stacktrace.Frames = cfi.contextify(th.Stacktrace.Frames)
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func (cfi *contextifyFramesIntegration) contextify(frames []Frame) []Frame {
|
||||
contextifiedFrames := make([]Frame, 0, len(frames))
|
||||
|
||||
for _, frame := range frames {
|
||||
if !frame.InApp {
|
||||
contextifiedFrames = append(contextifiedFrames, frame)
|
||||
continue
|
||||
}
|
||||
|
||||
var path string
|
||||
|
||||
if cachedPath, ok := cfi.cachedLocations[frame.AbsPath]; ok {
|
||||
path = cachedPath
|
||||
} else {
|
||||
// Optimize for happy path here
|
||||
if fileExists(frame.AbsPath) {
|
||||
path = frame.AbsPath
|
||||
} else {
|
||||
path = cfi.findNearbySourceCodeLocation(frame.AbsPath)
|
||||
}
|
||||
}
|
||||
|
||||
if path == "" {
|
||||
contextifiedFrames = append(contextifiedFrames, frame)
|
||||
continue
|
||||
}
|
||||
|
||||
lines, contextLine := cfi.sr.readContextLines(path, frame.Lineno, cfi.contextLines)
|
||||
contextifiedFrames = append(contextifiedFrames, cfi.addContextLinesToFrame(frame, lines, contextLine))
|
||||
}
|
||||
|
||||
return contextifiedFrames
|
||||
}
|
||||
|
||||
func (cfi *contextifyFramesIntegration) findNearbySourceCodeLocation(originalPath string) string {
|
||||
trimmedPath := strings.TrimPrefix(originalPath, "/")
|
||||
components := strings.Split(trimmedPath, "/")
|
||||
|
||||
for len(components) > 0 {
|
||||
components = components[1:]
|
||||
possibleLocation := strings.Join(components, "/")
|
||||
|
||||
if fileExists(possibleLocation) {
|
||||
cfi.cachedLocations[originalPath] = possibleLocation
|
||||
return possibleLocation
|
||||
}
|
||||
}
|
||||
|
||||
cfi.cachedLocations[originalPath] = ""
|
||||
return ""
|
||||
}
|
||||
|
||||
func (cfi *contextifyFramesIntegration) addContextLinesToFrame(frame Frame, lines [][]byte, contextLine int) Frame {
|
||||
for i, line := range lines {
|
||||
switch {
|
||||
case i < contextLine:
|
||||
frame.PreContext = append(frame.PreContext, string(line))
|
||||
case i == contextLine:
|
||||
frame.ContextLine = string(line)
|
||||
default:
|
||||
frame.PostContext = append(frame.PostContext, string(line))
|
||||
}
|
||||
}
|
||||
return frame
|
||||
}
|
180
vendor/github.com/getsentry/sentry-go/interfaces.go
generated
vendored
Normal file
180
vendor/github.com/getsentry/sentry-go/interfaces.go
generated
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Protocol Docs (kinda)
|
||||
// https://github.com/getsentry/rust-sentry-types/blob/master/src/protocol/v7.rs
|
||||
|
||||
// Level marks the severity of the event
|
||||
type Level string
|
||||
|
||||
const (
|
||||
LevelDebug Level = "debug"
|
||||
LevelInfo Level = "info"
|
||||
LevelWarning Level = "warning"
|
||||
LevelError Level = "error"
|
||||
LevelFatal Level = "fatal"
|
||||
)
|
||||
|
||||
// https://docs.sentry.io/development/sdk-dev/interfaces/sdk/
|
||||
type SdkInfo struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Integrations []string `json:"integrations,omitempty"`
|
||||
Packages []SdkPackage `json:"packages,omitempty"`
|
||||
}
|
||||
|
||||
type SdkPackage struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// TODO: This type could be more useful, as map of interface{} is too generic
|
||||
// and requires a lot of type assertions in beforeBreadcrumb calls
|
||||
// plus it could just be `map[string]interface{}` then
|
||||
type BreadcrumbHint map[string]interface{}
|
||||
|
||||
// https://docs.sentry.io/development/sdk-dev/interfaces/breadcrumbs/
|
||||
type Breadcrumb struct {
|
||||
Category string `json:"category,omitempty"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
Level Level `json:"level,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// https://docs.sentry.io/development/sdk-dev/interfaces/user/
|
||||
type User struct {
|
||||
Email string `json:"email,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
IPAddress string `json:"ip_address,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
}
|
||||
|
||||
// https://docs.sentry.io/development/sdk-dev/interfaces/http/
|
||||
type Request struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Data string `json:"data,omitempty"`
|
||||
QueryString string `json:"query_string,omitempty"`
|
||||
Cookies string `json:"cookies,omitempty"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
Env map[string]string `json:"env,omitempty"`
|
||||
}
|
||||
|
||||
func (r Request) FromHTTPRequest(request *http.Request) Request {
|
||||
// Method
|
||||
r.Method = request.Method
|
||||
|
||||
// URL
|
||||
protocol := schemeHTTP
|
||||
if request.TLS != nil || request.Header.Get("X-Forwarded-Proto") == "https" {
|
||||
protocol = schemeHTTPS
|
||||
}
|
||||
r.URL = fmt.Sprintf("%s://%s%s", protocol, request.Host, request.URL.Path)
|
||||
|
||||
// Headers
|
||||
headers := make(map[string]string, len(request.Header))
|
||||
for k, v := range request.Header {
|
||||
headers[k] = strings.Join(v, ",")
|
||||
}
|
||||
headers["Host"] = request.Host
|
||||
r.Headers = headers
|
||||
|
||||
// Cookies
|
||||
r.Cookies = request.Header.Get("Cookie")
|
||||
|
||||
// Env
|
||||
if addr, port, err := net.SplitHostPort(request.RemoteAddr); err == nil {
|
||||
r.Env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port}
|
||||
}
|
||||
|
||||
// QueryString
|
||||
r.QueryString = request.URL.RawQuery
|
||||
|
||||
// Body
|
||||
if request.GetBody != nil {
|
||||
if bodyCopy, err := request.GetBody(); err == nil && bodyCopy != nil {
|
||||
body, err := ioutil.ReadAll(bodyCopy)
|
||||
if err == nil {
|
||||
r.Data = string(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// https://docs.sentry.io/development/sdk-dev/interfaces/exception/
|
||||
type Exception struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Module string `json:"module,omitempty"`
|
||||
Stacktrace *Stacktrace `json:"stacktrace,omitempty"`
|
||||
RawStacktrace *Stacktrace `json:"raw_stacktrace,omitempty"`
|
||||
}
|
||||
|
||||
type EventID string
|
||||
|
||||
// https://docs.sentry.io/development/sdk-dev/attributes/
|
||||
type Event struct {
|
||||
Breadcrumbs []*Breadcrumb `json:"breadcrumbs,omitempty"`
|
||||
Contexts map[string]interface{} `json:"contexts,omitempty"`
|
||||
Dist string `json:"dist,omitempty"`
|
||||
Environment string `json:"environment,omitempty"`
|
||||
EventID EventID `json:"event_id,omitempty"`
|
||||
Extra map[string]interface{} `json:"extra,omitempty"`
|
||||
Fingerprint []string `json:"fingerprint,omitempty"`
|
||||
Level Level `json:"level,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Platform string `json:"platform,omitempty"`
|
||||
Release string `json:"release,omitempty"`
|
||||
Sdk SdkInfo `json:"sdk,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
Threads []Thread `json:"threads,omitempty"`
|
||||
Tags map[string]string `json:"tags,omitempty"`
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
Transaction string `json:"transaction,omitempty"`
|
||||
User User `json:"user,omitempty"`
|
||||
Logger string `json:"logger,omitempty"`
|
||||
Modules map[string]string `json:"modules,omitempty"`
|
||||
Request Request `json:"request,omitempty"`
|
||||
Exception []Exception `json:"exception,omitempty"`
|
||||
}
|
||||
|
||||
func NewEvent() *Event {
|
||||
event := Event{
|
||||
Contexts: make(map[string]interface{}),
|
||||
Extra: make(map[string]interface{}),
|
||||
Tags: make(map[string]string),
|
||||
Modules: make(map[string]string),
|
||||
}
|
||||
return &event
|
||||
}
|
||||
|
||||
type Thread struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Stacktrace *Stacktrace `json:"stacktrace,omitempty"`
|
||||
RawStacktrace *Stacktrace `json:"raw_stacktrace,omitempty"`
|
||||
Crashed bool `json:"crashed,omitempty"`
|
||||
Current bool `json:"current,omitempty"`
|
||||
}
|
||||
|
||||
type EventHint struct {
|
||||
Data interface{}
|
||||
EventID string
|
||||
OriginalException error
|
||||
RecoveredException interface{}
|
||||
Context context.Context
|
||||
Request *http.Request
|
||||
Response *http.Response
|
||||
}
|
291
vendor/github.com/getsentry/sentry-go/scope.go
generated
vendored
Normal file
291
vendor/github.com/getsentry/sentry-go/scope.go
generated
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Scope holds contextual data for the current scope.
|
||||
//
|
||||
// The scope is an object that can cloned efficiently and stores data that
|
||||
// is locally relevant to an event. For instance the scope will hold recorded
|
||||
// breadcrumbs and similar information.
|
||||
//
|
||||
// The scope can be interacted with in two ways:
|
||||
//
|
||||
// 1. the scope is routinely updated with information by functions such as
|
||||
// `AddBreadcrumb` which will modify the currently top-most scope.
|
||||
// 2. the topmost scope can also be configured through the `ConfigureScope`
|
||||
// method.
|
||||
//
|
||||
// Note that the scope can only be modified but not inspected.
|
||||
// Only the client can use the scope to extract information currently.
|
||||
type Scope struct {
|
||||
sync.RWMutex
|
||||
breadcrumbs []*Breadcrumb
|
||||
user User
|
||||
tags map[string]string
|
||||
contexts map[string]interface{}
|
||||
extra map[string]interface{}
|
||||
fingerprint []string
|
||||
level Level
|
||||
transaction string
|
||||
request Request
|
||||
eventProcessors []EventProcessor
|
||||
}
|
||||
|
||||
func NewScope() *Scope {
|
||||
scope := Scope{
|
||||
breadcrumbs: make([]*Breadcrumb, 0),
|
||||
tags: make(map[string]string),
|
||||
contexts: make(map[string]interface{}),
|
||||
extra: make(map[string]interface{}),
|
||||
fingerprint: make([]string, 0),
|
||||
}
|
||||
|
||||
return &scope
|
||||
}
|
||||
|
||||
// AddBreadcrumb adds new breadcrumb to the current scope
|
||||
// and optionaly throws the old one if limit is reached.
|
||||
func (scope *Scope) AddBreadcrumb(breadcrumb *Breadcrumb, limit int) {
|
||||
if breadcrumb.Timestamp == 0 {
|
||||
breadcrumb.Timestamp = time.Now().Unix()
|
||||
}
|
||||
|
||||
scope.Lock()
|
||||
defer scope.Unlock()
|
||||
|
||||
breadcrumbs := append(scope.breadcrumbs, breadcrumb)
|
||||
if len(breadcrumbs) > limit {
|
||||
scope.breadcrumbs = breadcrumbs[1 : limit+1]
|
||||
} else {
|
||||
scope.breadcrumbs = breadcrumbs
|
||||
}
|
||||
}
|
||||
|
||||
// ClearBreadcrumbs clears all breadcrumbs from the current scope.
|
||||
func (scope *Scope) ClearBreadcrumbs() {
|
||||
scope.breadcrumbs = []*Breadcrumb{}
|
||||
}
|
||||
|
||||
// SetUser sets new user for the current scope.
|
||||
func (scope *Scope) SetUser(user User) {
|
||||
scope.user = user
|
||||
}
|
||||
|
||||
// SetRequest sets new user for the current scope.
|
||||
func (scope *Scope) SetRequest(request Request) {
|
||||
scope.request = request
|
||||
}
|
||||
|
||||
// SetTag adds a tag to the current scope.
|
||||
func (scope *Scope) SetTag(key, value string) {
|
||||
scope.Lock()
|
||||
defer scope.Unlock()
|
||||
|
||||
scope.tags[key] = value
|
||||
}
|
||||
|
||||
// SetTags assigns multiple tags to the current scope.
|
||||
func (scope *Scope) SetTags(tags map[string]string) {
|
||||
scope.Lock()
|
||||
defer scope.Unlock()
|
||||
|
||||
for k, v := range tags {
|
||||
scope.tags[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveTag removes a tag from the current scope.
|
||||
func (scope *Scope) RemoveTag(key string) {
|
||||
scope.Lock()
|
||||
defer scope.Unlock()
|
||||
|
||||
delete(scope.tags, key)
|
||||
}
|
||||
|
||||
// SetContext adds a context to the current scope.
|
||||
func (scope *Scope) SetContext(key string, value interface{}) {
|
||||
scope.Lock()
|
||||
defer scope.Unlock()
|
||||
|
||||
scope.contexts[key] = value
|
||||
}
|
||||
|
||||
// SetContexts assigns multiple contexts to the current scope.
|
||||
func (scope *Scope) SetContexts(contexts map[string]interface{}) {
|
||||
scope.Lock()
|
||||
defer scope.Unlock()
|
||||
|
||||
for k, v := range contexts {
|
||||
scope.contexts[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveContext removes a context from the current scope.
|
||||
func (scope *Scope) RemoveContext(key string) {
|
||||
scope.Lock()
|
||||
defer scope.Unlock()
|
||||
|
||||
delete(scope.contexts, key)
|
||||
}
|
||||
|
||||
// SetExtra adds an extra to the current scope.
|
||||
func (scope *Scope) SetExtra(key string, value interface{}) {
|
||||
scope.Lock()
|
||||
defer scope.Unlock()
|
||||
|
||||
scope.extra[key] = value
|
||||
}
|
||||
|
||||
// SetExtras assigns multiple extras to the current scope.
|
||||
func (scope *Scope) SetExtras(extra map[string]interface{}) {
|
||||
scope.Lock()
|
||||
defer scope.Unlock()
|
||||
|
||||
for k, v := range extra {
|
||||
scope.extra[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveExtra removes a extra from the current scope.
|
||||
func (scope *Scope) RemoveExtra(key string) {
|
||||
scope.Lock()
|
||||
defer scope.Unlock()
|
||||
|
||||
delete(scope.extra, key)
|
||||
}
|
||||
|
||||
// SetFingerprint sets new fingerprint for the current scope.
|
||||
func (scope *Scope) SetFingerprint(fingerprint []string) {
|
||||
scope.fingerprint = fingerprint
|
||||
}
|
||||
|
||||
// SetLevel sets new level for the current scope.
|
||||
func (scope *Scope) SetLevel(level Level) {
|
||||
scope.level = level
|
||||
}
|
||||
|
||||
// SetTransaction sets new transaction name for the current transaction.
|
||||
func (scope *Scope) SetTransaction(transactionName string) {
|
||||
scope.transaction = transactionName
|
||||
}
|
||||
|
||||
// Clone returns a copy of the current scope with all data copied over.
|
||||
func (scope *Scope) Clone() *Scope {
|
||||
scope.RLock()
|
||||
defer scope.RUnlock()
|
||||
|
||||
clone := NewScope()
|
||||
clone.user = scope.user
|
||||
clone.breadcrumbs = make([]*Breadcrumb, len(scope.breadcrumbs))
|
||||
copy(clone.breadcrumbs, scope.breadcrumbs)
|
||||
for key, value := range scope.tags {
|
||||
clone.tags[key] = value
|
||||
}
|
||||
for key, value := range scope.contexts {
|
||||
clone.contexts[key] = value
|
||||
}
|
||||
for key, value := range scope.extra {
|
||||
clone.extra[key] = value
|
||||
}
|
||||
clone.fingerprint = make([]string, len(scope.fingerprint))
|
||||
copy(clone.fingerprint, scope.fingerprint)
|
||||
clone.level = scope.level
|
||||
clone.transaction = scope.transaction
|
||||
clone.request = scope.request
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
// Clear removed the data from the current scope.
|
||||
func (scope *Scope) Clear() {
|
||||
*scope = *NewScope()
|
||||
}
|
||||
|
||||
// AddEventProcessor adds an event processor to the current scope.
|
||||
func (scope *Scope) AddEventProcessor(processor EventProcessor) {
|
||||
scope.Lock()
|
||||
defer scope.Unlock()
|
||||
|
||||
scope.eventProcessors = append(scope.eventProcessors, processor)
|
||||
}
|
||||
|
||||
// ApplyToEvent takes the data from the current scope and attaches it to the event.
|
||||
func (scope *Scope) ApplyToEvent(event *Event, hint *EventHint) *Event {
|
||||
scope.RLock()
|
||||
defer scope.RUnlock()
|
||||
|
||||
if len(scope.breadcrumbs) > 0 {
|
||||
if event.Breadcrumbs == nil {
|
||||
event.Breadcrumbs = []*Breadcrumb{}
|
||||
}
|
||||
|
||||
event.Breadcrumbs = append(event.Breadcrumbs, scope.breadcrumbs...)
|
||||
}
|
||||
|
||||
if len(scope.tags) > 0 {
|
||||
if event.Tags == nil {
|
||||
event.Tags = make(map[string]string)
|
||||
}
|
||||
|
||||
for key, value := range scope.tags {
|
||||
event.Tags[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
if len(scope.contexts) > 0 {
|
||||
if event.Contexts == nil {
|
||||
event.Contexts = make(map[string]interface{})
|
||||
}
|
||||
|
||||
for key, value := range scope.contexts {
|
||||
event.Contexts[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
if len(scope.extra) > 0 {
|
||||
if event.Extra == nil {
|
||||
event.Extra = make(map[string]interface{})
|
||||
}
|
||||
|
||||
for key, value := range scope.extra {
|
||||
event.Extra[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
if (reflect.DeepEqual(event.User, User{})) {
|
||||
event.User = scope.user
|
||||
}
|
||||
|
||||
if (event.Fingerprint == nil || len(event.Fingerprint) == 0) &&
|
||||
len(scope.fingerprint) > 0 {
|
||||
event.Fingerprint = make([]string, len(scope.fingerprint))
|
||||
copy(event.Fingerprint, scope.fingerprint)
|
||||
}
|
||||
|
||||
if scope.level != "" {
|
||||
event.Level = scope.level
|
||||
}
|
||||
|
||||
if scope.transaction != "" {
|
||||
event.Transaction = scope.transaction
|
||||
}
|
||||
|
||||
if (reflect.DeepEqual(event.Request, Request{})) {
|
||||
event.Request = scope.request
|
||||
}
|
||||
|
||||
for _, processor := range scope.eventProcessors {
|
||||
id := event.EventID
|
||||
event = processor(event, hint)
|
||||
if event == nil {
|
||||
Logger.Printf("Event dropped by one of the Scope EventProcessors: %s\n", id)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
122
vendor/github.com/getsentry/sentry-go/sentry.go
generated
vendored
Normal file
122
vendor/github.com/getsentry/sentry-go/sentry.go
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Version Sentry-Go SDK Version
|
||||
const Version = "0.2.1"
|
||||
|
||||
// Init initializes whole SDK by creating new `Client` and binding it to the current `Hub`
|
||||
func Init(options ClientOptions) error {
|
||||
hub := CurrentHub()
|
||||
client, err := NewClient(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hub.BindClient(client)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddBreadcrumb records a new breadcrumb.
|
||||
//
|
||||
// The total number of breadcrumbs that can be recorded are limited by the
|
||||
// configuration on the client.
|
||||
func AddBreadcrumb(breadcrumb *Breadcrumb) {
|
||||
hub := CurrentHub()
|
||||
hub.AddBreadcrumb(breadcrumb, nil)
|
||||
}
|
||||
|
||||
// CaptureMessage captures an arbitrary message.
|
||||
func CaptureMessage(message string) *EventID {
|
||||
hub := CurrentHub()
|
||||
return hub.CaptureMessage(message)
|
||||
}
|
||||
|
||||
// CaptureException captures an error.
|
||||
func CaptureException(exception error) *EventID {
|
||||
hub := CurrentHub()
|
||||
return hub.CaptureException(exception)
|
||||
}
|
||||
|
||||
// CaptureEvent captures an event on the currently active client if any.
|
||||
//
|
||||
// The event must already be assembled. Typically code would instead use
|
||||
// the utility methods like `CaptureException`. The return value is the
|
||||
// event ID. In case Sentry is disabled or event was dropped, the return value will be nil.
|
||||
func CaptureEvent(event *Event) *EventID {
|
||||
hub := CurrentHub()
|
||||
return hub.CaptureEvent(event)
|
||||
}
|
||||
|
||||
// Recover captures a panic.
|
||||
func Recover() *EventID {
|
||||
if err := recover(); err != nil {
|
||||
hub := CurrentHub()
|
||||
return hub.Recover(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Recover captures a panic and passes relevant context object.
|
||||
func RecoverWithContext(ctx context.Context) *EventID {
|
||||
if err := recover(); err != nil {
|
||||
var hub *Hub
|
||||
|
||||
if HasHubOnContext(ctx) {
|
||||
hub = GetHubFromContext(ctx)
|
||||
} else {
|
||||
hub = CurrentHub()
|
||||
}
|
||||
|
||||
return hub.RecoverWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithScope temporarily pushes a scope for a single call.
|
||||
//
|
||||
// This function takes one argument, a callback that executes
|
||||
// in the context of that scope.
|
||||
//
|
||||
// This is useful when extra data should be send with a single capture call
|
||||
// for instance a different level or tags
|
||||
func WithScope(f func(scope *Scope)) {
|
||||
hub := CurrentHub()
|
||||
hub.WithScope(f)
|
||||
}
|
||||
|
||||
// ConfigureScope invokes a function that can modify the current scope.
|
||||
//
|
||||
// The function is passed a mutable reference to the `Scope` so that modifications
|
||||
// can be performed.
|
||||
func ConfigureScope(f func(scope *Scope)) {
|
||||
hub := CurrentHub()
|
||||
hub.ConfigureScope(f)
|
||||
}
|
||||
|
||||
// PushScope pushes a new scope.
|
||||
func PushScope() {
|
||||
hub := CurrentHub()
|
||||
hub.PushScope()
|
||||
}
|
||||
|
||||
// PopScope pushes a new scope.
|
||||
func PopScope() {
|
||||
hub := CurrentHub()
|
||||
hub.PopScope()
|
||||
}
|
||||
|
||||
// Flush notifies when all the buffered events have been sent by returning `true`
|
||||
// or `false` if timeout was reached.
|
||||
func Flush(timeout time.Duration) bool {
|
||||
hub := CurrentHub()
|
||||
return hub.Flush(timeout)
|
||||
}
|
||||
|
||||
// LastEventID returns an ID of last captured event.
|
||||
func LastEventID() EventID {
|
||||
hub := CurrentHub()
|
||||
return hub.LastEventID()
|
||||
}
|
69
vendor/github.com/getsentry/sentry-go/sourcereader.go
generated
vendored
Normal file
69
vendor/github.com/getsentry/sentry-go/sourcereader.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type sourceReader struct {
|
||||
mu sync.Mutex
|
||||
cache map[string][][]byte
|
||||
}
|
||||
|
||||
func newSourceReader() sourceReader {
|
||||
return sourceReader{
|
||||
cache: make(map[string][][]byte),
|
||||
}
|
||||
}
|
||||
|
||||
func (sr *sourceReader) readContextLines(filename string, line, context int) ([][]byte, int) {
|
||||
sr.mu.Lock()
|
||||
defer sr.mu.Unlock()
|
||||
|
||||
lines, ok := sr.cache[filename]
|
||||
|
||||
if !ok {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
sr.cache[filename] = nil
|
||||
return nil, 0
|
||||
}
|
||||
lines = bytes.Split(data, []byte{'\n'})
|
||||
sr.cache[filename] = lines
|
||||
}
|
||||
|
||||
return sr.calculateContextLines(lines, line, context)
|
||||
}
|
||||
|
||||
// `contextLine` points to a line that caused an issue itself, in relation to returned slice
|
||||
func (sr *sourceReader) calculateContextLines(lines [][]byte, line, context int) ([][]byte, int) {
|
||||
// Stacktrace lines are 1-indexed, slices are 0-indexed
|
||||
line--
|
||||
|
||||
contextLine := context
|
||||
|
||||
if lines == nil || line >= len(lines) || line < 0 {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
if context < 0 {
|
||||
context = 0
|
||||
contextLine = 0
|
||||
}
|
||||
|
||||
start := line - context
|
||||
|
||||
if start < 0 {
|
||||
contextLine += start
|
||||
start = 0
|
||||
}
|
||||
|
||||
end := line + context + 1
|
||||
|
||||
if end > len(lines) {
|
||||
end = len(lines)
|
||||
}
|
||||
|
||||
return lines[start:end], contextLine
|
||||
}
|
257
vendor/github.com/getsentry/sentry-go/stacktrace.go
generated
vendored
Normal file
257
vendor/github.com/getsentry/sentry-go/stacktrace.go
generated
vendored
Normal file
@ -0,0 +1,257 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const unknown string = "unknown"
|
||||
|
||||
// The module download is split into two parts: downloading the go.mod and downloading the actual code.
|
||||
// If you have dependencies only needed for tests, then they will show up in your go.mod,
|
||||
// and go get will download their go.mods, but it will not download their code.
|
||||
// The test-only dependencies get downloaded only when you need it, such as the first time you run go test.
|
||||
//
|
||||
// https://github.com/golang/go/issues/26913#issuecomment-411976222
|
||||
|
||||
// Stacktrace holds information about the frames of the stack.
|
||||
type Stacktrace struct {
|
||||
Frames []Frame `json:"frames,omitempty"`
|
||||
FramesOmitted []uint `json:"frames_omitted,omitempty"`
|
||||
}
|
||||
|
||||
// NewStacktrace creates a stacktrace using `runtime.Callers`.
|
||||
func NewStacktrace() *Stacktrace {
|
||||
pcs := make([]uintptr, 100)
|
||||
n := runtime.Callers(1, pcs)
|
||||
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
frames := extractFrames(pcs[:n])
|
||||
frames = filterFrames(frames)
|
||||
|
||||
stacktrace := Stacktrace{
|
||||
Frames: frames,
|
||||
}
|
||||
|
||||
return &stacktrace
|
||||
}
|
||||
|
||||
// ExtractStacktrace creates a new `Stacktrace` based on the given `error` object.
|
||||
// TODO: Make it configurable so that anyone can provide their own implementation?
|
||||
// Use of reflection allows us to not have a hard dependency on any given package, so we don't have to import it
|
||||
func ExtractStacktrace(err error) *Stacktrace {
|
||||
method := extractReflectedStacktraceMethod(err)
|
||||
|
||||
if !method.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
pcs := extractPcs(method)
|
||||
|
||||
if len(pcs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
frames := extractFrames(pcs)
|
||||
frames = filterFrames(frames)
|
||||
|
||||
stacktrace := Stacktrace{
|
||||
Frames: frames,
|
||||
}
|
||||
|
||||
return &stacktrace
|
||||
}
|
||||
|
||||
func extractReflectedStacktraceMethod(err error) reflect.Value {
|
||||
var method reflect.Value
|
||||
|
||||
// https://github.com/pingcap/errors
|
||||
methodGetStackTracer := reflect.ValueOf(err).MethodByName("GetStackTracer")
|
||||
// https://github.com/pkg/errors
|
||||
methodStackTrace := reflect.ValueOf(err).MethodByName("StackTrace")
|
||||
// https://github.com/go-errors/errors
|
||||
methodStackFrames := reflect.ValueOf(err).MethodByName("StackFrames")
|
||||
|
||||
if methodGetStackTracer.IsValid() {
|
||||
stacktracer := methodGetStackTracer.Call(make([]reflect.Value, 0))[0]
|
||||
stacktracerStackTrace := reflect.ValueOf(stacktracer).MethodByName("StackTrace")
|
||||
|
||||
if stacktracerStackTrace.IsValid() {
|
||||
method = stacktracerStackTrace
|
||||
}
|
||||
}
|
||||
|
||||
if methodStackTrace.IsValid() {
|
||||
method = methodStackTrace
|
||||
}
|
||||
|
||||
if methodStackFrames.IsValid() {
|
||||
method = methodStackFrames
|
||||
}
|
||||
|
||||
return method
|
||||
}
|
||||
|
||||
func extractPcs(method reflect.Value) []uintptr {
|
||||
var pcs []uintptr
|
||||
|
||||
stacktrace := method.Call(make([]reflect.Value, 0))[0]
|
||||
|
||||
if stacktrace.Kind() != reflect.Slice {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < stacktrace.Len(); i++ {
|
||||
pc := stacktrace.Index(i)
|
||||
|
||||
if pc.Kind() == reflect.Uintptr {
|
||||
pcs = append(pcs, uintptr(pc.Uint()))
|
||||
continue
|
||||
}
|
||||
|
||||
if pc.Kind() == reflect.Struct {
|
||||
field := pc.FieldByName("ProgramCounter")
|
||||
if field.IsValid() && field.Kind() == reflect.Uintptr {
|
||||
pcs = append(pcs, uintptr(field.Uint()))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pcs
|
||||
}
|
||||
|
||||
// https://docs.sentry.io/development/sdk-dev/interfaces/stacktrace/
|
||||
type Frame struct {
|
||||
Function string `json:"function,omitempty"`
|
||||
Symbol string `json:"symbol,omitempty"`
|
||||
Module string `json:"module,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
Filename string `json:"filename,omitempty"`
|
||||
AbsPath string `json:"abs_path,omitempty"`
|
||||
Lineno int `json:"lineno,omitempty"`
|
||||
Colno int `json:"colno,omitempty"`
|
||||
PreContext []string `json:"pre_context,omitempty"`
|
||||
ContextLine string `json:"context_line,omitempty"`
|
||||
PostContext []string `json:"post_context,omitempty"`
|
||||
InApp bool `json:"in_app,omitempty"`
|
||||
Vars map[string]interface{} `json:"vars,omitempty"`
|
||||
}
|
||||
|
||||
// NewFrame assembles a stacktrace frame out of `runtime.Frame`.
|
||||
func NewFrame(f runtime.Frame) Frame {
|
||||
abspath := f.File
|
||||
filename := f.File
|
||||
function := f.Function
|
||||
var module string
|
||||
|
||||
if filename != "" {
|
||||
filename = extractFilename(filename)
|
||||
} else {
|
||||
filename = unknown
|
||||
}
|
||||
|
||||
if abspath == "" {
|
||||
abspath = unknown
|
||||
}
|
||||
|
||||
if function != "" {
|
||||
module, function = deconstructFunctionName(function)
|
||||
}
|
||||
|
||||
frame := Frame{
|
||||
AbsPath: abspath,
|
||||
Filename: filename,
|
||||
Lineno: f.Line,
|
||||
Module: module,
|
||||
Function: function,
|
||||
}
|
||||
|
||||
frame.InApp = isInAppFrame(frame)
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
func extractFrames(pcs []uintptr) []Frame {
|
||||
var frames []Frame
|
||||
callersFrames := runtime.CallersFrames(pcs)
|
||||
|
||||
for {
|
||||
callerFrame, more := callersFrames.Next()
|
||||
|
||||
frames = append([]Frame{
|
||||
NewFrame(callerFrame),
|
||||
}, frames...)
|
||||
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return frames
|
||||
}
|
||||
|
||||
func filterFrames(frames []Frame) []Frame {
|
||||
isTestFileRegexp := regexp.MustCompile(`getsentry/sentry-go/.+_test.go`)
|
||||
isExampleFileRegexp := regexp.MustCompile(`getsentry/sentry-go/example/`)
|
||||
filteredFrames := make([]Frame, 0, len(frames))
|
||||
|
||||
for _, frame := range frames {
|
||||
// go runtime frames
|
||||
if frame.Module == "runtime" || frame.Module == "testing" {
|
||||
continue
|
||||
}
|
||||
// sentry internal frames
|
||||
isTestFile := isTestFileRegexp.MatchString(frame.AbsPath)
|
||||
isExampleFile := isExampleFileRegexp.MatchString(frame.AbsPath)
|
||||
if strings.Contains(frame.AbsPath, "github.com/getsentry/sentry-go") &&
|
||||
!isTestFile &&
|
||||
!isExampleFile {
|
||||
continue
|
||||
}
|
||||
filteredFrames = append(filteredFrames, frame)
|
||||
}
|
||||
|
||||
return filteredFrames
|
||||
}
|
||||
|
||||
func extractFilename(path string) string {
|
||||
_, file := filepath.Split(path)
|
||||
return file
|
||||
}
|
||||
|
||||
func isInAppFrame(frame Frame) bool {
|
||||
if strings.HasPrefix(frame.AbsPath, build.Default.GOROOT) ||
|
||||
strings.Contains(frame.Module, "vendor") ||
|
||||
strings.Contains(frame.Module, "third_party") {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Transform `runtime/debug.*T·ptrmethod` into `{ module: runtime/debug, function: *T.ptrmethod }`
|
||||
func deconstructFunctionName(name string) (module string, function string) {
|
||||
if idx := strings.LastIndex(name, "."); idx != -1 {
|
||||
module = name[:idx]
|
||||
function = name[idx+1:]
|
||||
}
|
||||
function = strings.Replace(function, "·", ".", -1)
|
||||
return module, function
|
||||
}
|
||||
|
||||
func callerFunctionName() string {
|
||||
pcs := make([]uintptr, 1)
|
||||
runtime.Callers(3, pcs)
|
||||
callersFrames := runtime.CallersFrames(pcs)
|
||||
callerFrame, _ := callersFrames.Next()
|
||||
_, function := deconstructFunctionName(callerFrame.Function)
|
||||
return function
|
||||
}
|
316
vendor/github.com/getsentry/sentry-go/transport.go
generated
vendored
Normal file
316
vendor/github.com/getsentry/sentry-go/transport.go
generated
vendored
Normal file
@ -0,0 +1,316 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const defaultBufferSize = 30
|
||||
const defaultRetryAfter = time.Second * 60
|
||||
const defaultTimeout = time.Second * 30
|
||||
|
||||
// Transport is used by the `Client` to deliver events to remote server.
|
||||
type Transport interface {
|
||||
Flush(timeout time.Duration) bool
|
||||
Configure(options ClientOptions)
|
||||
SendEvent(event *Event)
|
||||
}
|
||||
|
||||
func getProxyConfig(options ClientOptions) func(*http.Request) (*url.URL, error) {
|
||||
if options.HTTPSProxy != "" {
|
||||
return func(_ *http.Request) (*url.URL, error) {
|
||||
return url.Parse(options.HTTPSProxy)
|
||||
}
|
||||
} else if options.HTTPProxy != "" {
|
||||
return func(_ *http.Request) (*url.URL, error) {
|
||||
return url.Parse(options.HTTPProxy)
|
||||
}
|
||||
}
|
||||
|
||||
return http.ProxyFromEnvironment
|
||||
}
|
||||
|
||||
func getTLSConfig(options ClientOptions) *tls.Config {
|
||||
if options.CaCerts != nil {
|
||||
return &tls.Config{
|
||||
RootCAs: options.CaCerts,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func retryAfter(now time.Time, r *http.Response) time.Duration {
|
||||
retryAfterHeader := r.Header["Retry-After"]
|
||||
|
||||
if retryAfterHeader == nil {
|
||||
return defaultRetryAfter
|
||||
}
|
||||
|
||||
if date, err := time.Parse(time.RFC1123, retryAfterHeader[0]); err == nil {
|
||||
return date.Sub(now)
|
||||
}
|
||||
|
||||
if seconds, err := strconv.Atoi(retryAfterHeader[0]); err == nil {
|
||||
return time.Second * time.Duration(seconds)
|
||||
}
|
||||
|
||||
return defaultRetryAfter
|
||||
}
|
||||
|
||||
// ================================
|
||||
// HTTPTransport
|
||||
// ================================
|
||||
|
||||
// HTTPTransport is a default implementation of `Transport` interface used by `Client`.
|
||||
type HTTPTransport struct {
|
||||
dsn *Dsn
|
||||
client *http.Client
|
||||
transport *http.Transport
|
||||
|
||||
buffer chan *http.Request
|
||||
disabledUntil time.Time
|
||||
|
||||
wg sync.WaitGroup
|
||||
start sync.Once
|
||||
|
||||
// Size of the transport buffer. Defaults to 30.
|
||||
BufferSize int
|
||||
// HTTP Client request timeout. Defaults to 30 seconds.
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// NewHTTPTransport returns a new pre-configured instance of HTTPTransport
|
||||
func NewHTTPTransport() *HTTPTransport {
|
||||
transport := HTTPTransport{
|
||||
BufferSize: defaultBufferSize,
|
||||
Timeout: defaultTimeout,
|
||||
}
|
||||
return &transport
|
||||
}
|
||||
|
||||
// Configure is called by the `Client` itself, providing it it's own `ClientOptions`.
|
||||
func (t *HTTPTransport) Configure(options ClientOptions) {
|
||||
dsn, err := NewDsn(options.Dsn)
|
||||
if err != nil {
|
||||
Logger.Printf("%v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.dsn = dsn
|
||||
t.buffer = make(chan *http.Request, t.BufferSize)
|
||||
|
||||
if options.HTTPTransport != nil {
|
||||
t.transport = options.HTTPTransport
|
||||
} else {
|
||||
t.transport = &http.Transport{
|
||||
Proxy: getProxyConfig(options),
|
||||
TLSClientConfig: getTLSConfig(options),
|
||||
}
|
||||
}
|
||||
|
||||
t.client = &http.Client{
|
||||
Transport: t.transport,
|
||||
Timeout: t.Timeout,
|
||||
}
|
||||
|
||||
t.start.Do(func() {
|
||||
go t.worker()
|
||||
})
|
||||
}
|
||||
|
||||
// SendEvent assembles a new packet out of `Event` and sends it to remote server.
|
||||
func (t *HTTPTransport) SendEvent(event *Event) {
|
||||
if t.dsn == nil || time.Now().Before(t.disabledUntil) {
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(event)
|
||||
|
||||
request, _ := http.NewRequest(
|
||||
http.MethodPost,
|
||||
t.dsn.StoreAPIURL().String(),
|
||||
bytes.NewBuffer(body),
|
||||
)
|
||||
|
||||
for headerKey, headerValue := range t.dsn.RequestHeaders() {
|
||||
request.Header.Set(headerKey, headerValue)
|
||||
}
|
||||
|
||||
t.wg.Add(1)
|
||||
|
||||
select {
|
||||
case t.buffer <- request:
|
||||
Logger.Printf(
|
||||
"Sending %s event [%s] to %s project: %d\n",
|
||||
event.Level,
|
||||
event.EventID,
|
||||
t.dsn.host,
|
||||
t.dsn.projectID,
|
||||
)
|
||||
default:
|
||||
t.wg.Done()
|
||||
Logger.Println("Event dropped due to transport buffer being full.")
|
||||
// worker would block, drop the packet
|
||||
}
|
||||
}
|
||||
|
||||
// Flush notifies when all the buffered events have been sent by returning `true`
|
||||
// or `false` if timeout was reached.
|
||||
func (t *HTTPTransport) Flush(timeout time.Duration) bool {
|
||||
c := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
t.wg.Wait()
|
||||
close(c)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-c:
|
||||
Logger.Println("Buffer flushed successfully.")
|
||||
return true
|
||||
case <-time.After(timeout):
|
||||
Logger.Println("Buffer flushing reached the timeout.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (t *HTTPTransport) worker() {
|
||||
for request := range t.buffer {
|
||||
if time.Now().Before(t.disabledUntil) {
|
||||
t.wg.Done()
|
||||
continue
|
||||
}
|
||||
|
||||
response, err := t.client.Do(request)
|
||||
|
||||
if err != nil {
|
||||
Logger.Printf("There was an issue with sending an event: %v", err)
|
||||
}
|
||||
|
||||
if response != nil && response.StatusCode == http.StatusTooManyRequests {
|
||||
t.disabledUntil = time.Now().Add(retryAfter(time.Now(), response))
|
||||
Logger.Printf("Too many requests, backing off till: %s\n", t.disabledUntil)
|
||||
}
|
||||
|
||||
t.wg.Done()
|
||||
}
|
||||
}
|
||||
|
||||
// ================================
|
||||
// HTTPSyncTransport
|
||||
// ================================
|
||||
|
||||
// HTTPSyncTransport is an implementation of `Transport` interface which blocks after each captured event.
|
||||
type HTTPSyncTransport struct {
|
||||
dsn *Dsn
|
||||
client *http.Client
|
||||
transport *http.Transport
|
||||
disabledUntil time.Time
|
||||
|
||||
// HTTP Client request timeout. Defaults to 30 seconds.
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// NewHTTPSyncTransport returns a new pre-configured instance of HTTPSyncTransport
|
||||
func NewHTTPSyncTransport() *HTTPSyncTransport {
|
||||
transport := HTTPSyncTransport{
|
||||
Timeout: defaultTimeout,
|
||||
}
|
||||
|
||||
return &transport
|
||||
}
|
||||
|
||||
// Configure is called by the `Client` itself, providing it it's own `ClientOptions`.
|
||||
func (t *HTTPSyncTransport) Configure(options ClientOptions) {
|
||||
dsn, err := NewDsn(options.Dsn)
|
||||
if err != nil {
|
||||
Logger.Printf("%v\n", err)
|
||||
return
|
||||
}
|
||||
t.dsn = dsn
|
||||
|
||||
if options.HTTPTransport != nil {
|
||||
t.transport = options.HTTPTransport
|
||||
} else {
|
||||
t.transport = &http.Transport{
|
||||
Proxy: getProxyConfig(options),
|
||||
TLSClientConfig: getTLSConfig(options),
|
||||
}
|
||||
}
|
||||
|
||||
t.client = &http.Client{
|
||||
Transport: t.transport,
|
||||
Timeout: t.Timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// SendEvent assembles a new packet out of `Event` and sends it to remote server.
|
||||
func (t *HTTPSyncTransport) SendEvent(event *Event) {
|
||||
if t.dsn == nil || time.Now().Before(t.disabledUntil) {
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(event)
|
||||
|
||||
request, _ := http.NewRequest(
|
||||
http.MethodPost,
|
||||
t.dsn.StoreAPIURL().String(),
|
||||
bytes.NewBuffer(body),
|
||||
)
|
||||
|
||||
for headerKey, headerValue := range t.dsn.RequestHeaders() {
|
||||
request.Header.Set(headerKey, headerValue)
|
||||
}
|
||||
|
||||
Logger.Printf(
|
||||
"Sending %s event [%s] to %s project: %d\n",
|
||||
event.Level,
|
||||
event.EventID,
|
||||
t.dsn.host,
|
||||
t.dsn.projectID,
|
||||
)
|
||||
|
||||
response, err := t.client.Do(request)
|
||||
|
||||
if err != nil {
|
||||
Logger.Printf("There was an issue with sending an event: %v", err)
|
||||
}
|
||||
|
||||
if response != nil && response.StatusCode == http.StatusTooManyRequests {
|
||||
t.disabledUntil = time.Now().Add(retryAfter(time.Now(), response))
|
||||
Logger.Printf("Too many requests, backing off till: %s\n", t.disabledUntil)
|
||||
}
|
||||
}
|
||||
|
||||
// Flush notifies when all the buffered events have been sent by returning `true`
|
||||
// or `false` if timeout was reached. No-op for HTTPSyncTransport.
|
||||
func (t *HTTPSyncTransport) Flush(_ time.Duration) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ================================
|
||||
// noopTransport
|
||||
// ================================
|
||||
|
||||
// noopTransport is an implementation of `Transport` interface which drops all the events.
|
||||
// Only used internally when an empty DSN is provided, which effectively disables the SDK.
|
||||
type noopTransport struct{}
|
||||
|
||||
func (t *noopTransport) Configure(options ClientOptions) {
|
||||
Logger.Println("Sentry client initialized with an empty DSN. Using noopTransport. No events will be delivered.")
|
||||
}
|
||||
|
||||
func (t *noopTransport) SendEvent(event *Event) {
|
||||
Logger.Println("Event dropped due to noopTransport usage.")
|
||||
}
|
||||
|
||||
func (t *noopTransport) Flush(_ time.Duration) bool {
|
||||
return true
|
||||
}
|
34
vendor/github.com/getsentry/sentry-go/util.go
generated
vendored
Normal file
34
vendor/github.com/getsentry/sentry-go/util.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func uuid() string {
|
||||
id := make([]byte, 16)
|
||||
_, _ = io.ReadFull(rand.Reader, id)
|
||||
id[6] &= 0x0F // clear version
|
||||
id[6] |= 0x40 // set version to 4 (random uuid)
|
||||
id[8] &= 0x3F // clear variant
|
||||
id[8] |= 0x80 // set to IETF variant
|
||||
return hex.EncodeToString(id)
|
||||
}
|
||||
|
||||
func fileExists(fileName string) bool {
|
||||
if _, err := os.Stat(fileName); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// nolint: deadcode, unused
|
||||
func prettyPrint(data interface{}) {
|
||||
dbg, _ := json.MarshalIndent(data, "", " ")
|
||||
fmt.Println(string(dbg))
|
||||
}
|
24
vendor/github.com/pkg/errors/.gitignore
generated
vendored
24
vendor/github.com/pkg/errors/.gitignore
generated
vendored
@ -1,24 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
15
vendor/github.com/pkg/errors/.travis.yml
generated
vendored
15
vendor/github.com/pkg/errors/.travis.yml
generated
vendored
@ -1,15 +0,0 @@
|
||||
language: go
|
||||
go_import_path: github.com/pkg/errors
|
||||
go:
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
@ -1,23 +0,0 @@
|
||||
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.
|
52
vendor/github.com/pkg/errors/README.md
generated
vendored
52
vendor/github.com/pkg/errors/README.md
generated
vendored
@ -1,52 +0,0 @@
|
||||
# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge)
|
||||
|
||||
Package errors provides simple error handling primitives.
|
||||
|
||||
`go get github.com/pkg/errors`
|
||||
|
||||
The traditional error handling idiom in Go is roughly akin to
|
||||
```go
|
||||
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. For example
|
||||
```go
|
||||
_, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read failed")
|
||||
}
|
||||
```
|
||||
## 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 can be inspected by `errors.Cause`.
|
||||
```go
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
```
|
||||
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
|
||||
```go
|
||||
switch err := errors.Cause(err).(type) {
|
||||
case *MyError:
|
||||
// handle specifically
|
||||
default:
|
||||
// unknown error
|
||||
}
|
||||
```
|
||||
|
||||
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
|
||||
|
||||
Before proposing a change, please discuss your change by raising an issue.
|
||||
|
||||
## License
|
||||
|
||||
BSD-2-Clause
|
32
vendor/github.com/pkg/errors/appveyor.yml
generated
vendored
32
vendor/github.com/pkg/errors/appveyor.yml
generated
vendored
@ -1,32 +0,0 @@
|
||||
version: build-{build}.{branch}
|
||||
|
||||
clone_folder: C:\gopath\src\github.com\pkg\errors
|
||||
shallow_clone: true # for startup speed
|
||||
|
||||
environment:
|
||||
GOPATH: C:\gopath
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
# http://www.appveyor.com/docs/installed-software
|
||||
install:
|
||||
# some helpful output for debugging builds
|
||||
- go version
|
||||
- go env
|
||||
# pre-installed MinGW at C:\MinGW is 32bit only
|
||||
# but MSYS2 at C:\msys64 has mingw64
|
||||
- set PATH=C:\msys64\mingw64\bin;%PATH%
|
||||
- gcc --version
|
||||
- g++ --version
|
||||
|
||||
build_script:
|
||||
- go install -v ./...
|
||||
|
||||
test_script:
|
||||
- set PATH=C:\gopath\bin;%PATH%
|
||||
- go test -v ./...
|
||||
|
||||
#artifacts:
|
||||
# - path: '%GOPATH%\bin\*.exe'
|
||||
deploy: off
|
282
vendor/github.com/pkg/errors/errors.go
generated
vendored
282
vendor/github.com/pkg/errors/errors.go
generated
vendored
@ -1,282 +0,0 @@
|
||||
// Package errors provides simple error handling primitives.
|
||||
//
|
||||
// The traditional error handling idiom in Go is roughly akin to
|
||||
//
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// which when 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,
|
||||
// together with 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: annotating an error with a stack trace and with 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 that 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
|
||||
// }
|
||||
//
|
||||
// Although the causer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// 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
|
||||
// }
|
||||
//
|
||||
// The returned errors.StackTrace type 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)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Although the stackTracer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// 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 called, 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,
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessagef annotates err with the format specifier.
|
||||
// If err is nil, WithMessagef returns nil.
|
||||
func WithMessagef(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
147
vendor/github.com/pkg/errors/stack.go
generated
vendored
147
vendor/github.com/pkg/errors/stack.go
generated
vendored
@ -1,147 +0,0 @@
|
||||
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 function name and path of source file relative to the compile time
|
||||
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
||||
// %+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
|
||||
|
||||
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s lists source files for each Frame in the stack
|
||||
// %v lists the source file and line number for each Frame in the stack
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||
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:]
|
||||
}
|
8
vendor/modules.txt
vendored
8
vendor/modules.txt
vendored
@ -53,12 +53,10 @@ github.com/bugsnag/bugsnag-go/headers
|
||||
github.com/bugsnag/bugsnag-go/sessions
|
||||
# github.com/bugsnag/panicwrap v1.2.0
|
||||
github.com/bugsnag/panicwrap
|
||||
# github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261
|
||||
github.com/certifi/gocertifi
|
||||
# github.com/davecgh/go-spew v1.1.1
|
||||
github.com/davecgh/go-spew/spew
|
||||
# github.com/getsentry/raven-go v0.2.0
|
||||
github.com/getsentry/raven-go
|
||||
# github.com/getsentry/sentry-go v0.2.1
|
||||
github.com/getsentry/sentry-go
|
||||
# github.com/go-ole/go-ole v1.2.2
|
||||
github.com/go-ole/go-ole
|
||||
github.com/go-ole/go-ole/oleutil
|
||||
@ -99,8 +97,6 @@ github.com/newrelic/go-agent/internal/jsonx
|
||||
github.com/newrelic/go-agent/internal/sysinfo
|
||||
# github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709
|
||||
github.com/pborman/uuid
|
||||
# github.com/pkg/errors v0.8.1
|
||||
github.com/pkg/errors
|
||||
# github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/pmezard/go-difflib/difflib
|
||||
# github.com/prometheus/client_golang v0.9.2
|
||||
|
Loading…
x
Reference in New Issue
Block a user