瀏覽代碼

Added Sentry

Yoginth 7 年之前
父節點
當前提交
491a7fac57

+ 3 - 0
vendor/github.com/certifi/gocertifi/LICENSE

@@ -0,0 +1,3 @@
+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 - 0
vendor/github.com/certifi/gocertifi/README.md

@@ -0,0 +1,60 @@
+# 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.

File diff suppressed because it is too large
+ 4330 - 0
vendor/github.com/certifi/gocertifi/certifi.go


+ 13 - 0
vendor/github.com/getsentry/raven-go/Dockerfile.test

@@ -0,0 +1,13 @@
+FROM golang:1.7
+
+RUN mkdir -p /go/src/github.com/getsentry/raven-go
+WORKDIR /go/src/github.com/getsentry/raven-go
+ENV GOPATH /go
+
+RUN go install -race std && go get golang.org/x/tools/cmd/cover
+
+COPY . /go/src/github.com/getsentry/raven-go
+
+RUN go get -v ./...
+
+CMD ["./runtests.sh"]

+ 28 - 0
vendor/github.com/getsentry/raven-go/LICENSE

@@ -0,0 +1,28 @@
+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.

+ 17 - 0
vendor/github.com/getsentry/raven-go/README.md

@@ -0,0 +1,17 @@
+# 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 a Go client 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
+```

+ 977 - 0
vendor/github.com/getsentry/raven-go/client.go

@@ -0,0 +1,977 @@
+// 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 - 0
vendor/github.com/getsentry/raven-go/errors.go

@@ -0,0 +1,60 @@
+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 - 0
vendor/github.com/getsentry/raven-go/exception.go

@@ -0,0 +1,50 @@
+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 - 0
vendor/github.com/getsentry/raven-go/http.go

@@ -0,0 +1,99 @@
+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 - 0
vendor/github.com/getsentry/raven-go/interfaces.go

@@ -0,0 +1,49 @@
+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 - 0
vendor/github.com/getsentry/raven-go/runtests.sh

@@ -0,0 +1,4 @@
+#!/bin/bash
+go test -race ./...
+go test -cover ./...
+go test -v ./...

+ 276 - 0
vendor/github.com/getsentry/raven-go/stacktrace.go

@@ -0,0 +1,276 @@
+// 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"
+	"net/url"
+	"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 file string
+			var line int
+			if fn != nil {
+				file, line = fn.FileLine(pc)
+			} else {
+				file = "unknown"
+			}
+			frame := NewStacktraceFrame(pc, 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
+	for i := 1 + skip; ; i++ {
+		pc, file, line, ok := runtime.Caller(i)
+		if !ok {
+			break
+		}
+		frame := NewStacktraceFrame(pc, file, line, context, appPackagePrefixes)
+		if frame != nil {
+			frames = append(frames, frame)
+		}
+	}
+	// 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, file string, line, context int, appPackagePrefixes []string) *StacktraceFrame {
+	frame := &StacktraceFrame{AbsolutePath: file, Filename: trimPath(file), Lineno: line, InApp: false}
+	frame.Module, frame.Function = functionName(pc)
+
+	// `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(pc uintptr) (string, string) {
+	fn := runtime.FuncForPC(pc)
+	if fn == nil {
+		return "", ""
+	}
+
+	return splitFunctionName(fn.Name())
+}
+
+func splitFunctionName(name string) (string, string) {
+	var pack string
+
+	if pos := strings.LastIndex(name, "/"); pos != -1 {
+		pack = name[:pos+1]
+		name = name[pos+1:]
+	}
+
+	if pos := strings.Index(name, "."); pos != -1 {
+		pack += name[:pos]
+		name = name[pos+1:]
+	}
+
+	if p, err := url.QueryUnescape(pack); err == nil {
+		pack = p
+	}
+
+	return pack, name
+}
+
+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 - 0
vendor/github.com/getsentry/raven-go/writer.go

@@ -0,0 +1,20 @@
+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
+}

+ 23 - 0
vendor/github.com/pkg/errors/LICENSE

@@ -0,0 +1,23 @@
+Copyright (c) 2015, Dave Cheney <dave@cheney.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large
+ 52 - 0
vendor/github.com/pkg/errors/README.md


+ 32 - 0
vendor/github.com/pkg/errors/appveyor.yml

@@ -0,0 +1,32 @@
+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 - 0
vendor/github.com/pkg/errors/errors.go

@@ -0,0 +1,282 @@
+// 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 - 0
vendor/github.com/pkg/errors/stack.go

@@ -0,0 +1,147 @@
+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:]
+}

+ 18 - 0
vendor/vendor.json

@@ -44,6 +44,12 @@
 			"revision": "fb1f79c6b65acda83063cbc69f6bba1522558bfc",
 			"revisionTime": "2016-01-17T19:21:50Z"
 		},
+		{
+			"checksumSHA1": "YPQ2GOj+Yt62cQGmA78MN2JbRyM=",
+			"path": "github.com/certifi/gocertifi",
+			"revision": "ee1a9a0726d2ae45f54118cac878c990d4016ded",
+			"revisionTime": "2018-08-28T16:03:48Z"
+		},
 		{
 			"checksumSHA1": "NKoZRlZix5wzCfN0rTg29GtKZRU=",
 			"path": "github.com/chaseadamsio/goorgeous",
@@ -62,6 +68,12 @@
 			"revision": "42c364ba490082e4815b5222728711b3440603eb",
 			"revisionTime": "2017-01-13T15:16:12Z"
 		},
+		{
+			"checksumSHA1": "hL8smC/vjdkuE1twM8TKpuTiOmw=",
+			"path": "github.com/getsentry/raven-go",
+			"revision": "3033899c76deb3fb6570d9c4074d00443aeab88f",
+			"revisionTime": "2018-10-23T13:08:49Z"
+		},
 		{
 			"checksumSHA1": "J0tcl2i76AQvMio9MWNQaucepYA=",
 			"path": "github.com/go-macaron/binding",
@@ -284,6 +296,12 @@
 			"revision": "891127d8d1b52734debe1b3c3d7e747502b6c366",
 			"revisionTime": "2016-07-24T20:39:20Z"
 		},
+		{
+			"checksumSHA1": "DTy0iJ2w5C+FDsN9EnzfhNmvS+o=",
+			"path": "github.com/pkg/errors",
+			"revision": "059132a15dd08d6704c67711dae0cf35ab991756",
+			"revisionTime": "2018-10-23T23:59:46Z"
+		},
 		{
 			"checksumSHA1": "woY3inKe+d7B1jPTFxVKNCCFH9c=",
 			"path": "github.com/pquerna/otp",