Bumps the go-deps group with 11 updates in the / directory: | Package | From | To | | --- | --- | --- | | [cloud.google.com/go/compute](https://github.com/googleapis/google-cloud-go) | `1.25.1` | `1.26.0` | | [github.com/Azure/azure-sdk-for-go/sdk/azidentity](https://github.com/Azure/azure-sdk-for-go) | `1.5.1` | `1.5.2` | | [github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5](https://github.com/Azure/azure-sdk-for-go) | `5.5.0` | `5.7.0` | | [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) | `1.51.25` | `1.52.0` | | [github.com/getsentry/sentry-go](https://github.com/getsentry/sentry-go) | `0.26.0` | `0.27.0` | | [github.com/gophercloud/gophercloud](https://github.com/gophercloud/gophercloud) | `1.10.0` | `1.11.0` | | [github.com/jackc/pgtype](https://github.com/jackc/pgtype) | `1.14.1` | `1.14.3` | | [github.com/labstack/echo/v4](https://github.com/labstack/echo) | `4.11.4` | `4.12.0` | | [github.com/openshift-online/ocm-sdk-go](https://github.com/openshift-online/ocm-sdk-go) | `0.1.398` | `0.1.418` | | [github.com/osbuild/images](https://github.com/osbuild/images) | `0.56.0` | `0.58.0` | | [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) | `1.18.0` | `1.19.0` | Updates `cloud.google.com/go/compute` from 1.25.1 to 1.26.0 - [Release notes](https://github.com/googleapis/google-cloud-go/releases) - [Changelog](https://github.com/googleapis/google-cloud-go/blob/main/documentai/CHANGES.md) - [Commits](https://github.com/googleapis/google-cloud-go/compare/pubsub/v1.25.1...pubsub/v1.26.0) Updates `github.com/Azure/azure-sdk-for-go/sdk/azidentity` from 1.5.1 to 1.5.2 - [Release notes](https://github.com/Azure/azure-sdk-for-go/releases) - [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md) - [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/internal/v1.5.1...sdk/internal/v1.5.2) Updates `github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5` from 5.5.0 to 5.7.0 - [Release notes](https://github.com/Azure/azure-sdk-for-go/releases) - [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md) - [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/resourcemanager/compute/armcompute/v5.5.0...sdk/resourcemanager/compute/armcompute/v5.7.0) Updates `github.com/aws/aws-sdk-go` from 1.51.25 to 1.52.0 - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.51.25...v1.52.0) Updates `github.com/getsentry/sentry-go` from 0.26.0 to 0.27.0 - [Release notes](https://github.com/getsentry/sentry-go/releases) - [Changelog](https://github.com/getsentry/sentry-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-go/compare/v0.26.0...v0.27.0) Updates `github.com/gophercloud/gophercloud` from 1.10.0 to 1.11.0 - [Release notes](https://github.com/gophercloud/gophercloud/releases) - [Changelog](https://github.com/gophercloud/gophercloud/blob/v1.11.0/CHANGELOG.md) - [Commits](https://github.com/gophercloud/gophercloud/compare/v1.10.0...v1.11.0) Updates `github.com/jackc/pgtype` from 1.14.1 to 1.14.3 - [Changelog](https://github.com/jackc/pgtype/blob/master/CHANGELOG.md) - [Commits](https://github.com/jackc/pgtype/compare/v1.14.1...v1.14.3) Updates `github.com/jackc/pgx/v4` from 4.18.1 to 4.18.2 - [Changelog](https://github.com/jackc/pgx/blob/v4.18.2/CHANGELOG.md) - [Commits](https://github.com/jackc/pgx/compare/v4.18.1...v4.18.2) Updates `github.com/labstack/echo/v4` from 4.11.4 to 4.12.0 - [Release notes](https://github.com/labstack/echo/releases) - [Changelog](https://github.com/labstack/echo/blob/master/CHANGELOG.md) - [Commits](https://github.com/labstack/echo/compare/v4.11.4...v4.12.0) Updates `github.com/openshift-online/ocm-sdk-go` from 0.1.398 to 0.1.418 - [Release notes](https://github.com/openshift-online/ocm-sdk-go/releases) - [Changelog](https://github.com/openshift-online/ocm-sdk-go/blob/main/CHANGES.md) - [Commits](https://github.com/openshift-online/ocm-sdk-go/compare/v0.1.398...v0.1.418) Updates `github.com/osbuild/images` from 0.56.0 to 0.58.0 - [Release notes](https://github.com/osbuild/images/releases) - [Commits](https://github.com/osbuild/images/compare/v0.56.0...v0.58.0) Updates `github.com/prometheus/client_golang` from 1.18.0 to 1.19.0 - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.18.0...v1.19.0) Updates `golang.org/x/sync` from 0.6.0 to 0.7.0 - [Commits](https://github.com/golang/sync/compare/v0.6.0...v0.7.0) Updates `google.golang.org/api` from 0.175.0 to 0.177.0 - [Release notes](https://github.com/googleapis/google-api-go-client/releases) - [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md) - [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.175.0...v0.177.0) --- updated-dependencies: - dependency-name: cloud.google.com/go/compute dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-deps - dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azidentity dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-deps - dependency-name: github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-deps - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-deps - dependency-name: github.com/getsentry/sentry-go dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-deps - dependency-name: github.com/gophercloud/gophercloud dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-deps - dependency-name: github.com/jackc/pgtype dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-deps - dependency-name: github.com/jackc/pgx/v4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-deps - dependency-name: github.com/labstack/echo/v4 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-deps - dependency-name: github.com/openshift-online/ocm-sdk-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-deps - dependency-name: github.com/osbuild/images dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-deps - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-deps - dependency-name: golang.org/x/sync dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-deps - dependency-name: google.golang.org/api dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-deps ... Signed-off-by: dependabot[bot] <support@github.com>
443 lines
11 KiB
Go
443 lines
11 KiB
Go
package sentry
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net/http"
|
|
"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. First, the scope is routinely
|
|
// updated with information by functions such as AddBreadcrumb which will modify
|
|
// the current scope. Second, the current scope can be configured through the
|
|
// ConfigureScope function or Hub method of the same name.
|
|
//
|
|
// The scope is meant to be modified but not inspected directly. When preparing
|
|
// an event for reporting, the current client adds information from the current
|
|
// scope into the event.
|
|
type Scope struct {
|
|
mu sync.RWMutex
|
|
breadcrumbs []*Breadcrumb
|
|
attachments []*Attachment
|
|
user User
|
|
tags map[string]string
|
|
contexts map[string]Context
|
|
extra map[string]interface{}
|
|
fingerprint []string
|
|
level Level
|
|
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
|
|
}
|
|
|
|
// NewScope creates a new Scope.
|
|
func NewScope() *Scope {
|
|
scope := Scope{
|
|
breadcrumbs: make([]*Breadcrumb, 0),
|
|
attachments: make([]*Attachment, 0),
|
|
tags: make(map[string]string),
|
|
contexts: make(map[string]Context),
|
|
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()
|
|
}
|
|
|
|
scope.mu.Lock()
|
|
defer scope.mu.Unlock()
|
|
|
|
scope.breadcrumbs = append(scope.breadcrumbs, breadcrumb)
|
|
if len(scope.breadcrumbs) > limit {
|
|
scope.breadcrumbs = scope.breadcrumbs[1 : limit+1]
|
|
}
|
|
}
|
|
|
|
// ClearBreadcrumbs clears all breadcrumbs from the current scope.
|
|
func (scope *Scope) ClearBreadcrumbs() {
|
|
scope.mu.Lock()
|
|
defer scope.mu.Unlock()
|
|
|
|
scope.breadcrumbs = []*Breadcrumb{}
|
|
}
|
|
|
|
// AddAttachment adds new attachment to the current scope.
|
|
func (scope *Scope) AddAttachment(attachment *Attachment) {
|
|
scope.mu.Lock()
|
|
defer scope.mu.Unlock()
|
|
|
|
scope.attachments = append(scope.attachments, attachment)
|
|
}
|
|
|
|
// ClearAttachments clears all attachments from the current scope.
|
|
func (scope *Scope) ClearAttachments() {
|
|
scope.mu.Lock()
|
|
defer scope.mu.Unlock()
|
|
|
|
scope.attachments = []*Attachment{}
|
|
}
|
|
|
|
// 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
|
|
// io.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 Context) {
|
|
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]Context) {
|
|
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
|
|
}
|
|
|
|
// 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)
|
|
clone.attachments = make([]*Attachment, len(scope.attachments))
|
|
copy(clone.attachments, scope.attachments)
|
|
for key, value := range scope.tags {
|
|
clone.tags[key] = value
|
|
}
|
|
for key, value := range scope.contexts {
|
|
clone.contexts[key] = cloneContext(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.request = scope.request
|
|
clone.requestBody = scope.requestBody
|
|
clone.eventProcessors = scope.eventProcessors
|
|
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 {
|
|
event.Breadcrumbs = append(event.Breadcrumbs, scope.breadcrumbs...)
|
|
}
|
|
|
|
if len(scope.attachments) > 0 {
|
|
event.Attachments = append(event.Attachments, scope.attachments...)
|
|
}
|
|
|
|
if len(scope.tags) > 0 {
|
|
if event.Tags == nil {
|
|
event.Tags = make(map[string]string, len(scope.tags))
|
|
}
|
|
|
|
for key, value := range scope.tags {
|
|
event.Tags[key] = value
|
|
}
|
|
}
|
|
|
|
if len(scope.contexts) > 0 {
|
|
if event.Contexts == nil {
|
|
event.Contexts = make(map[string]Context)
|
|
}
|
|
|
|
for key, value := range scope.contexts {
|
|
if key == "trace" && event.Type == transactionType {
|
|
// Do not override trace context of
|
|
// transactions, otherwise it breaks the
|
|
// transaction event representation.
|
|
// For error events, the trace context is used
|
|
// to link errors and traces/spans in Sentry.
|
|
continue
|
|
}
|
|
|
|
// Ensure we are not overwriting event fields
|
|
if _, ok := event.Contexts[key]; !ok {
|
|
event.Contexts[key] = cloneContext(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(scope.extra) > 0 {
|
|
if event.Extra == nil {
|
|
event.Extra = make(map[string]interface{}, len(scope.extra))
|
|
}
|
|
|
|
for key, value := range scope.extra {
|
|
event.Extra[key] = value
|
|
}
|
|
}
|
|
|
|
if event.User.IsEmpty() {
|
|
event.User = scope.user
|
|
}
|
|
|
|
if len(event.Fingerprint) == 0 {
|
|
event.Fingerprint = append(event.Fingerprint, scope.fingerprint...)
|
|
}
|
|
|
|
if scope.level != "" {
|
|
event.Level = scope.level
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// cloneContext returns a new context with keys and values copied from the passed one.
|
|
//
|
|
// Note: a new Context (map) is returned, but the function does NOT do
|
|
// a proper deep copy: if some context values are pointer types (e.g. maps),
|
|
// they won't be properly copied.
|
|
func cloneContext(c Context) Context {
|
|
res := Context{}
|
|
for k, v := range c {
|
|
res[k] = v
|
|
}
|
|
return res
|
|
}
|