*: raven is archived, long live sentry-go
I don't have sentry set up to test this, but it looks like it is migrated correctly. Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
This commit is contained in:
parent
f9ceebad58
commit
cabf16cc0a
97 changed files with 4518 additions and 12599 deletions
416
vendor/github.com/getsentry/sentry-go/scope.go
generated
vendored
Normal file
416
vendor/github.com/getsentry/sentry-go/scope.go
generated
vendored
Normal file
|
@ -0,0 +1,416 @@
|
|||
package sentry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"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 {
|
||||
mu 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 *http.Request
|
||||
// requestBody holds a reference to the original request.Body.
|
||||
requestBody interface {
|
||||
// Bytes returns bytes from the original body, lazily buffered as the
|
||||
// original body is read.
|
||||
Bytes() []byte
|
||||
// Overflow returns true if the body is larger than the maximum buffer
|
||||
// size.
|
||||
Overflow() bool
|
||||
}
|
||||
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 optionally throws the old one if limit is reached.
|
||||
func (scope *Scope) AddBreadcrumb(breadcrumb *Breadcrumb, limit int) {
|
||||
if breadcrumb.Timestamp.IsZero() {
|
||||
breadcrumb.Timestamp = time.Now().UTC()
|
||||
}
|
||||
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.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.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
scope.breadcrumbs = []*Breadcrumb{}
|
||||
}
|
||||
|
||||
// SetUser sets the user for the current scope.
|
||||
func (scope *Scope) SetUser(user User) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
scope.user = user
|
||||
}
|
||||
|
||||
// SetRequest sets the request for the current scope.
|
||||
func (scope *Scope) SetRequest(r *http.Request) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
scope.request = r
|
||||
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't buffer request body if we know it is oversized.
|
||||
if r.ContentLength > maxRequestBodyBytes {
|
||||
return
|
||||
}
|
||||
// Don't buffer if there is no body.
|
||||
if r.Body == nil || r.Body == http.NoBody {
|
||||
return
|
||||
}
|
||||
buf := &limitedBuffer{Capacity: maxRequestBodyBytes}
|
||||
r.Body = readCloser{
|
||||
Reader: io.TeeReader(r.Body, buf),
|
||||
Closer: r.Body,
|
||||
}
|
||||
scope.requestBody = buf
|
||||
}
|
||||
|
||||
// SetRequestBody sets the request body for the current scope.
|
||||
//
|
||||
// This method should only be called when the body bytes are already available
|
||||
// in memory. Typically, the request body is buffered lazily from the
|
||||
// Request.Body from SetRequest.
|
||||
func (scope *Scope) SetRequestBody(b []byte) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
capacity := maxRequestBodyBytes
|
||||
overflow := false
|
||||
if len(b) > capacity {
|
||||
overflow = true
|
||||
b = b[:capacity]
|
||||
}
|
||||
scope.requestBody = &limitedBuffer{
|
||||
Capacity: capacity,
|
||||
Buffer: *bytes.NewBuffer(b),
|
||||
overflow: overflow,
|
||||
}
|
||||
}
|
||||
|
||||
// maxRequestBodyBytes is the default maximum request body size to send to
|
||||
// Sentry.
|
||||
const maxRequestBodyBytes = 10 * 1024
|
||||
|
||||
// A limitedBuffer is like a bytes.Buffer, but limited to store at most Capacity
|
||||
// bytes. Any writes past the capacity are silently discarded, similar to
|
||||
// ioutil.Discard.
|
||||
type limitedBuffer struct {
|
||||
Capacity int
|
||||
|
||||
bytes.Buffer
|
||||
overflow bool
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (b *limitedBuffer) Write(p []byte) (n int, err error) {
|
||||
// Silently ignore writes after overflow.
|
||||
if b.overflow {
|
||||
return len(p), nil
|
||||
}
|
||||
left := b.Capacity - b.Len()
|
||||
if left < 0 {
|
||||
left = 0
|
||||
}
|
||||
if len(p) > left {
|
||||
b.overflow = true
|
||||
p = p[:left]
|
||||
}
|
||||
return b.Buffer.Write(p)
|
||||
}
|
||||
|
||||
// Overflow returns true if the limitedBuffer discarded bytes written to it.
|
||||
func (b *limitedBuffer) Overflow() bool {
|
||||
return b.overflow
|
||||
}
|
||||
|
||||
// readCloser combines an io.Reader and an io.Closer to implement io.ReadCloser.
|
||||
type readCloser struct {
|
||||
io.Reader
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// SetTag adds a tag to the current scope.
|
||||
func (scope *Scope) SetTag(key, value string) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
scope.tags[key] = value
|
||||
}
|
||||
|
||||
// SetTags assigns multiple tags to the current scope.
|
||||
func (scope *Scope) SetTags(tags map[string]string) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.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.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
delete(scope.tags, key)
|
||||
}
|
||||
|
||||
// SetContext adds a context to the current scope.
|
||||
func (scope *Scope) SetContext(key string, value interface{}) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
scope.contexts[key] = value
|
||||
}
|
||||
|
||||
// SetContexts assigns multiple contexts to the current scope.
|
||||
func (scope *Scope) SetContexts(contexts map[string]interface{}) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.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.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
delete(scope.contexts, key)
|
||||
}
|
||||
|
||||
// SetExtra adds an extra to the current scope.
|
||||
func (scope *Scope) SetExtra(key string, value interface{}) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
scope.extra[key] = value
|
||||
}
|
||||
|
||||
// SetExtras assigns multiple extras to the current scope.
|
||||
func (scope *Scope) SetExtras(extra map[string]interface{}) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.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.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
delete(scope.extra, key)
|
||||
}
|
||||
|
||||
// SetFingerprint sets new fingerprint for the current scope.
|
||||
func (scope *Scope) SetFingerprint(fingerprint []string) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
scope.fingerprint = fingerprint
|
||||
}
|
||||
|
||||
// SetLevel sets new level for the current scope.
|
||||
func (scope *Scope) SetLevel(level Level) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
scope.level = level
|
||||
}
|
||||
|
||||
// SetTransaction sets new transaction name for the current transaction.
|
||||
func (scope *Scope) SetTransaction(transactionName string) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
scope.transaction = transactionName
|
||||
}
|
||||
|
||||
// Clone returns a copy of the current scope with all data copied over.
|
||||
func (scope *Scope) Clone() *Scope {
|
||||
scope.mu.RLock()
|
||||
defer scope.mu.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 removes the data from the current scope. Not safe for concurrent use.
|
||||
func (scope *Scope) Clear() {
|
||||
*scope = *NewScope()
|
||||
}
|
||||
|
||||
// AddEventProcessor adds an event processor to the current scope.
|
||||
func (scope *Scope) AddEventProcessor(processor EventProcessor) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.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.mu.RLock()
|
||||
defer scope.mu.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 event.Request == nil && scope.request != nil {
|
||||
event.Request = NewRequest(scope.request)
|
||||
// NOTE: The SDK does not attempt to send partial request body data.
|
||||
//
|
||||
// The reason being that Sentry's ingest pipeline and UI are optimized
|
||||
// to show structured data. Additionally, tooling around PII scrubbing
|
||||
// relies on structured data; truncated request bodies would create
|
||||
// invalid payloads that are more prone to leaking PII data.
|
||||
//
|
||||
// Users can still send more data along their events if they want to,
|
||||
// for example using Event.Extra.
|
||||
if scope.requestBody != nil && !scope.requestBody.Overflow() {
|
||||
event.Request.Data = string(scope.requestBody.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue