Upgrade versions of vendored libs
This commit is contained in:
13
vendor/github.com/getsentry/sentry-go/.codecov.yml
generated
vendored
Normal file
13
vendor/github.com/getsentry/sentry-go/.codecov.yml
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
codecov:
|
||||
# across
|
||||
notify:
|
||||
# Do not notify until at least this number of reports have been uploaded
|
||||
# from the CI pipeline. We normally have more than that number, but 6
|
||||
# should be enough to get a first notification.
|
||||
after_n_builds: 6
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
# Do not fail the commit status if the coverage was reduced up to this value
|
||||
threshold: 0.5%
|
||||
9
vendor/github.com/getsentry/sentry-go/.craft.yml
generated
vendored
9
vendor/github.com/getsentry/sentry-go/.craft.yml
generated
vendored
@@ -1,12 +1,13 @@
|
||||
minVersion: 0.23.1
|
||||
preReleaseCommand: bash scripts/craft-pre-release.sh
|
||||
changelogPolicy: auto
|
||||
minVersion: 0.35.0
|
||||
changelogPolicy: simple
|
||||
artifactProvider:
|
||||
name: none
|
||||
targets:
|
||||
- name: github
|
||||
includeNames: /none/
|
||||
tagPrefix: v
|
||||
- name: github
|
||||
tagPrefix: otel/v
|
||||
tagOnly: true
|
||||
- name: registry
|
||||
sdks:
|
||||
github:getsentry/sentry-go:
|
||||
|
||||
4
vendor/github.com/getsentry/sentry-go/.gitignore
generated
vendored
4
vendor/github.com/getsentry/sentry-go/.gitignore
generated
vendored
@@ -1,4 +1,8 @@
|
||||
# Code coverage artifacts
|
||||
coverage.txt
|
||||
coverage.out
|
||||
coverage.html
|
||||
.coverage/
|
||||
|
||||
# Just my personal way of tracking stuff — Kamil
|
||||
FIXME.md
|
||||
|
||||
1
vendor/github.com/getsentry/sentry-go/.golangci.yml
generated
vendored
1
vendor/github.com/getsentry/sentry-go/.golangci.yml
generated
vendored
@@ -35,6 +35,7 @@ issues:
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- goconst
|
||||
- prealloc
|
||||
- path: _test\.go
|
||||
text: "G306:"
|
||||
|
||||
269
vendor/github.com/getsentry/sentry-go/CHANGELOG.md
generated
vendored
269
vendor/github.com/getsentry/sentry-go/CHANGELOG.md
generated
vendored
@@ -1,17 +1,276 @@
|
||||
# Changelog
|
||||
|
||||
## 0.21.0
|
||||
|
||||
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.21.0.
|
||||
|
||||
Note: this release includes one **breaking change** and some **deprecations**, which are listed below.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
**This change does not apply if you use [https://sentry.io](https://sentry.io)**
|
||||
|
||||
- Remove support for the `/store` endpoint ([#631](https://github.com/getsentry/sentry-go/pull/631))
|
||||
- This change requires a self-hosted version of Sentry 20.6.0 or higher. If you are using a version of [self-hosted Sentry](https://develop.sentry.dev/self-hosted/) (aka *on-premise*) older than 20.6.0, then you will need to [upgrade](https://develop.sentry.dev/self-hosted/releases/) your instance.
|
||||
|
||||
### Features
|
||||
|
||||
- Rename four span option functions ([#611](https://github.com/getsentry/sentry-go/pull/611), [#624](https://github.com/getsentry/sentry-go/pull/624))
|
||||
- `TransctionSource` -> `WithTransactionSource`
|
||||
- `SpanSampled` -> `WithSpanSampled`
|
||||
- `OpName` -> `WithOpName`
|
||||
- `TransactionName` -> `WithTransactionName`
|
||||
- Old functions `TransctionSource`, `SpanSampled`, `OpName`, and `TransactionName` are still available but are now **deprecated** and will be removed in a future release.
|
||||
- Make `client.EventFromMessage` and `client.EventFromException` methods public ([#607](https://github.com/getsentry/sentry-go/pull/607))
|
||||
- Add `client.SetException` method ([#607](https://github.com/getsentry/sentry-go/pull/607))
|
||||
- This allows to set or add errors to an existing `Event`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Protect from panics while doing concurrent reads/writes to Span data fields ([#609](https://github.com/getsentry/sentry-go/pull/609))
|
||||
- [otel] Improve detection of Sentry-related spans ([#632](https://github.com/getsentry/sentry-go/pull/632), [#636](https://github.com/getsentry/sentry-go/pull/636))
|
||||
- Fixes cases when HTTP spans containing requests to Sentry were captured by Sentry ([#627](https://github.com/getsentry/sentry-go/issues/627))
|
||||
|
||||
### Misc
|
||||
|
||||
- Drop testing in (legacy) GOPATH mode ([#618](https://github.com/getsentry/sentry-go/pull/618))
|
||||
- Remove outdated documentation from https://pkg.go.dev/github.com/getsentry/sentry-go ([#623](https://github.com/getsentry/sentry-go/pull/623))
|
||||
|
||||
## 0.20.0
|
||||
|
||||
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.20.0.
|
||||
|
||||
Note: this release has some **breaking changes**, which are listed below.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Remove the following methods: `Scope.SetTransaction()`, `Scope.Transaction()` ([#605](https://github.com/getsentry/sentry-go/pull/605))
|
||||
|
||||
Span.Name should be used instead to access the transaction's name.
|
||||
|
||||
For example, the following [`TracesSampler`](https://docs.sentry.io/platforms/go/configuration/sampling/#setting-a-sampling-function) function should be now written as follows:
|
||||
|
||||
**Before:**
|
||||
```go
|
||||
TracesSampler: func(ctx sentry.SamplingContext) float64 {
|
||||
hub := sentry.GetHubFromContext(ctx.Span.Context())
|
||||
if hub.Scope().Transaction() == "GET /health" {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
},
|
||||
```
|
||||
|
||||
**After:**
|
||||
```go
|
||||
TracesSampler: func(ctx sentry.SamplingContext) float64 {
|
||||
if ctx.Span.Name == "GET /health" {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
},
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
- Add `Span.SetContext()` method ([#599](https://github.com/getsentry/sentry-go/pull/599/))
|
||||
- It is recommended to use it instead of `hub.Scope().SetContext` when setting or updating context on transactions.
|
||||
- Add `DebugMeta` interface to `Event` and extend `Frame` structure with more fields ([#606](https://github.com/getsentry/sentry-go/pull/606))
|
||||
- More about DebugMeta interface [here](https://develop.sentry.dev/sdk/event-payloads/debugmeta/).
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [otel] Fix missing OpenTelemetry context on some events ([#599](https://github.com/getsentry/sentry-go/pull/599), [#605](https://github.com/getsentry/sentry-go/pull/605))
|
||||
- Fixes ([#596](https://github.com/getsentry/sentry-go/issues/596)).
|
||||
- [otel] Better handling for HTTP span attributes ([#610](https://github.com/getsentry/sentry-go/pull/610))
|
||||
|
||||
### Misc
|
||||
|
||||
- Bump minimum versions: `github.com/kataras/iris/v12` to 12.2.0, `github.com/labstack/echo/v4` to v4.10.0 ([#595](https://github.com/getsentry/sentry-go/pull/595))
|
||||
- Resolves [GO-2022-1144 / CVE-2022-41717](https://deps.dev/advisory/osv/GO-2022-1144), [GO-2023-1495 / CVE-2022-41721](https://deps.dev/advisory/osv/GO-2023-1495), [GO-2022-1059 / CVE-2022-32149](https://deps.dev/advisory/osv/GO-2022-1059).
|
||||
- Bump `google.golang.org/protobuf` minimum required version to 1.29.1 ([#604](https://github.com/getsentry/sentry-go/pull/604))
|
||||
- This fixes a potential denial of service issue ([CVE-2023-24535](https://github.com/advisories/GHSA-hw7c-3rfg-p46j)).
|
||||
- Exclude the `otel` module when building in GOPATH mode ([#615](https://github.com/getsentry/sentry-go/pull/615))
|
||||
|
||||
## 0.19.0
|
||||
|
||||
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.19.0.
|
||||
|
||||
### Features
|
||||
|
||||
- Add support for exception mechanism metadata ([#564](https://github.com/getsentry/sentry-go/pull/564/))
|
||||
- More about exception mechanisms [here](https://develop.sentry.dev/sdk/event-payloads/exception/#exception-mechanism).
|
||||
|
||||
### Bug Fixes
|
||||
- [otel] Use the correct "trace" context when sending a Sentry error ([#580](https://github.com/getsentry/sentry-go/pull/580/))
|
||||
|
||||
|
||||
### Misc
|
||||
- Drop support for Go 1.17, add support for Go 1.20 ([#563](https://github.com/getsentry/sentry-go/pull/563/))
|
||||
- According to our policy, we're officially supporting the last three minor releases of Go.
|
||||
- Switch repository license to MIT ([#583](https://github.com/getsentry/sentry-go/pull/583/))
|
||||
- More about Sentry licensing [here](https://open.sentry.io/licensing/).
|
||||
- Bump `golang.org/x/text` minimum required version to 0.3.8 ([#586](https://github.com/getsentry/sentry-go/pull/586))
|
||||
- This fixes [CVE-2022-32149](https://github.com/advisories/GHSA-69ch-w2m2-3vjp) vulnerability.
|
||||
|
||||
## 0.18.0
|
||||
|
||||
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.18.0.
|
||||
This release contains initial support for [OpenTelemetry](https://opentelemetry.io/) and various other bug fixes and improvements.
|
||||
|
||||
**Note**: This is the last release supporting Go 1.17.
|
||||
|
||||
### Features
|
||||
|
||||
- Initial support for [OpenTelemetry](https://opentelemetry.io/).
|
||||
You can now send all your OpenTelemetry spans to Sentry.
|
||||
|
||||
Install the `otel` module
|
||||
|
||||
```bash
|
||||
go get github.com/getsentry/sentry-go \
|
||||
github.com/getsentry/sentry-go/otel
|
||||
```
|
||||
|
||||
Configure the Sentry and OpenTelemetry SDKs
|
||||
|
||||
```go
|
||||
import (
|
||||
"go.opentelemetry.io/otel"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/getsentry/sentry-go/otel"
|
||||
// ...
|
||||
)
|
||||
|
||||
// Initlaize the Sentry SDK
|
||||
sentry.Init(sentry.ClientOptions{
|
||||
Dsn: "__DSN__",
|
||||
EnableTracing: true,
|
||||
TracesSampleRate: 1.0,
|
||||
})
|
||||
|
||||
// Set up the Sentry span processor
|
||||
tp := sdktrace.NewTracerProvider(
|
||||
sdktrace.WithSpanProcessor(sentryotel.NewSentrySpanProcessor()),
|
||||
// ...
|
||||
)
|
||||
otel.SetTracerProvider(tp)
|
||||
|
||||
// Set up the Sentry propagator
|
||||
otel.SetTextMapPropagator(sentryotel.NewSentryPropagator())
|
||||
```
|
||||
|
||||
You can read more about using OpenTelemetry with Sentry in our [docs](https://docs.sentry.io/platforms/go/performance/instrumentation/opentelemetry/).
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Do not freeze the Dynamic Sampling Context when no Sentry values are present in the baggage header ([#532](https://github.com/getsentry/sentry-go/pull/532))
|
||||
- Create a frozen Dynamic Sampling Context when calling `span.ToBaggage()` ([#566](https://github.com/getsentry/sentry-go/pull/566))
|
||||
- Fix baggage parsing and encoding in vendored otel package ([#568](https://github.com/getsentry/sentry-go/pull/568))
|
||||
|
||||
### Misc
|
||||
|
||||
- Add `Span.SetDynamicSamplingContext()` ([#539](https://github.com/getsentry/sentry-go/pull/539/))
|
||||
- Add various getters for `Dsn` ([#540](https://github.com/getsentry/sentry-go/pull/540))
|
||||
- Add `SpanOption::SpanSampled` ([#546](https://github.com/getsentry/sentry-go/pull/546))
|
||||
- Add `Span.SetData()` ([#542](https://github.com/getsentry/sentry-go/pull/542))
|
||||
- Add `Span.IsTransaction()` ([#543](https://github.com/getsentry/sentry-go/pull/543))
|
||||
- Add `Span.GetTransaction()` method ([#558](https://github.com/getsentry/sentry-go/pull/558))
|
||||
|
||||
## 0.17.0
|
||||
|
||||
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.17.0.
|
||||
This release contains a new `BeforeSendTransaction` hook option and corrects two regressions introduced in `0.16.0`.
|
||||
|
||||
### Features
|
||||
|
||||
- Add `BeforeSendTransaction` hook to `ClientOptions` ([#517](https://github.com/getsentry/sentry-go/pull/517))
|
||||
- Here's [an example](https://github.com/getsentry/sentry-go/blob/master/_examples/http/main.go#L56-L66) of how BeforeSendTransaction can be used to modify or drop transaction events.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Do not crash in Span.Finish() when the Client is empty [#520](https://github.com/getsentry/sentry-go/pull/520)
|
||||
- Fixes [#518](https://github.com/getsentry/sentry-go/issues/518)
|
||||
- Attach non-PII/non-sensitive request headers to events when `ClientOptions.SendDefaultPii` is set to `false` ([#524](https://github.com/getsentry/sentry-go/pull/524))
|
||||
- Fixes [#523](https://github.com/getsentry/sentry-go/issues/523)
|
||||
|
||||
### Misc
|
||||
|
||||
- Clarify how to handle logrus.Fatalf events ([#501](https://github.com/getsentry/sentry-go/pull/501/))
|
||||
- Rename the `examples` directory to `_examples` ([#521](https://github.com/getsentry/sentry-go/pull/521))
|
||||
- This removes an indirect dependency to `github.com/golang-jwt/jwt`
|
||||
|
||||
## 0.16.0
|
||||
|
||||
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.16.0.
|
||||
Due to ongoing work towards a stable API for `v1.0.0`, we sadly had to include **two breaking changes** in this release.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Add `EnableTracing`, a boolean option flag to enable performance monitoring (`false` by default).
|
||||
- If you're using `TracesSampleRate` or `TracesSampler`, this option is **required** to enable performance monitoring.
|
||||
|
||||
```go
|
||||
sentry.Init(sentry.ClientOptions{
|
||||
EnableTracing: true,
|
||||
TracesSampleRate: 1.0,
|
||||
})
|
||||
```
|
||||
- Unify TracesSampler [#498](https://github.com/getsentry/sentry-go/pull/498)
|
||||
- `TracesSampler` was changed to a callback that must return a `float64` between `0.0` and `1.0`.
|
||||
|
||||
For example, you can apply a sample rate of `1.0` (100%) to all `/api` transactions, and a sample rate of `0.5` (50%) to all other transactions.
|
||||
You can read more about this in our [SDK docs](https://docs.sentry.io/platforms/go/configuration/filtering/#using-sampling-to-filter-transaction-events).
|
||||
|
||||
```go
|
||||
sentry.Init(sentry.ClientOptions{
|
||||
TracesSampler: sentry.TracesSampler(func(ctx sentry.SamplingContext) float64 {
|
||||
hub := sentry.GetHubFromContext(ctx.Span.Context())
|
||||
name := hub.Scope().Transaction()
|
||||
|
||||
if strings.HasPrefix(name, "GET /api") {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
return 0.5
|
||||
}),
|
||||
}
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
- Send errors logged with [Logrus](https://github.com/sirupsen/logrus) to Sentry.
|
||||
- Have a look at our [logrus examples](https://github.com/getsentry/sentry-go/blob/master/_examples/logrus/main.go) on how to use the integration.
|
||||
- Add support for Dynamic Sampling [#491](https://github.com/getsentry/sentry-go/pull/491)
|
||||
- You can read more about Dynamic Sampling in our [product docs](https://docs.sentry.io/product/data-management-settings/dynamic-sampling/).
|
||||
- Add detailed logging about the reason transactions are being dropped.
|
||||
- You can enable SDK logging via `sentry.ClientOptions.Debug: true`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Do not clone the hub when calling `StartTransaction` [#505](https://github.com/getsentry/sentry-go/pull/505)
|
||||
- Fixes [#502](https://github.com/getsentry/sentry-go/issues/502)
|
||||
|
||||
## 0.15.0
|
||||
|
||||
- fix: Scope values should not override Event values (#446)
|
||||
- feat: Make maximum amount of spans configurable (#460)
|
||||
- feat: Add a method to start a transaction (#482)
|
||||
- feat: Extend User interface by adding Data, Name and Segment (#483)
|
||||
- feat: Add ClientOptions.SendDefaultPII (#485)
|
||||
|
||||
## 0.14.0
|
||||
|
||||
- feat: Add function to continue from trace string (#434)
|
||||
- feat: Add `max-depth` options (#428)
|
||||
- ref: Use a `Context` type mapping to a `map[string]interface{}` for all event contexts (#444)
|
||||
- *[breaking]* ref: Use a `Context` type mapping to a `map[string]interface{}` for all event contexts (#444)
|
||||
- *[breaking]* ref: Replace deprecated `ioutil` pkg with `os` & `io` (#454)
|
||||
- ref: Optimize `stacktrace.go` from size and speed (#467)
|
||||
- ref: Replace deprecated `ioutil` pkg with `os` & `io` (#454)
|
||||
- ci: Test against `go1.19` and `go1.18`, drop `go1.16` and `go1.15` support (#432, #477)
|
||||
- deps: Dependency update to fix CVEs (#462, #464, #477)
|
||||
|
||||
_NOTE:_
|
||||
This version drops support for Go 1.16 and Go 1.15. The currently supported Go versions are the last 3 stable releases: 1.19, 1.18 and 1.17.
|
||||
_NOTE:_ This version drops support for Go 1.16 and Go 1.15. The currently supported Go versions are the last 3 stable releases: 1.19, 1.18 and 1.17.
|
||||
|
||||
## v0.13.0
|
||||
|
||||
@@ -70,7 +329,7 @@ There are no breaking changes and upgrading should be a smooth experience for al
|
||||
_NOTE:_
|
||||
This version introduces support for [Sentry's Performance Monitoring](https://docs.sentry.io/platforms/go/performance/).
|
||||
The new tracing capabilities are beta, and we plan to expand them on future versions. Feedback is welcome, please open new issues on GitHub.
|
||||
The `sentryhttp` package got better API docs, an [updated usage example](https://github.com/getsentry/sentry-go/tree/master/example/http) and support for creating automatic transactions as part of Performance Monitoring.
|
||||
The `sentryhttp` package got better API docs, an [updated usage example](https://github.com/getsentry/sentry-go/tree/master/_examples/http) and support for creating automatic transactions as part of Performance Monitoring.
|
||||
|
||||
## v0.8.0
|
||||
|
||||
|
||||
2
vendor/github.com/getsentry/sentry-go/CONTRIBUTING.md
generated
vendored
2
vendor/github.com/getsentry/sentry-go/CONTRIBUTING.md
generated
vendored
@@ -72,6 +72,8 @@ $ go test -race -coverprofile=coverage.txt -covermode=atomic && go tool cover -h
|
||||
|
||||
## Linting
|
||||
|
||||
Lint with [`golangci-lint`](https://github.com/golangci/golangci-lint):
|
||||
|
||||
```console
|
||||
$ golangci-lint run
|
||||
```
|
||||
|
||||
24
vendor/github.com/getsentry/sentry-go/LICENSE
generated
vendored
24
vendor/github.com/getsentry/sentry-go/LICENSE
generated
vendored
@@ -1,9 +1,21 @@
|
||||
Copyright (c) 2019 Sentry (https://sentry.io) and individual contributors.
|
||||
All rights reserved.
|
||||
MIT License
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
Copyright (c) 2019 Functional Software, Inc. dba Sentry
|
||||
|
||||
* 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.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
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.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
83
vendor/github.com/getsentry/sentry-go/Makefile
generated
vendored
Normal file
83
vendor/github.com/getsentry/sentry-go/Makefile
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
MKFILE_DIR := $(dir $(MKFILE_PATH))
|
||||
ALL_GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort)
|
||||
GO = go
|
||||
TIMEOUT = 300
|
||||
|
||||
# Parse Makefile and display the help
|
||||
help: ## Show help
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
.PHONY: help
|
||||
|
||||
build: ## Build everything
|
||||
for dir in $(ALL_GO_MOD_DIRS); do \
|
||||
cd "$${dir}"; \
|
||||
echo ">>> Running 'go build' for module: $${dir}"; \
|
||||
go build ./...; \
|
||||
done;
|
||||
.PHONY: build
|
||||
|
||||
### Tests (inspired by https://github.com/open-telemetry/opentelemetry-go/blob/main/Makefile)
|
||||
TEST_TARGETS := test-short test-verbose test-race
|
||||
test-race: ARGS=-race
|
||||
test-short: ARGS=-short
|
||||
test-verbose: ARGS=-v -race
|
||||
$(TEST_TARGETS): test
|
||||
test: $(ALL_GO_MOD_DIRS:%=test/%) ## Run tests
|
||||
test/%: DIR=$*
|
||||
test/%:
|
||||
@echo ">>> Running tests for module: $(DIR)"
|
||||
@# We use '-count=1' to disable test caching.
|
||||
(cd $(DIR) && $(GO) test -count=1 -timeout $(TIMEOUT)s $(ARGS) ./...)
|
||||
.PHONY: $(TEST_TARGETS) test
|
||||
|
||||
# Coverage
|
||||
COVERAGE_MODE = atomic
|
||||
COVERAGE_PROFILE = coverage.out
|
||||
COVERAGE_REPORT_DIR = .coverage
|
||||
COVERAGE_REPORT_DIR_ABS = "$(MKFILE_DIR)/$(COVERAGE_REPORT_DIR)"
|
||||
$(COVERAGE_REPORT_DIR):
|
||||
mkdir -p $(COVERAGE_REPORT_DIR)
|
||||
clean-report-dir: $(COVERAGE_REPORT_DIR)
|
||||
test $(COVERAGE_REPORT_DIR) && rm -f $(COVERAGE_REPORT_DIR)/*
|
||||
test-coverage: $(COVERAGE_REPORT_DIR) clean-report-dir ## Test with coverage enabled
|
||||
set -e ; \
|
||||
for dir in $(ALL_GO_MOD_DIRS); do \
|
||||
echo ">>> Running tests with coverage for module: $${dir}"; \
|
||||
DIR_ABS=$$(python -c 'import os, sys; print(os.path.realpath(sys.argv[1]))' $${dir}) ; \
|
||||
REPORT_NAME=$$(basename $${DIR_ABS}); \
|
||||
(cd "$${dir}" && \
|
||||
$(GO) test -count=1 -coverpkg=./... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" ./... && \
|
||||
cp $(COVERAGE_PROFILE) "$(COVERAGE_REPORT_DIR_ABS)/$${REPORT_NAME}_$(COVERAGE_PROFILE)" && \
|
||||
$(GO) tool cover -html=$(COVERAGE_PROFILE) -o coverage.html); \
|
||||
done;
|
||||
.PHONY: test-coverage clean-report-dir
|
||||
|
||||
mod-tidy: ## Check go.mod tidiness
|
||||
set -e ; \
|
||||
for dir in $(ALL_GO_MOD_DIRS); do \
|
||||
cd "$${dir}"; \
|
||||
echo ">>> Running 'go mod tidy' for module: $${dir}"; \
|
||||
go mod tidy -go=1.18 -compat=1.18; \
|
||||
done; \
|
||||
git diff --exit-code;
|
||||
.PHONY: mod-tidy
|
||||
|
||||
vet: ## Run "go vet"
|
||||
set -e ; \
|
||||
for dir in $(ALL_GO_MOD_DIRS); do \
|
||||
cd "$${dir}"; \
|
||||
echo ">>> Running 'go vet' for module: $${dir}"; \
|
||||
go vet ./...; \
|
||||
done;
|
||||
.PHONY: vet
|
||||
|
||||
lint: ## Lint (using "golangci-lint")
|
||||
golangci-lint run
|
||||
.PHONY: lint
|
||||
|
||||
fmt: ## Format all Go files
|
||||
gofmt -l -w -s .
|
||||
.PHONY: fmt
|
||||
12
vendor/github.com/getsentry/sentry-go/README.md
generated
vendored
12
vendor/github.com/getsentry/sentry-go/README.md
generated
vendored
@@ -29,7 +29,7 @@ The only requirement is a Go compiler.
|
||||
|
||||
We verify this package against the 3 most recent releases of Go. Those are the
|
||||
supported versions. The exact versions are defined in
|
||||
[`GitHub workflow`](.github/workflows/ci.yml).
|
||||
[`GitHub workflow`](.github/workflows/test.yml).
|
||||
|
||||
In addition, we run tests against the current master branch of the Go toolchain,
|
||||
though support for this configuration is best-effort.
|
||||
@@ -42,7 +42,7 @@ though support for this configuration is best-effort.
|
||||
$ go get github.com/getsentry/sentry-go@latest
|
||||
```
|
||||
|
||||
Check out the [list of released versions](https://pkg.go.dev/github.com/getsentry/sentry-go?tab=versions).
|
||||
Check out the [list of released versions](https://github.com/getsentry/sentry-go/releases).
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -62,9 +62,9 @@ More on this in the [Configuration section of the official Sentry Go SDK documen
|
||||
|
||||
The SDK supports reporting errors and tracking application performance.
|
||||
|
||||
To get started, have a look at one of our [examples](example/):
|
||||
- [Basic error instrumentation](example/basic/main.go)
|
||||
- [Error and tracing for HTTP servers](example/http/main.go)
|
||||
To get started, have a look at one of our [examples](_examples/):
|
||||
- [Basic error instrumentation](_examples/basic/main.go)
|
||||
- [Error and tracing for HTTP servers](_examples/http/main.go)
|
||||
|
||||
We also provide a [complete API reference](https://pkg.go.dev/github.com/getsentry/sentry-go).
|
||||
|
||||
@@ -96,7 +96,7 @@ checkout the official documentation:
|
||||
## License
|
||||
|
||||
Licensed under
|
||||
[The 2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause), see
|
||||
[The MIT License](https://opensource.org/licenses/mit/), see
|
||||
[`LICENSE`](LICENSE).
|
||||
|
||||
## Community
|
||||
|
||||
103
vendor/github.com/getsentry/sentry-go/client.go
generated
vendored
103
vendor/github.com/getsentry/sentry-go/client.go
generated
vendored
@@ -3,14 +3,12 @@ package sentry
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -28,6 +26,11 @@ import (
|
||||
// stack trace is often the most useful information.
|
||||
const maxErrorDepth = 10
|
||||
|
||||
// defaultMaxSpans limits the default number of recorded spans per transaction. The limit is
|
||||
// meant to bound memory usage and prevent too large transaction events that
|
||||
// would be rejected by Sentry.
|
||||
const defaultMaxSpans = 1000
|
||||
|
||||
// hostname is the host name reported by the kernel. It is precomputed once to
|
||||
// avoid syscalls when capturing events.
|
||||
//
|
||||
@@ -121,6 +124,8 @@ type ClientOptions struct {
|
||||
// 0.0 is treated as if it was 1.0. To drop all events, set the DSN to the
|
||||
// empty string.
|
||||
SampleRate float64
|
||||
// Enable performance tracing.
|
||||
EnableTracing bool
|
||||
// The sample rate for sampling traces in the range [0.0, 1.0].
|
||||
TracesSampleRate float64
|
||||
// Used to customize the sampling of traces, overrides TracesSampleRate.
|
||||
@@ -129,10 +134,15 @@ type ClientOptions struct {
|
||||
// and if applicable, caught errors type and value.
|
||||
// If the match is found, then a whole event will be dropped.
|
||||
IgnoreErrors []string
|
||||
// If this flag is enabled, certain personally identifiable information (PII) is added by active integrations.
|
||||
// By default, no such data is sent.
|
||||
SendDefaultPII bool
|
||||
// BeforeSend is called before error events are sent to Sentry.
|
||||
// Use it to mutate the event or return nil to discard the event.
|
||||
// See EventProcessor if you need to mutate transactions.
|
||||
BeforeSend func(event *Event, hint *EventHint) *Event
|
||||
// BeforeSendTransaction is called before transaction events are sent to Sentry.
|
||||
// Use it to mutate the transaction or return nil to discard the transaction.
|
||||
BeforeSendTransaction func(event *Event, hint *EventHint) *Event
|
||||
// Before breadcrumb add callback.
|
||||
BeforeBreadcrumb func(breadcrumb *Breadcrumb, hint *BreadcrumbHint) *Breadcrumb
|
||||
// Integrations to be installed on the current Client, receives default
|
||||
@@ -175,6 +185,11 @@ type ClientOptions struct {
|
||||
// Maximum number of breadcrumbs
|
||||
// when MaxBreadcrumbs is negative then ignore breadcrumbs.
|
||||
MaxBreadcrumbs int
|
||||
// Maximum number of spans.
|
||||
//
|
||||
// See https://develop.sentry.dev/sdk/envelopes/#size-limits for size limits
|
||||
// applied during event ingestion. Events that exceed these limits might get dropped.
|
||||
MaxSpans int
|
||||
// An optional pointer to http.Client that will be used with a default
|
||||
// HTTPTransport. Using your own client will make HTTPTransport, HTTPProxy,
|
||||
// HTTPSProxy and CaCerts options ignored.
|
||||
@@ -222,10 +237,6 @@ type Client struct {
|
||||
// single goroutine) or hub methods (for concurrent programs, for example web
|
||||
// servers).
|
||||
func NewClient(options ClientOptions) (*Client, error) {
|
||||
if options.TracesSampleRate != 0.0 && options.TracesSampler != nil {
|
||||
return nil, errors.New("TracesSampleRate and TracesSampler are mutually exclusive")
|
||||
}
|
||||
|
||||
if options.Debug {
|
||||
debugWriter := options.DebugWriter
|
||||
if debugWriter == nil {
|
||||
@@ -250,6 +261,10 @@ func NewClient(options ClientOptions) (*Client, error) {
|
||||
options.MaxErrorDepth = maxErrorDepth
|
||||
}
|
||||
|
||||
if options.MaxSpans == 0 {
|
||||
options.MaxSpans = defaultMaxSpans
|
||||
}
|
||||
|
||||
// SENTRYGODEBUG is a comma-separated list of key=value pairs (similar
|
||||
// to GODEBUG). It is not a supported feature: recognized debug options
|
||||
// may change any time.
|
||||
@@ -306,7 +321,7 @@ func (client *Client) setupTransport() {
|
||||
// accommodate more concurrent events.
|
||||
// TODO(tracing): consider using separate buffers per
|
||||
// event type.
|
||||
if opts.TracesSampleRate != 0 || opts.TracesSampler != nil {
|
||||
if opts.EnableTracing {
|
||||
httpTransport.BufferSize = 1000
|
||||
}
|
||||
transport = httpTransport
|
||||
@@ -362,13 +377,13 @@ func (client Client) Options() ClientOptions {
|
||||
|
||||
// CaptureMessage captures an arbitrary message.
|
||||
func (client *Client) CaptureMessage(message string, hint *EventHint, scope EventModifier) *EventID {
|
||||
event := client.eventFromMessage(message, LevelInfo)
|
||||
event := client.EventFromMessage(message, LevelInfo)
|
||||
return client.CaptureEvent(event, hint, scope)
|
||||
}
|
||||
|
||||
// CaptureException captures an error.
|
||||
func (client *Client) CaptureException(exception error, hint *EventHint, scope EventModifier) *EventID {
|
||||
event := client.eventFromException(exception, LevelError)
|
||||
event := client.EventFromException(exception, LevelError)
|
||||
return client.CaptureEvent(event, hint, scope)
|
||||
}
|
||||
|
||||
@@ -423,11 +438,11 @@ func (client *Client) RecoverWithContext(
|
||||
var event *Event
|
||||
switch err := err.(type) {
|
||||
case error:
|
||||
event = client.eventFromException(err, LevelFatal)
|
||||
event = client.EventFromException(err, LevelFatal)
|
||||
case string:
|
||||
event = client.eventFromMessage(err, LevelFatal)
|
||||
event = client.EventFromMessage(err, LevelFatal)
|
||||
default:
|
||||
event = client.eventFromMessage(fmt.Sprintf("%#v", err), LevelFatal)
|
||||
event = client.EventFromMessage(fmt.Sprintf("%#v", err), LevelFatal)
|
||||
}
|
||||
return client.CaptureEvent(event, hint, scope)
|
||||
}
|
||||
@@ -447,10 +462,11 @@ func (client *Client) Flush(timeout time.Duration) bool {
|
||||
return client.Transport.Flush(timeout)
|
||||
}
|
||||
|
||||
func (client *Client) eventFromMessage(message string, level Level) *Event {
|
||||
// EventFromMessage creates an event from the given message string.
|
||||
func (client *Client) EventFromMessage(message string, level Level) *Event {
|
||||
if message == "" {
|
||||
err := usageError{fmt.Errorf("%s called with empty message", callerFunctionName())}
|
||||
return client.eventFromException(err, level)
|
||||
return client.EventFromException(err, level)
|
||||
}
|
||||
event := NewEvent()
|
||||
event.Level = level
|
||||
@@ -467,41 +483,17 @@ func (client *Client) eventFromMessage(message string, level Level) *Event {
|
||||
return event
|
||||
}
|
||||
|
||||
func (client *Client) eventFromException(exception error, level Level) *Event {
|
||||
// EventFromException creates a new Sentry event from the given `error` instance.
|
||||
func (client *Client) EventFromException(exception error, level Level) *Event {
|
||||
event := NewEvent()
|
||||
event.Level = level
|
||||
|
||||
err := exception
|
||||
if err == nil {
|
||||
err = usageError{fmt.Errorf("%s called with nil error", callerFunctionName())}
|
||||
}
|
||||
|
||||
event := NewEvent()
|
||||
event.Level = level
|
||||
|
||||
for i := 0; i < client.options.MaxErrorDepth && err != nil; i++ {
|
||||
event.Exception = append(event.Exception, Exception{
|
||||
Value: err.Error(),
|
||||
Type: reflect.TypeOf(err).String(),
|
||||
Stacktrace: ExtractStacktrace(err),
|
||||
})
|
||||
switch previous := err.(type) {
|
||||
case interface{ Unwrap() error }:
|
||||
err = previous.Unwrap()
|
||||
case interface{ Cause() error }:
|
||||
err = previous.Cause()
|
||||
default:
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Add a trace of the current stack to the most recent error in a chain if
|
||||
// it doesn't have a stack trace yet.
|
||||
// We only add to the most recent error to avoid duplication and because the
|
||||
// current stack is most likely unrelated to errors deeper in the chain.
|
||||
if event.Exception[0].Stacktrace == nil {
|
||||
event.Exception[0].Stacktrace = NewStacktrace()
|
||||
}
|
||||
|
||||
// event.Exception should be sorted such that the most recent error is last.
|
||||
reverse(event.Exception)
|
||||
event.SetException(err, client.options.MaxErrorDepth)
|
||||
|
||||
return event
|
||||
}
|
||||
@@ -556,11 +548,18 @@ func (client *Client) processEvent(event *Event, hint *EventHint, scope EventMod
|
||||
return nil
|
||||
}
|
||||
|
||||
// As per spec, transactions do not go through BeforeSend.
|
||||
if event.Type != transactionType && options.BeforeSend != nil {
|
||||
if hint == nil {
|
||||
hint = &EventHint{}
|
||||
// Apply beforeSend* processors
|
||||
if hint == nil {
|
||||
hint = &EventHint{}
|
||||
}
|
||||
if event.Type == transactionType && options.BeforeSendTransaction != nil {
|
||||
// Transaction events
|
||||
if event = options.BeforeSendTransaction(event, hint); event == nil {
|
||||
Logger.Println("Transaction dropped due to BeforeSendTransaction callback.")
|
||||
return nil
|
||||
}
|
||||
} else if event.Type != transactionType && options.BeforeSend != nil {
|
||||
// All other events
|
||||
if event = options.BeforeSend(event, hint); event == nil {
|
||||
Logger.Println("Event dropped due to BeforeSend callback.")
|
||||
return nil
|
||||
@@ -607,12 +606,12 @@ func (client *Client) prepareEvent(event *Event, hint *EventHint, scope EventMod
|
||||
|
||||
event.Platform = "go"
|
||||
event.Sdk = SdkInfo{
|
||||
Name: "sentry.go",
|
||||
Version: Version,
|
||||
Name: SDKIdentifier,
|
||||
Version: SDKVersion,
|
||||
Integrations: client.listIntegrations(),
|
||||
Packages: []SdkPackage{{
|
||||
Name: "sentry-go",
|
||||
Version: Version,
|
||||
Version: SDKVersion,
|
||||
}},
|
||||
}
|
||||
|
||||
|
||||
61
vendor/github.com/getsentry/sentry-go/doc.go
generated
vendored
61
vendor/github.com/getsentry/sentry-go/doc.go
generated
vendored
@@ -1,63 +1,6 @@
|
||||
/*
|
||||
Package sentry is the official Sentry SDK for Go.
|
||||
Package repository: https://github.com/getsentry/sentry-go/
|
||||
|
||||
Use it to report errors and track application performance through distributed
|
||||
tracing.
|
||||
|
||||
For more information about Sentry and SDK features please have a look at the
|
||||
documentation site https://docs.sentry.io/platforms/go/.
|
||||
|
||||
Basic Usage
|
||||
|
||||
The first step is to initialize the SDK, providing at a minimum the DSN of your
|
||||
Sentry project. This step is accomplished through a call to sentry.Init.
|
||||
|
||||
func main() {
|
||||
err := sentry.Init(...)
|
||||
...
|
||||
}
|
||||
|
||||
A more detailed yet simple example is available at
|
||||
https://github.com/getsentry/sentry-go/blob/master/example/basic/main.go.
|
||||
|
||||
Error Reporting
|
||||
|
||||
The Capture* functions report messages and errors to Sentry.
|
||||
|
||||
sentry.CaptureMessage(...)
|
||||
sentry.CaptureException(...)
|
||||
sentry.CaptureEvent(...)
|
||||
|
||||
Use similarly named functions in the Hub for concurrent programs like web
|
||||
servers.
|
||||
|
||||
Performance Monitoring
|
||||
|
||||
You can use Sentry to monitor your application's performance. More information
|
||||
on the product page https://docs.sentry.io/product/performance/.
|
||||
|
||||
The StartSpan function creates new spans.
|
||||
|
||||
span := sentry.StartSpan(ctx, "operation")
|
||||
...
|
||||
span.Finish()
|
||||
|
||||
Integrations
|
||||
|
||||
The SDK has support for several Go frameworks, available as subpackages.
|
||||
|
||||
Getting Support
|
||||
|
||||
For paid Sentry.io accounts, head out to https://sentry.io/support.
|
||||
|
||||
For all users, support channels include:
|
||||
Forum: https://forum.sentry.io
|
||||
Discord: https://discord.gg/Ww9hbqr (#go channel)
|
||||
|
||||
If you found an issue with the SDK, please report through
|
||||
https://github.com/getsentry/sentry-go/issues/new/choose.
|
||||
|
||||
For responsibly disclosing a security issue, please follow the steps in
|
||||
https://sentry.io/security/#vulnerability-disclosure.
|
||||
For more information about Sentry and SDK features, please have a look at the official documentation site: https://docs.sentry.io/platforms/go/
|
||||
*/
|
||||
package sentry
|
||||
|
||||
47
vendor/github.com/getsentry/sentry-go/dsn.go
generated
vendored
47
vendor/github.com/getsentry/sentry-go/dsn.go
generated
vendored
@@ -145,19 +145,44 @@ func (dsn Dsn) String() string {
|
||||
return url
|
||||
}
|
||||
|
||||
// StoreAPIURL returns the URL of the store endpoint of the project associated
|
||||
// with the DSN.
|
||||
func (dsn Dsn) StoreAPIURL() *url.URL {
|
||||
return dsn.getAPIURL("store")
|
||||
// Get the scheme of the DSN.
|
||||
func (dsn Dsn) GetScheme() string {
|
||||
return string(dsn.scheme)
|
||||
}
|
||||
|
||||
// EnvelopeAPIURL returns the URL of the envelope endpoint of the project
|
||||
// Get the public key of the DSN.
|
||||
func (dsn Dsn) GetPublicKey() string {
|
||||
return dsn.publicKey
|
||||
}
|
||||
|
||||
// Get the secret key of the DSN.
|
||||
func (dsn Dsn) GetSecretKey() string {
|
||||
return dsn.secretKey
|
||||
}
|
||||
|
||||
// Get the host of the DSN.
|
||||
func (dsn Dsn) GetHost() string {
|
||||
return dsn.host
|
||||
}
|
||||
|
||||
// Get the port of the DSN.
|
||||
func (dsn Dsn) GetPort() int {
|
||||
return dsn.port
|
||||
}
|
||||
|
||||
// Get the path of the DSN.
|
||||
func (dsn Dsn) GetPath() string {
|
||||
return dsn.path
|
||||
}
|
||||
|
||||
// Get the project ID of the DSN.
|
||||
func (dsn Dsn) GetProjectID() string {
|
||||
return dsn.projectID
|
||||
}
|
||||
|
||||
// GetAPIURL returns the URL of the envelope endpoint of the project
|
||||
// associated with the DSN.
|
||||
func (dsn Dsn) EnvelopeAPIURL() *url.URL {
|
||||
return dsn.getAPIURL("envelope")
|
||||
}
|
||||
|
||||
func (dsn Dsn) getAPIURL(s string) *url.URL {
|
||||
func (dsn Dsn) GetAPIURL() *url.URL {
|
||||
var rawURL string
|
||||
rawURL += fmt.Sprintf("%s://%s", dsn.scheme, dsn.host)
|
||||
if dsn.port != dsn.scheme.defaultPort() {
|
||||
@@ -166,7 +191,7 @@ func (dsn Dsn) getAPIURL(s string) *url.URL {
|
||||
if dsn.path != "" {
|
||||
rawURL += dsn.path
|
||||
}
|
||||
rawURL += fmt.Sprintf("/api/%s/%s/", dsn.projectID, s)
|
||||
rawURL += fmt.Sprintf("/api/%s/%s/", dsn.projectID, "envelope")
|
||||
parsedURL, _ := url.Parse(rawURL)
|
||||
return parsedURL
|
||||
}
|
||||
|
||||
119
vendor/github.com/getsentry/sentry-go/dynamic_sampling_context.go
generated
vendored
Normal file
119
vendor/github.com/getsentry/sentry-go/dynamic_sampling_context.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/getsentry/sentry-go/internal/otel/baggage"
|
||||
)
|
||||
|
||||
const (
|
||||
sentryPrefix = "sentry-"
|
||||
)
|
||||
|
||||
// DynamicSamplingContext holds information about the current event that can be used to make dynamic sampling decisions.
|
||||
type DynamicSamplingContext struct {
|
||||
Entries map[string]string
|
||||
Frozen bool
|
||||
}
|
||||
|
||||
func DynamicSamplingContextFromHeader(header []byte) (DynamicSamplingContext, error) {
|
||||
bag, err := baggage.Parse(string(header))
|
||||
if err != nil {
|
||||
return DynamicSamplingContext{}, err
|
||||
}
|
||||
|
||||
entries := map[string]string{}
|
||||
for _, member := range bag.Members() {
|
||||
// We only store baggage members if their key starts with "sentry-".
|
||||
if k, v := member.Key(), member.Value(); strings.HasPrefix(k, sentryPrefix) {
|
||||
entries[strings.TrimPrefix(k, sentryPrefix)] = v
|
||||
}
|
||||
}
|
||||
|
||||
return DynamicSamplingContext{
|
||||
Entries: entries,
|
||||
// If there's at least one Sentry value, we consider the DSC frozen
|
||||
Frozen: len(entries) > 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DynamicSamplingContextFromTransaction(span *Span) DynamicSamplingContext {
|
||||
entries := map[string]string{}
|
||||
|
||||
hub := hubFromContext(span.Context())
|
||||
scope := hub.Scope()
|
||||
client := hub.Client()
|
||||
|
||||
if client == nil || scope == nil {
|
||||
return DynamicSamplingContext{
|
||||
Entries: map[string]string{},
|
||||
Frozen: false,
|
||||
}
|
||||
}
|
||||
|
||||
options := client.Options()
|
||||
|
||||
if traceID := span.TraceID.String(); traceID != "" {
|
||||
entries["trace_id"] = traceID
|
||||
}
|
||||
if sampleRate := span.sampleRate; sampleRate != 0 {
|
||||
entries["sample_rate"] = strconv.FormatFloat(sampleRate, 'f', -1, 64)
|
||||
}
|
||||
|
||||
if dsn := client.dsn; dsn != nil {
|
||||
if publicKey := dsn.publicKey; publicKey != "" {
|
||||
entries["public_key"] = publicKey
|
||||
}
|
||||
}
|
||||
if release := options.Release; release != "" {
|
||||
entries["release"] = release
|
||||
}
|
||||
if environment := options.Environment; environment != "" {
|
||||
entries["environment"] = environment
|
||||
}
|
||||
|
||||
// Only include the transaction name if it's of good quality (not empty and not SourceURL)
|
||||
if span.Source != "" && span.Source != SourceURL {
|
||||
if span.IsTransaction() {
|
||||
entries["transaction"] = span.Name
|
||||
}
|
||||
}
|
||||
|
||||
if userSegment := scope.user.Segment; userSegment != "" {
|
||||
entries["user_segment"] = userSegment
|
||||
}
|
||||
|
||||
return DynamicSamplingContext{
|
||||
Entries: entries,
|
||||
Frozen: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (d DynamicSamplingContext) HasEntries() bool {
|
||||
return len(d.Entries) > 0
|
||||
}
|
||||
|
||||
func (d DynamicSamplingContext) IsFrozen() bool {
|
||||
return d.Frozen
|
||||
}
|
||||
|
||||
func (d DynamicSamplingContext) String() string {
|
||||
members := []baggage.Member{}
|
||||
for k, entry := range d.Entries {
|
||||
member, err := baggage.NewMember(sentryPrefix+k, entry)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
members = append(members, member)
|
||||
}
|
||||
if len(members) > 0 {
|
||||
baggage, err := baggage.New(members...)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return baggage.String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
209
vendor/github.com/getsentry/sentry-go/interfaces.go
generated
vendored
209
vendor/github.com/getsentry/sentry-go/interfaces.go
generated
vendored
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -16,6 +17,9 @@ import (
|
||||
// transactionType is the type of a transaction event.
|
||||
const transactionType = "transaction"
|
||||
|
||||
// eventType is the type of an error event.
|
||||
const eventType = "event"
|
||||
|
||||
// Level marks the severity of the event.
|
||||
type Level string
|
||||
|
||||
@@ -28,6 +32,15 @@ const (
|
||||
LevelFatal Level = "fatal"
|
||||
)
|
||||
|
||||
func getSensitiveHeaders() map[string]bool {
|
||||
return map[string]bool{
|
||||
"Authorization": true,
|
||||
"Cookie": true,
|
||||
"X-Forwarded-For": true,
|
||||
"X-Real-Ip": true,
|
||||
}
|
||||
}
|
||||
|
||||
// SdkInfo contains all metadata about about the SDK being used.
|
||||
type SdkInfo struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
@@ -93,10 +106,45 @@ func (b *Breadcrumb) MarshalJSON() ([]byte, error) {
|
||||
// User describes the user associated with an Event. If this is used, at least
|
||||
// an ID or an IP address should be provided.
|
||||
type User struct {
|
||||
Email string `json:"email,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
IPAddress string `json:"ip_address,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
IPAddress string `json:"ip_address,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Segment string `json:"segment,omitempty"`
|
||||
Data map[string]string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (u User) IsEmpty() bool {
|
||||
if len(u.ID) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(u.Email) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(u.IPAddress) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(u.Username) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(u.Name) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(u.Segment) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(u.Data) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Request contains information on a HTTP request related to the event.
|
||||
@@ -121,23 +169,35 @@ func NewRequest(r *http.Request) *Request {
|
||||
}
|
||||
url := fmt.Sprintf("%s://%s%s", protocol, r.Host, r.URL.Path)
|
||||
|
||||
// We read only the first Cookie header because of the specification:
|
||||
// https://tools.ietf.org/html/rfc6265#section-5.4
|
||||
// When the user agent generates an HTTP request, the user agent MUST NOT
|
||||
// attach more than one Cookie header field.
|
||||
cookies := r.Header.Get("Cookie")
|
||||
|
||||
headers := make(map[string]string, len(r.Header))
|
||||
for k, v := range r.Header {
|
||||
headers[k] = strings.Join(v, ",")
|
||||
}
|
||||
headers["Host"] = r.Host
|
||||
|
||||
var cookies string
|
||||
var env map[string]string
|
||||
if addr, port, err := net.SplitHostPort(r.RemoteAddr); err == nil {
|
||||
env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port}
|
||||
headers := map[string]string{}
|
||||
|
||||
if client := CurrentHub().Client(); client != nil && client.Options().SendDefaultPII {
|
||||
// We read only the first Cookie header because of the specification:
|
||||
// https://tools.ietf.org/html/rfc6265#section-5.4
|
||||
// When the user agent generates an HTTP request, the user agent MUST NOT
|
||||
// attach more than one Cookie header field.
|
||||
cookies = r.Header.Get("Cookie")
|
||||
|
||||
for k, v := range r.Header {
|
||||
headers[k] = strings.Join(v, ",")
|
||||
}
|
||||
|
||||
if addr, port, err := net.SplitHostPort(r.RemoteAddr); err == nil {
|
||||
env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port}
|
||||
}
|
||||
} else {
|
||||
sensitiveHeaders := getSensitiveHeaders()
|
||||
for k, v := range r.Header {
|
||||
if _, ok := sensitiveHeaders[k]; !ok {
|
||||
headers[k] = strings.Join(v, ",")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
headers["Host"] = r.Host
|
||||
|
||||
return &Request{
|
||||
URL: url,
|
||||
Method: r.Method,
|
||||
@@ -148,6 +208,22 @@ func NewRequest(r *http.Request) *Request {
|
||||
}
|
||||
}
|
||||
|
||||
// Mechanism is the mechanism by which an exception was generated and handled.
|
||||
type Mechanism struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
HelpLink string `json:"help_link,omitempty"`
|
||||
Handled *bool `json:"handled,omitempty"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// SetUnhandled indicates that the exception is an unhandled exception, i.e.
|
||||
// from a panic.
|
||||
func (m *Mechanism) SetUnhandled() {
|
||||
h := false
|
||||
m.Handled = &h
|
||||
}
|
||||
|
||||
// Exception specifies an error that occurred.
|
||||
type Exception struct {
|
||||
Type string `json:"type,omitempty"` // used as the main issue title
|
||||
@@ -155,6 +231,46 @@ type Exception struct {
|
||||
Module string `json:"module,omitempty"`
|
||||
ThreadID string `json:"thread_id,omitempty"`
|
||||
Stacktrace *Stacktrace `json:"stacktrace,omitempty"`
|
||||
Mechanism *Mechanism `json:"mechanism,omitempty"`
|
||||
}
|
||||
|
||||
// SDKMetaData is a struct to stash data which is needed at some point in the SDK's event processing pipeline
|
||||
// but which shouldn't get send to Sentry.
|
||||
type SDKMetaData struct {
|
||||
dsc DynamicSamplingContext
|
||||
}
|
||||
|
||||
// Contains information about how the name of the transaction was determined.
|
||||
type TransactionInfo struct {
|
||||
Source TransactionSource `json:"source,omitempty"`
|
||||
}
|
||||
|
||||
// The DebugMeta interface is not used in Golang apps, but may be populated
|
||||
// when proxying Events from other platforms, like iOS, Android, and the
|
||||
// Web. (See: https://develop.sentry.dev/sdk/event-payloads/debugmeta/ ).
|
||||
type DebugMeta struct {
|
||||
SdkInfo *DebugMetaSdkInfo `json:"sdk_info,omitempty"`
|
||||
Images []DebugMetaImage `json:"images,omitempty"`
|
||||
}
|
||||
|
||||
type DebugMetaSdkInfo struct {
|
||||
SdkName string `json:"sdk_name,omitempty"`
|
||||
VersionMajor int `json:"version_major,omitempty"`
|
||||
VersionMinor int `json:"version_minor,omitempty"`
|
||||
VersionPatchlevel int `json:"version_patchlevel,omitempty"`
|
||||
}
|
||||
|
||||
type DebugMetaImage struct {
|
||||
Type string `json:"type,omitempty"` // all
|
||||
ImageAddr string `json:"image_addr,omitempty"` // macho,elf,pe
|
||||
ImageSize int `json:"image_size,omitempty"` // macho,elf,pe
|
||||
DebugID string `json:"debug_id,omitempty"` // macho,elf,pe,wasm,sourcemap
|
||||
DebugFile string `json:"debug_file,omitempty"` // macho,elf,pe,wasm
|
||||
CodeID string `json:"code_id,omitempty"` // macho,elf,pe,wasm
|
||||
CodeFile string `json:"code_file,omitempty"` // macho,elf,pe,wasm,sourcemap
|
||||
ImageVmaddr string `json:"image_vmaddr,omitempty"` // macho,elf,pe
|
||||
Arch string `json:"arch,omitempty"` // macho,elf,pe
|
||||
UUID string `json:"uuid,omitempty"` // proguard
|
||||
}
|
||||
|
||||
// EventID is a hexadecimal string representing a unique uuid4 for an Event.
|
||||
@@ -187,12 +303,56 @@ type Event struct {
|
||||
Modules map[string]string `json:"modules,omitempty"`
|
||||
Request *Request `json:"request,omitempty"`
|
||||
Exception []Exception `json:"exception,omitempty"`
|
||||
DebugMeta *DebugMeta `json:"debug_meta,omitempty"`
|
||||
|
||||
// The fields below are only relevant for transactions.
|
||||
|
||||
Type string `json:"type,omitempty"`
|
||||
StartTime time.Time `json:"start_timestamp"`
|
||||
Spans []*Span `json:"spans,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
StartTime time.Time `json:"start_timestamp"`
|
||||
Spans []*Span `json:"spans,omitempty"`
|
||||
TransactionInfo *TransactionInfo `json:"transaction_info,omitempty"`
|
||||
|
||||
// The fields below are not part of the final JSON payload.
|
||||
|
||||
sdkMetaData SDKMetaData
|
||||
}
|
||||
|
||||
// SetException appends the unwrapped errors to the event's exception list.
|
||||
//
|
||||
// maxErrorDepth is the maximum depth of the error chain we will look
|
||||
// into while unwrapping the errors.
|
||||
func (e *Event) SetException(exception error, maxErrorDepth int) {
|
||||
err := exception
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < maxErrorDepth && err != nil; i++ {
|
||||
e.Exception = append(e.Exception, Exception{
|
||||
Value: err.Error(),
|
||||
Type: reflect.TypeOf(err).String(),
|
||||
Stacktrace: ExtractStacktrace(err),
|
||||
})
|
||||
switch previous := err.(type) {
|
||||
case interface{ Unwrap() error }:
|
||||
err = previous.Unwrap()
|
||||
case interface{ Cause() error }:
|
||||
err = previous.Cause()
|
||||
default:
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Add a trace of the current stack to the most recent error in a chain if
|
||||
// it doesn't have a stack trace yet.
|
||||
// We only add to the most recent error to avoid duplication and because the
|
||||
// current stack is most likely unrelated to errors deeper in the chain.
|
||||
if e.Exception[0].Stacktrace == nil {
|
||||
e.Exception[0].Stacktrace = NewStacktrace()
|
||||
}
|
||||
|
||||
// event.Exception should be sorted such that the most recent error is last.
|
||||
reverse(e.Exception)
|
||||
}
|
||||
|
||||
// TODO: Event.Contexts map[string]interface{} => map[string]EventContext,
|
||||
@@ -234,9 +394,10 @@ func (e *Event) defaultMarshalJSON() ([]byte, error) {
|
||||
// be sent for transactions. They shadow the respective fields in Event
|
||||
// and are meant to remain nil, triggering the omitempty behavior.
|
||||
|
||||
Type json.RawMessage `json:"type,omitempty"`
|
||||
StartTime json.RawMessage `json:"start_timestamp,omitempty"`
|
||||
Spans json.RawMessage `json:"spans,omitempty"`
|
||||
Type json.RawMessage `json:"type,omitempty"`
|
||||
StartTime json.RawMessage `json:"start_timestamp,omitempty"`
|
||||
Spans json.RawMessage `json:"spans,omitempty"`
|
||||
TransactionInfo json.RawMessage `json:"transaction_info,omitempty"`
|
||||
}
|
||||
|
||||
x := errorEvent{event: (*event)(e)}
|
||||
|
||||
23
vendor/github.com/getsentry/sentry-go/internal/crypto/randutil/randutil.go
generated
vendored
23
vendor/github.com/getsentry/sentry-go/internal/crypto/randutil/randutil.go
generated
vendored
@@ -1,23 +0,0 @@
|
||||
package randutil
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
floatMax = 1 << 53
|
||||
floatMask = floatMax - 1
|
||||
)
|
||||
|
||||
// Float64 returns a cryptographically secure random number in [0.0, 1.0).
|
||||
func Float64() float64 {
|
||||
// The implementation is, in essence:
|
||||
// return float64(rand.Int63n(1<<53)) / (1<<53)
|
||||
b := make([]byte, 8)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return float64(binary.LittleEndian.Uint64(b)&floatMask) / floatMax
|
||||
}
|
||||
12
vendor/github.com/getsentry/sentry-go/internal/otel/baggage/README.md
generated
vendored
Normal file
12
vendor/github.com/getsentry/sentry-go/internal/otel/baggage/README.md
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
## Why do we have this "otel/baggage" folder?
|
||||
|
||||
The root sentry-go SDK (namely, the Dynamic Sampling functionality) needs an implementation of the [baggage spec](https://www.w3.org/TR/baggage/).
|
||||
For that reason, we've taken the existing baggage implementation from the [opentelemetry-go](https://github.com/open-telemetry/opentelemetry-go/) repository, and fixed a few things that in our opinion were violating the specification.
|
||||
|
||||
These issues are:
|
||||
1. Baggage string value `one%20two` should be properly parsed as "one two"
|
||||
1. Baggage string value `one+two` should be parsed as "one+two"
|
||||
1. Go string value "one two" should be encoded as `one%20two` (percent encoding), and NOT as `one+two` (URL query encoding).
|
||||
1. Go string value "1=1" might be encoded as `1=1`, because the spec says: "Note, value MAY contain any number of the equal sign (=) characters. Parsers MUST NOT assume that the equal sign is only used to separate key and value.". `1%3D1` is also valid, but to simplify the implementation we're not doing it.
|
||||
|
||||
Changes were made in this PR: https://github.com/getsentry/sentry-go/pull/568
|
||||
604
vendor/github.com/getsentry/sentry-go/internal/otel/baggage/baggage.go
generated
vendored
Normal file
604
vendor/github.com/getsentry/sentry-go/internal/otel/baggage/baggage.go
generated
vendored
Normal file
@@ -0,0 +1,604 @@
|
||||
// Adapted from https://github.com/open-telemetry/opentelemetry-go/blob/c21b6b6bb31a2f74edd06e262f1690f3f6ea3d5c/baggage/baggage.go
|
||||
//
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package baggage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/getsentry/sentry-go/internal/otel/baggage/internal/baggage"
|
||||
)
|
||||
|
||||
const (
|
||||
maxMembers = 180
|
||||
maxBytesPerMembers = 4096
|
||||
maxBytesPerBaggageString = 8192
|
||||
|
||||
listDelimiter = ","
|
||||
keyValueDelimiter = "="
|
||||
propertyDelimiter = ";"
|
||||
|
||||
keyDef = `([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5a\x5e-\x7a\x7c\x7e]+)`
|
||||
valueDef = `([\x21\x23-\x2b\x2d-\x3a\x3c-\x5B\x5D-\x7e]*)`
|
||||
keyValueDef = `\s*` + keyDef + `\s*` + keyValueDelimiter + `\s*` + valueDef + `\s*`
|
||||
)
|
||||
|
||||
var (
|
||||
keyRe = regexp.MustCompile(`^` + keyDef + `$`)
|
||||
valueRe = regexp.MustCompile(`^` + valueDef + `$`)
|
||||
propertyRe = regexp.MustCompile(`^(?:\s*` + keyDef + `\s*|` + keyValueDef + `)$`)
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidKey = errors.New("invalid key")
|
||||
errInvalidValue = errors.New("invalid value")
|
||||
errInvalidProperty = errors.New("invalid baggage list-member property")
|
||||
errInvalidMember = errors.New("invalid baggage list-member")
|
||||
errMemberNumber = errors.New("too many list-members in baggage-string")
|
||||
errMemberBytes = errors.New("list-member too large")
|
||||
errBaggageBytes = errors.New("baggage-string too large")
|
||||
)
|
||||
|
||||
// Property is an additional metadata entry for a baggage list-member.
|
||||
type Property struct {
|
||||
key, value string
|
||||
|
||||
// hasValue indicates if a zero-value value means the property does not
|
||||
// have a value or if it was the zero-value.
|
||||
hasValue bool
|
||||
|
||||
// hasData indicates whether the created property contains data or not.
|
||||
// Properties that do not contain data are invalid with no other check
|
||||
// required.
|
||||
hasData bool
|
||||
}
|
||||
|
||||
// NewKeyProperty returns a new Property for key.
|
||||
//
|
||||
// If key is invalid, an error will be returned.
|
||||
func NewKeyProperty(key string) (Property, error) {
|
||||
if !keyRe.MatchString(key) {
|
||||
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
|
||||
}
|
||||
|
||||
p := Property{key: key, hasData: true}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewKeyValueProperty returns a new Property for key with value.
|
||||
//
|
||||
// If key or value are invalid, an error will be returned.
|
||||
func NewKeyValueProperty(key, value string) (Property, error) {
|
||||
if !keyRe.MatchString(key) {
|
||||
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
|
||||
}
|
||||
if !valueRe.MatchString(value) {
|
||||
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
|
||||
}
|
||||
|
||||
p := Property{
|
||||
key: key,
|
||||
value: value,
|
||||
hasValue: true,
|
||||
hasData: true,
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func newInvalidProperty() Property {
|
||||
return Property{}
|
||||
}
|
||||
|
||||
// parseProperty attempts to decode a Property from the passed string. It
|
||||
// returns an error if the input is invalid according to the W3C Baggage
|
||||
// specification.
|
||||
func parseProperty(property string) (Property, error) {
|
||||
if property == "" {
|
||||
return newInvalidProperty(), nil
|
||||
}
|
||||
|
||||
match := propertyRe.FindStringSubmatch(property)
|
||||
if len(match) != 4 {
|
||||
return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidProperty, property)
|
||||
}
|
||||
|
||||
p := Property{hasData: true}
|
||||
if match[1] != "" {
|
||||
p.key = match[1]
|
||||
} else {
|
||||
p.key = match[2]
|
||||
p.value = match[3]
|
||||
p.hasValue = true
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// validate ensures p conforms to the W3C Baggage specification, returning an
|
||||
// error otherwise.
|
||||
func (p Property) validate() error {
|
||||
errFunc := func(err error) error {
|
||||
return fmt.Errorf("invalid property: %w", err)
|
||||
}
|
||||
|
||||
if !p.hasData {
|
||||
return errFunc(fmt.Errorf("%w: %q", errInvalidProperty, p))
|
||||
}
|
||||
|
||||
if !keyRe.MatchString(p.key) {
|
||||
return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key))
|
||||
}
|
||||
if p.hasValue && !valueRe.MatchString(p.value) {
|
||||
return errFunc(fmt.Errorf("%w: %q", errInvalidValue, p.value))
|
||||
}
|
||||
if !p.hasValue && p.value != "" {
|
||||
return errFunc(errors.New("inconsistent value"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Key returns the Property key.
|
||||
func (p Property) Key() string {
|
||||
return p.key
|
||||
}
|
||||
|
||||
// Value returns the Property value. Additionally, a boolean value is returned
|
||||
// indicating if the returned value is the empty if the Property has a value
|
||||
// that is empty or if the value is not set.
|
||||
func (p Property) Value() (string, bool) {
|
||||
return p.value, p.hasValue
|
||||
}
|
||||
|
||||
// String encodes Property into a string compliant with the W3C Baggage
|
||||
// specification.
|
||||
func (p Property) String() string {
|
||||
if p.hasValue {
|
||||
return fmt.Sprintf("%s%s%v", p.key, keyValueDelimiter, p.value)
|
||||
}
|
||||
return p.key
|
||||
}
|
||||
|
||||
type properties []Property
|
||||
|
||||
func fromInternalProperties(iProps []baggage.Property) properties {
|
||||
if len(iProps) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
props := make(properties, len(iProps))
|
||||
for i, p := range iProps {
|
||||
props[i] = Property{
|
||||
key: p.Key,
|
||||
value: p.Value,
|
||||
hasValue: p.HasValue,
|
||||
}
|
||||
}
|
||||
return props
|
||||
}
|
||||
|
||||
func (p properties) asInternal() []baggage.Property {
|
||||
if len(p) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
iProps := make([]baggage.Property, len(p))
|
||||
for i, prop := range p {
|
||||
iProps[i] = baggage.Property{
|
||||
Key: prop.key,
|
||||
Value: prop.value,
|
||||
HasValue: prop.hasValue,
|
||||
}
|
||||
}
|
||||
return iProps
|
||||
}
|
||||
|
||||
func (p properties) Copy() properties {
|
||||
if len(p) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
props := make(properties, len(p))
|
||||
copy(props, p)
|
||||
return props
|
||||
}
|
||||
|
||||
// validate ensures each Property in p conforms to the W3C Baggage
|
||||
// specification, returning an error otherwise.
|
||||
func (p properties) validate() error {
|
||||
for _, prop := range p {
|
||||
if err := prop.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes properties into a string compliant with the W3C Baggage
|
||||
// specification.
|
||||
func (p properties) String() string {
|
||||
props := make([]string, len(p))
|
||||
for i, prop := range p {
|
||||
props[i] = prop.String()
|
||||
}
|
||||
return strings.Join(props, propertyDelimiter)
|
||||
}
|
||||
|
||||
// Member is a list-member of a baggage-string as defined by the W3C Baggage
|
||||
// specification.
|
||||
type Member struct {
|
||||
key, value string
|
||||
properties properties
|
||||
|
||||
// hasData indicates whether the created property contains data or not.
|
||||
// Properties that do not contain data are invalid with no other check
|
||||
// required.
|
||||
hasData bool
|
||||
}
|
||||
|
||||
// NewMember returns a new Member from the passed arguments. The key will be
|
||||
// used directly while the value will be url decoded after validation. An error
|
||||
// is returned if the created Member would be invalid according to the W3C
|
||||
// Baggage specification.
|
||||
func NewMember(key, value string, props ...Property) (Member, error) {
|
||||
m := Member{
|
||||
key: key,
|
||||
value: value,
|
||||
properties: properties(props).Copy(),
|
||||
hasData: true,
|
||||
}
|
||||
if err := m.validate(); err != nil {
|
||||
return newInvalidMember(), err
|
||||
}
|
||||
//// NOTE(anton): I don't think we need to unescape here
|
||||
// decodedValue, err := url.PathUnescape(value)
|
||||
// if err != nil {
|
||||
// return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
|
||||
// }
|
||||
// m.value = decodedValue
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func newInvalidMember() Member {
|
||||
return Member{}
|
||||
}
|
||||
|
||||
// parseMember attempts to decode a Member from the passed string. It returns
|
||||
// an error if the input is invalid according to the W3C Baggage
|
||||
// specification.
|
||||
func parseMember(member string) (Member, error) {
|
||||
if n := len(member); n > maxBytesPerMembers {
|
||||
return newInvalidMember(), fmt.Errorf("%w: %d", errMemberBytes, n)
|
||||
}
|
||||
|
||||
var (
|
||||
key, value string
|
||||
props properties
|
||||
)
|
||||
|
||||
parts := strings.SplitN(member, propertyDelimiter, 2)
|
||||
switch len(parts) {
|
||||
case 2:
|
||||
// Parse the member properties.
|
||||
for _, pStr := range strings.Split(parts[1], propertyDelimiter) {
|
||||
p, err := parseProperty(pStr)
|
||||
if err != nil {
|
||||
return newInvalidMember(), err
|
||||
}
|
||||
props = append(props, p)
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
// Parse the member key/value pair.
|
||||
|
||||
// Take into account a value can contain equal signs (=).
|
||||
kv := strings.SplitN(parts[0], keyValueDelimiter, 2)
|
||||
if len(kv) != 2 {
|
||||
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidMember, member)
|
||||
}
|
||||
// "Leading and trailing whitespaces are allowed but MUST be trimmed
|
||||
// when converting the header into a data structure."
|
||||
key = strings.TrimSpace(kv[0])
|
||||
value = strings.TrimSpace(kv[1])
|
||||
var err error
|
||||
if !keyRe.MatchString(key) {
|
||||
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
|
||||
}
|
||||
if !valueRe.MatchString(value) {
|
||||
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
|
||||
}
|
||||
decodedValue, err := url.PathUnescape(value)
|
||||
if err != nil {
|
||||
return newInvalidMember(), fmt.Errorf("%w: %q", err, value)
|
||||
}
|
||||
value = decodedValue
|
||||
default:
|
||||
// This should never happen unless a developer has changed the string
|
||||
// splitting somehow. Panic instead of failing silently and allowing
|
||||
// the bug to slip past the CI checks.
|
||||
panic("failed to parse baggage member")
|
||||
}
|
||||
|
||||
return Member{key: key, value: value, properties: props, hasData: true}, nil
|
||||
}
|
||||
|
||||
// validate ensures m conforms to the W3C Baggage specification.
|
||||
// A key is just an ASCII string, but a value must be URL encoded UTF-8,
|
||||
// returning an error otherwise.
|
||||
func (m Member) validate() error {
|
||||
if !m.hasData {
|
||||
return fmt.Errorf("%w: %q", errInvalidMember, m)
|
||||
}
|
||||
|
||||
if !keyRe.MatchString(m.key) {
|
||||
return fmt.Errorf("%w: %q", errInvalidKey, m.key)
|
||||
}
|
||||
//// NOTE(anton): IMO it's too early to validate the value here.
|
||||
// if !valueRe.MatchString(m.value) {
|
||||
// return fmt.Errorf("%w: %q", errInvalidValue, m.value)
|
||||
// }
|
||||
return m.properties.validate()
|
||||
}
|
||||
|
||||
// Key returns the Member key.
|
||||
func (m Member) Key() string { return m.key }
|
||||
|
||||
// Value returns the Member value.
|
||||
func (m Member) Value() string { return m.value }
|
||||
|
||||
// Properties returns a copy of the Member properties.
|
||||
func (m Member) Properties() []Property { return m.properties.Copy() }
|
||||
|
||||
// String encodes Member into a string compliant with the W3C Baggage
|
||||
// specification.
|
||||
func (m Member) String() string {
|
||||
// A key is just an ASCII string, but a value is URL encoded UTF-8.
|
||||
s := fmt.Sprintf("%s%s%s", m.key, keyValueDelimiter, percentEncodeValue(m.value))
|
||||
if len(m.properties) > 0 {
|
||||
s = fmt.Sprintf("%s%s%s", s, propertyDelimiter, m.properties.String())
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// percentEncodeValue encodes the baggage value, using percent-encoding for
|
||||
// disallowed octets.
|
||||
func percentEncodeValue(s string) string {
|
||||
const upperhex = "0123456789ABCDEF"
|
||||
var sb strings.Builder
|
||||
|
||||
for byteIndex, width := 0, 0; byteIndex < len(s); byteIndex += width {
|
||||
runeValue, w := utf8.DecodeRuneInString(s[byteIndex:])
|
||||
width = w
|
||||
char := string(runeValue)
|
||||
if valueRe.MatchString(char) && char != "%" {
|
||||
// The character is returned as is, no need to percent-encode
|
||||
sb.WriteString(char)
|
||||
} else {
|
||||
// We need to percent-encode each byte of the multi-octet character
|
||||
for j := 0; j < width; j++ {
|
||||
b := s[byteIndex+j]
|
||||
sb.WriteByte('%')
|
||||
// Bitwise operations are inspired by "net/url"
|
||||
sb.WriteByte(upperhex[b>>4])
|
||||
sb.WriteByte(upperhex[b&15])
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// Baggage is a list of baggage members representing the baggage-string as
|
||||
// defined by the W3C Baggage specification.
|
||||
type Baggage struct { //nolint:golint
|
||||
list baggage.List
|
||||
}
|
||||
|
||||
// New returns a new valid Baggage. It returns an error if it results in a
|
||||
// Baggage exceeding limits set in that specification.
|
||||
//
|
||||
// It expects all the provided members to have already been validated.
|
||||
func New(members ...Member) (Baggage, error) {
|
||||
if len(members) == 0 {
|
||||
return Baggage{}, nil
|
||||
}
|
||||
|
||||
b := make(baggage.List)
|
||||
for _, m := range members {
|
||||
if !m.hasData {
|
||||
return Baggage{}, errInvalidMember
|
||||
}
|
||||
|
||||
// OpenTelemetry resolves duplicates by last-one-wins.
|
||||
b[m.key] = baggage.Item{
|
||||
Value: m.value,
|
||||
Properties: m.properties.asInternal(),
|
||||
}
|
||||
}
|
||||
|
||||
// Check member numbers after deduplication.
|
||||
if len(b) > maxMembers {
|
||||
return Baggage{}, errMemberNumber
|
||||
}
|
||||
|
||||
bag := Baggage{b}
|
||||
if n := len(bag.String()); n > maxBytesPerBaggageString {
|
||||
return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
|
||||
}
|
||||
|
||||
return bag, nil
|
||||
}
|
||||
|
||||
// Parse attempts to decode a baggage-string from the passed string. It
|
||||
// returns an error if the input is invalid according to the W3C Baggage
|
||||
// specification.
|
||||
//
|
||||
// If there are duplicate list-members contained in baggage, the last one
|
||||
// defined (reading left-to-right) will be the only one kept. This diverges
|
||||
// from the W3C Baggage specification which allows duplicate list-members, but
|
||||
// conforms to the OpenTelemetry Baggage specification.
|
||||
func Parse(bStr string) (Baggage, error) {
|
||||
if bStr == "" {
|
||||
return Baggage{}, nil
|
||||
}
|
||||
|
||||
if n := len(bStr); n > maxBytesPerBaggageString {
|
||||
return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
|
||||
}
|
||||
|
||||
b := make(baggage.List)
|
||||
for _, memberStr := range strings.Split(bStr, listDelimiter) {
|
||||
m, err := parseMember(memberStr)
|
||||
if err != nil {
|
||||
return Baggage{}, err
|
||||
}
|
||||
// OpenTelemetry resolves duplicates by last-one-wins.
|
||||
b[m.key] = baggage.Item{
|
||||
Value: m.value,
|
||||
Properties: m.properties.asInternal(),
|
||||
}
|
||||
}
|
||||
|
||||
// OpenTelemetry does not allow for duplicate list-members, but the W3C
|
||||
// specification does. Now that we have deduplicated, ensure the baggage
|
||||
// does not exceed list-member limits.
|
||||
if len(b) > maxMembers {
|
||||
return Baggage{}, errMemberNumber
|
||||
}
|
||||
|
||||
return Baggage{b}, nil
|
||||
}
|
||||
|
||||
// Member returns the baggage list-member identified by key.
|
||||
//
|
||||
// If there is no list-member matching the passed key the returned Member will
|
||||
// be a zero-value Member.
|
||||
// The returned member is not validated, as we assume the validation happened
|
||||
// when it was added to the Baggage.
|
||||
func (b Baggage) Member(key string) Member {
|
||||
v, ok := b.list[key]
|
||||
if !ok {
|
||||
// We do not need to worry about distinguishing between the situation
|
||||
// where a zero-valued Member is included in the Baggage because a
|
||||
// zero-valued Member is invalid according to the W3C Baggage
|
||||
// specification (it has an empty key).
|
||||
return newInvalidMember()
|
||||
}
|
||||
|
||||
return Member{
|
||||
key: key,
|
||||
value: v.Value,
|
||||
properties: fromInternalProperties(v.Properties),
|
||||
hasData: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Members returns all the baggage list-members.
|
||||
// The order of the returned list-members does not have significance.
|
||||
//
|
||||
// The returned members are not validated, as we assume the validation happened
|
||||
// when they were added to the Baggage.
|
||||
func (b Baggage) Members() []Member {
|
||||
if len(b.list) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
members := make([]Member, 0, len(b.list))
|
||||
for k, v := range b.list {
|
||||
members = append(members, Member{
|
||||
key: k,
|
||||
value: v.Value,
|
||||
properties: fromInternalProperties(v.Properties),
|
||||
hasData: true,
|
||||
})
|
||||
}
|
||||
return members
|
||||
}
|
||||
|
||||
// SetMember returns a copy the Baggage with the member included. If the
|
||||
// baggage contains a Member with the same key the existing Member is
|
||||
// replaced.
|
||||
//
|
||||
// If member is invalid according to the W3C Baggage specification, an error
|
||||
// is returned with the original Baggage.
|
||||
func (b Baggage) SetMember(member Member) (Baggage, error) {
|
||||
if !member.hasData {
|
||||
return b, errInvalidMember
|
||||
}
|
||||
|
||||
n := len(b.list)
|
||||
if _, ok := b.list[member.key]; !ok {
|
||||
n++
|
||||
}
|
||||
list := make(baggage.List, n)
|
||||
|
||||
for k, v := range b.list {
|
||||
// Do not copy if we are just going to overwrite.
|
||||
if k == member.key {
|
||||
continue
|
||||
}
|
||||
list[k] = v
|
||||
}
|
||||
|
||||
list[member.key] = baggage.Item{
|
||||
Value: member.value,
|
||||
Properties: member.properties.asInternal(),
|
||||
}
|
||||
|
||||
return Baggage{list: list}, nil
|
||||
}
|
||||
|
||||
// DeleteMember returns a copy of the Baggage with the list-member identified
|
||||
// by key removed.
|
||||
func (b Baggage) DeleteMember(key string) Baggage {
|
||||
n := len(b.list)
|
||||
if _, ok := b.list[key]; ok {
|
||||
n--
|
||||
}
|
||||
list := make(baggage.List, n)
|
||||
|
||||
for k, v := range b.list {
|
||||
if k == key {
|
||||
continue
|
||||
}
|
||||
list[k] = v
|
||||
}
|
||||
|
||||
return Baggage{list: list}
|
||||
}
|
||||
|
||||
// Len returns the number of list-members in the Baggage.
|
||||
func (b Baggage) Len() int {
|
||||
return len(b.list)
|
||||
}
|
||||
|
||||
// String encodes Baggage into a string compliant with the W3C Baggage
|
||||
// specification. The returned string will be invalid if the Baggage contains
|
||||
// any invalid list-members.
|
||||
func (b Baggage) String() string {
|
||||
members := make([]string, 0, len(b.list))
|
||||
for k, v := range b.list {
|
||||
members = append(members, Member{
|
||||
key: k,
|
||||
value: v.Value,
|
||||
properties: fromInternalProperties(v.Properties),
|
||||
}.String())
|
||||
}
|
||||
return strings.Join(members, listDelimiter)
|
||||
}
|
||||
45
vendor/github.com/getsentry/sentry-go/internal/otel/baggage/internal/baggage/baggage.go
generated
vendored
Normal file
45
vendor/github.com/getsentry/sentry-go/internal/otel/baggage/internal/baggage/baggage.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Adapted from https://github.com/open-telemetry/opentelemetry-go/blob/c21b6b6bb31a2f74edd06e262f1690f3f6ea3d5c/internal/baggage/baggage.go
|
||||
//
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/*
|
||||
Package baggage provides base types and functionality to store and retrieve
|
||||
baggage in Go context. This package exists because the OpenTracing bridge to
|
||||
OpenTelemetry needs to synchronize state whenever baggage for a context is
|
||||
modified and that context contains an OpenTracing span. If it were not for
|
||||
this need this package would not need to exist and the
|
||||
`go.opentelemetry.io/otel/baggage` package would be the singular place where
|
||||
W3C baggage is handled.
|
||||
*/
|
||||
package baggage
|
||||
|
||||
// List is the collection of baggage members. The W3C allows for duplicates,
|
||||
// but OpenTelemetry does not, therefore, this is represented as a map.
|
||||
type List map[string]Item
|
||||
|
||||
// Item is the value and metadata properties part of a list-member.
|
||||
type Item struct {
|
||||
Value string
|
||||
Properties []Property
|
||||
}
|
||||
|
||||
// Property is a metadata entry for a list-member.
|
||||
type Property struct {
|
||||
Key, Value string
|
||||
|
||||
// HasValue indicates if a zero-value value means the property does not
|
||||
// have a value or if it was the zero-value.
|
||||
HasValue bool
|
||||
}
|
||||
2
vendor/github.com/getsentry/sentry-go/internal/ratelimit/rate_limits.go
generated
vendored
2
vendor/github.com/getsentry/sentry-go/internal/ratelimit/rate_limits.go
generated
vendored
@@ -15,7 +15,7 @@ var errInvalidXSRLRetryAfter = errors.New("invalid retry-after value")
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// X-Sentry-Rate-Limits: 60:transaction, 2700:default;error;security
|
||||
// X-Sentry-Rate-Limits: 60:transaction, 2700:default;error;security
|
||||
//
|
||||
// This will rate limit transactions for the next 60 seconds and errors for the
|
||||
// next 2700 seconds.
|
||||
|
||||
31
vendor/github.com/getsentry/sentry-go/scope.go
generated
vendored
31
vendor/github.com/getsentry/sentry-go/scope.go
generated
vendored
@@ -31,7 +31,6 @@ type Scope struct {
|
||||
extra map[string]interface{}
|
||||
fingerprint []string
|
||||
level Level
|
||||
transaction string
|
||||
request *http.Request
|
||||
// requestBody holds a reference to the original request.Body.
|
||||
requestBody interface {
|
||||
@@ -275,22 +274,6 @@ func (scope *Scope) SetLevel(level Level) {
|
||||
scope.level = level
|
||||
}
|
||||
|
||||
// SetTransaction sets the transaction name for the current transaction.
|
||||
func (scope *Scope) SetTransaction(name string) {
|
||||
scope.mu.Lock()
|
||||
defer scope.mu.Unlock()
|
||||
|
||||
scope.transaction = name
|
||||
}
|
||||
|
||||
// Transaction returns the transaction name for the current transaction.
|
||||
func (scope *Scope) Transaction() (name string) {
|
||||
scope.mu.RLock()
|
||||
defer scope.mu.RUnlock()
|
||||
|
||||
return scope.transaction
|
||||
}
|
||||
|
||||
// Clone returns a copy of the current scope with all data copied over.
|
||||
func (scope *Scope) Clone() *Scope {
|
||||
scope.mu.RLock()
|
||||
@@ -312,7 +295,6 @@ func (scope *Scope) Clone() *Scope {
|
||||
clone.fingerprint = make([]string, len(scope.fingerprint))
|
||||
copy(clone.fingerprint, scope.fingerprint)
|
||||
clone.level = scope.level
|
||||
clone.transaction = scope.transaction
|
||||
clone.request = scope.request
|
||||
clone.requestBody = scope.requestBody
|
||||
clone.eventProcessors = scope.eventProcessors
|
||||
@@ -365,7 +347,11 @@ func (scope *Scope) ApplyToEvent(event *Event, hint *EventHint) *Event {
|
||||
// to link errors and traces/spans in Sentry.
|
||||
continue
|
||||
}
|
||||
event.Contexts[key] = value
|
||||
|
||||
// Ensure we are not overwriting event fields
|
||||
if _, ok := event.Contexts[key]; !ok {
|
||||
event.Contexts[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,8 +365,7 @@ func (scope *Scope) ApplyToEvent(event *Event, hint *EventHint) *Event {
|
||||
}
|
||||
}
|
||||
|
||||
var emptyUser User
|
||||
if event.User == emptyUser {
|
||||
if event.User.IsEmpty() {
|
||||
event.User = scope.user
|
||||
}
|
||||
|
||||
@@ -392,10 +377,6 @@ func (scope *Scope) ApplyToEvent(event *Event, hint *EventHint) *Event {
|
||||
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.
|
||||
|
||||
10
vendor/github.com/getsentry/sentry-go/sentry.go
generated
vendored
10
vendor/github.com/getsentry/sentry-go/sentry.go
generated
vendored
@@ -5,15 +5,21 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Deprecated: Use SDKVersion instead.
|
||||
const Version = SDKVersion
|
||||
|
||||
// Version is the version of the SDK.
|
||||
const Version = "0.14.0"
|
||||
const SDKVersion = "0.21.0"
|
||||
|
||||
// The identifier of the SDK.
|
||||
const SDKIdentifier = "sentry.go"
|
||||
|
||||
// apiVersion is the minimum version of the Sentry API compatible with the
|
||||
// sentry-go SDK.
|
||||
const apiVersion = "7"
|
||||
|
||||
// userAgent is the User-Agent of outgoing HTTP requests.
|
||||
const userAgent = "sentry-go/" + Version
|
||||
const userAgent = "sentry-go/" + SDKVersion
|
||||
|
||||
// Init initializes the SDK with options. The returned error is non-nil if
|
||||
// options is invalid, for instance if a malformed DSN is provided.
|
||||
|
||||
9
vendor/github.com/getsentry/sentry-go/span_recorder.go
generated
vendored
9
vendor/github.com/getsentry/sentry-go/span_recorder.go
generated
vendored
@@ -4,11 +4,6 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// maxSpans limits the number of recorded spans per transaction. The limit is
|
||||
// meant to bound memory usage and prevent too large transaction events that
|
||||
// would be rejected by Sentry.
|
||||
const maxSpans = 1000
|
||||
|
||||
// A spanRecorder stores a span tree that makes up a transaction. Safe for
|
||||
// concurrent use. It is okay to add child spans from multiple goroutines.
|
||||
type spanRecorder struct {
|
||||
@@ -20,6 +15,10 @@ type spanRecorder struct {
|
||||
// record stores a span. The first stored span is assumed to be the root of a
|
||||
// span tree.
|
||||
func (r *spanRecorder) record(s *Span) {
|
||||
maxSpans := defaultMaxSpans
|
||||
if client := CurrentHub().Client(); client != nil {
|
||||
maxSpans = client.Options().MaxSpans
|
||||
}
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if len(r.spans) >= maxSpans {
|
||||
|
||||
20
vendor/github.com/getsentry/sentry-go/stacktrace.go
generated
vendored
20
vendor/github.com/getsentry/sentry-go/stacktrace.go
generated
vendored
@@ -166,13 +166,7 @@ type Frame struct {
|
||||
Symbol string `json:"symbol,omitempty"`
|
||||
// Module is, despite the name, the Sentry protocol equivalent of a Go
|
||||
// package's import path.
|
||||
Module string `json:"module,omitempty"`
|
||||
// Package is not used for Go stack trace frames. In other platforms it
|
||||
// refers to a container where the Module can be found. For example, a
|
||||
// Java JAR, a .NET Assembly, or a native dynamic library.
|
||||
// It exists for completeness, allowing the construction and reporting
|
||||
// of custom event payloads.
|
||||
Package string `json:"package,omitempty"`
|
||||
Module string `json:"module,omitempty"`
|
||||
Filename string `json:"filename,omitempty"`
|
||||
AbsPath string `json:"abs_path,omitempty"`
|
||||
Lineno int `json:"lineno,omitempty"`
|
||||
@@ -182,6 +176,18 @@ type Frame struct {
|
||||
PostContext []string `json:"post_context,omitempty"`
|
||||
InApp bool `json:"in_app,omitempty"`
|
||||
Vars map[string]interface{} `json:"vars,omitempty"`
|
||||
// Package and the below are not used for Go stack trace frames. In
|
||||
// other platforms it refers to a container where the Module can be
|
||||
// found. For example, a Java JAR, a .NET Assembly, or a native
|
||||
// dynamic library. They exists for completeness, allowing the
|
||||
// construction and reporting of custom event payloads.
|
||||
Package string `json:"package,omitempty"`
|
||||
InstructionAddr string `json:"instruction_addr,omitempty"`
|
||||
AddrMode string `json:"addr_mode,omitempty"`
|
||||
SymbolAddr string `json:"symbol_addr,omitempty"`
|
||||
ImageAddr string `json:"image_addr,omitempty"`
|
||||
Platform string `json:"platform,omitempty"`
|
||||
StackStart bool `json:"stack_start,omitempty"`
|
||||
}
|
||||
|
||||
// NewFrame assembles a stacktrace frame out of runtime.Frame.
|
||||
|
||||
164
vendor/github.com/getsentry/sentry-go/traces_sampler.go
generated
vendored
164
vendor/github.com/getsentry/sentry-go/traces_sampler.go
generated
vendored
@@ -1,171 +1,19 @@
|
||||
package sentry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/getsentry/sentry-go/internal/crypto/randutil"
|
||||
)
|
||||
|
||||
// A TracesSampler makes sampling decisions for spans.
|
||||
//
|
||||
// In addition to the sampling context passed to the Sample method,
|
||||
// implementations may keep and use internal state to make decisions.
|
||||
//
|
||||
// Sampling is one of the last steps when starting a new span, such that the
|
||||
// sampler can inspect most of the state of the span to make a decision.
|
||||
//
|
||||
// Implementations must be safe for concurrent use by multiple goroutines.
|
||||
type TracesSampler interface {
|
||||
Sample(ctx SamplingContext) Sampled
|
||||
}
|
||||
|
||||
// Implementation note:
|
||||
//
|
||||
// TracesSampler.Sample return type is Sampled (instead of bool or float64), so
|
||||
// that we can compose samplers by letting a sampler return SampledUndefined to
|
||||
// defer the decision to the next sampler.
|
||||
//
|
||||
// For example, a hypothetical InheritFromParentSampler would return
|
||||
// SampledUndefined if there is no parent span in the SamplingContext, deferring
|
||||
// the sampling decision to another sampler, like a UniformSampler.
|
||||
//
|
||||
// var _ TracesSampler = sentry.TracesSamplers{
|
||||
// sentry.InheritFromParentSampler,
|
||||
// sentry.UniformTracesSampler(0.1),
|
||||
// }
|
||||
//
|
||||
// Another example, we can provide a sampler that returns SampledFalse if the
|
||||
// SamplingContext matches some condition, and SampledUndefined otherwise:
|
||||
//
|
||||
// var _ TracesSampler = sentry.TracesSamplers{
|
||||
// sentry.IgnoreTransaction(regexp.MustCompile(`^\w+ /(favicon.ico|healthz)`),
|
||||
// sentry.InheritFromParentSampler,
|
||||
// sentry.UniformTracesSampler(0.1),
|
||||
// }
|
||||
//
|
||||
// If after running all samplers the decision is still undefined, the
|
||||
// span/transaction is not sampled.
|
||||
|
||||
// A SamplingContext is passed to a TracesSampler to determine a sampling
|
||||
// decision.
|
||||
//
|
||||
// TODO(tracing): possibly expand SamplingContext to include custom /
|
||||
// user-provided data.
|
||||
type SamplingContext struct {
|
||||
Span *Span // The current span, always non-nil.
|
||||
Parent *Span // The parent span, may be nil.
|
||||
}
|
||||
|
||||
// TODO(tracing): possibly expand SamplingContext to include custom /
|
||||
// user-provided data.
|
||||
//
|
||||
// Unlike in other SDKs, the current http.Request is not part of the
|
||||
// SamplingContext to avoid bloating it with possibly unnecessary values that
|
||||
// could confuse people or have negative performance consequences.
|
||||
//
|
||||
// For the request to be provided in a SamplingContext, a request pointer would
|
||||
// most likely need to be stored in the span context and it would open precedent
|
||||
// for more arbitrary data like fasthttp.Request.
|
||||
//
|
||||
// Users wanting to influence the sampling decision based on the request can
|
||||
// still do so, either by updating the transaction directly on their HTTP
|
||||
// handler:
|
||||
//
|
||||
// func(w http.ResponseWriter, r *http.Request) {
|
||||
// transaction := sentry.TransactionFromContext(r.Context())
|
||||
// if r.Header.Get("X-Custom-Sampling") == "yes" {
|
||||
// transaction.Sampled = sentry.SampledTrue
|
||||
// } else {
|
||||
// transaction.Sampled = sentry.SampledFalse
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Or by having their own middleware that stores arbitrary data in the request
|
||||
// context (a pointer to the request itself included):
|
||||
//
|
||||
// type myContextKey struct{}
|
||||
// type myContextData struct {
|
||||
// request *http.Request
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// func middleware(h http.Handler) http.Handler {
|
||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// data := &myContextData{
|
||||
// request: r,
|
||||
// }
|
||||
// ctx := context.WithValue(r.Context(), myContextKey{}, data)
|
||||
// h.ServeHTTP(w, r.WithContext(ctx))
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// func main() {
|
||||
// err := sentry.Init(sentry.ClientOptions{
|
||||
// // A custom TracesSampler can access data from the span's context:
|
||||
// TracesSampler: sentry.TracesSamplerFunc(func(ctx sentry.SamplingContext) bool {
|
||||
// data, ok := ctx.Span.Context().Value(myContextKey{}).(*myContextData)
|
||||
// if !ok {
|
||||
// return false
|
||||
// }
|
||||
// return data.request.URL.Hostname() == "example.com"
|
||||
// }),
|
||||
// })
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// Note, however, that for the middleware to be effective, it would have to run
|
||||
// before sentryhttp's own middleware, meaning the middleware itself is not
|
||||
// instrumented to send panics to Sentry and it is not part of the timed
|
||||
// transaction.
|
||||
//
|
||||
// If neither of those prove to be sufficient, we can consider including a
|
||||
// (possibly nil) *http.Request field to SamplingContext. In that case, the SDK
|
||||
// would need to track the request either in the Scope or the Span.Context.
|
||||
//
|
||||
// Alternatively, add a map-like type or simply a generic interface{} similar to
|
||||
// the CustomSamplingContext type in the Java SDK:
|
||||
//
|
||||
// type SamplingContext struct {
|
||||
// Span *Span // The current span, always non-nil.
|
||||
// Parent *Span // The parent span, may be nil.
|
||||
// CustomData interface{}
|
||||
// }
|
||||
//
|
||||
// func CustomSamplingContext(data interface{}) SpanOption {
|
||||
// return func(s *Span) {
|
||||
// s.customSamplingContext = data
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func main() {
|
||||
// // ...
|
||||
// span := sentry.StartSpan(ctx, "op", CustomSamplingContext(data))
|
||||
// // ...
|
||||
// }
|
||||
|
||||
// The TracesSamplerFunc type is an adapter to allow the use of ordinary
|
||||
// The TracesSample type is an adapter to allow the use of ordinary
|
||||
// functions as a TracesSampler.
|
||||
type TracesSamplerFunc func(ctx SamplingContext) Sampled
|
||||
type TracesSampler func(ctx SamplingContext) float64
|
||||
|
||||
var _ TracesSampler = TracesSamplerFunc(nil)
|
||||
|
||||
func (f TracesSamplerFunc) Sample(ctx SamplingContext) Sampled {
|
||||
func (f TracesSampler) Sample(ctx SamplingContext) float64 {
|
||||
return f(ctx)
|
||||
}
|
||||
|
||||
// UniformTracesSampler is a TracesSampler that samples root spans randomly at a
|
||||
// uniform rate.
|
||||
type UniformTracesSampler float64
|
||||
|
||||
var _ TracesSampler = UniformTracesSampler(0)
|
||||
|
||||
func (s UniformTracesSampler) Sample(ctx SamplingContext) Sampled {
|
||||
if s < 0.0 || s > 1.0 {
|
||||
panic(fmt.Errorf("sampling rate out of range [0.0, 1.0]: %f", s))
|
||||
}
|
||||
if randutil.Float64() < float64(s) {
|
||||
return SampledTrue
|
||||
}
|
||||
return SampledFalse
|
||||
}
|
||||
|
||||
// TODO(tracing): implement and export basic TracesSampler implementations:
|
||||
// parent-based, span ID / trace ID based, etc. It should be possible to compose
|
||||
// parent-based with other samplers.
|
||||
|
||||
411
vendor/github.com/getsentry/sentry-go/tracing.go
generated
vendored
411
vendor/github.com/getsentry/sentry-go/tracing.go
generated
vendored
@@ -9,9 +9,15 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
SentryTraceHeader = "sentry-trace"
|
||||
SentryBaggageHeader = "baggage"
|
||||
)
|
||||
|
||||
// A Span is the building block of a Sentry transaction. Spans build up a tree
|
||||
// structure of timed operations. The span tree makes up a transaction event
|
||||
// that is sent to Sentry when the root span is finished.
|
||||
@@ -21,6 +27,7 @@ type Span struct { //nolint: maligned // prefer readability over optimal memory
|
||||
TraceID TraceID `json:"trace_id"`
|
||||
SpanID SpanID `json:"span_id"`
|
||||
ParentSpanID SpanID `json:"parent_span_id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Op string `json:"op,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Status SpanStatus `json:"status,omitempty"`
|
||||
@@ -28,24 +35,44 @@ type Span struct { //nolint: maligned // prefer readability over optimal memory
|
||||
StartTime time.Time `json:"start_timestamp"`
|
||||
EndTime time.Time `json:"timestamp"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
Sampled Sampled `json:"-"`
|
||||
Source TransactionSource `json:"-"`
|
||||
|
||||
Sampled Sampled `json:"-"`
|
||||
|
||||
// mu protects concurrent writes to map fields
|
||||
mu sync.RWMutex
|
||||
// sample rate the span was sampled with.
|
||||
sampleRate float64
|
||||
// ctx is the context where the span was started. Always non-nil.
|
||||
ctx context.Context
|
||||
|
||||
// Dynamic Sampling context
|
||||
dynamicSamplingContext DynamicSamplingContext
|
||||
// parent refers to the immediate local parent span. A remote parent span is
|
||||
// only referenced by setting ParentSpanID.
|
||||
parent *Span
|
||||
|
||||
// isTransaction is true only for the root span of a local span tree. The
|
||||
// root span is the first span started in a context. Note that a local root
|
||||
// span may have a remote parent belonging to the same trace, therefore
|
||||
// isTransaction depends on ctx and not on parent.
|
||||
isTransaction bool
|
||||
|
||||
// recorder stores all spans in a transaction. Guaranteed to be non-nil.
|
||||
recorder *spanRecorder
|
||||
// span context, can only be set on transactions
|
||||
contexts map[string]Context
|
||||
}
|
||||
|
||||
// TraceParentContext describes the context of a (remote) parent span.
|
||||
//
|
||||
// The context is normally extracted from a received "sentry-trace" header and
|
||||
// used to initialize a new transaction.
|
||||
//
|
||||
// Note: the name might be not the best one. It was taken mostly to stay aligned
|
||||
// with other SDKs, and it alludes to W3C "traceparent" header (https://www.w3.org/TR/trace-context/),
|
||||
// which serves a similar purpose to "sentry-trace". We should eventually consider
|
||||
// making this type internal-only and give it a better name.
|
||||
type TraceParentContext struct {
|
||||
TraceID TraceID
|
||||
ParentSpanID SpanID
|
||||
Sampled Sampled
|
||||
}
|
||||
|
||||
// (*) Note on maligned:
|
||||
@@ -81,14 +108,19 @@ func StartSpan(ctx context.Context, operation string, options ...SpanOption) *Sp
|
||||
// defaults
|
||||
Op: operation,
|
||||
StartTime: time.Now(),
|
||||
Sampled: SampledUndefined,
|
||||
|
||||
ctx: context.WithValue(ctx, spanContextKey{}, &span),
|
||||
parent: parent,
|
||||
isTransaction: !hasParent,
|
||||
}
|
||||
|
||||
if hasParent {
|
||||
span.TraceID = parent.TraceID
|
||||
} else {
|
||||
// Only set the Source if this is a transaction
|
||||
span.Source = SourceCustom
|
||||
|
||||
// Implementation note:
|
||||
//
|
||||
// While math/rand is ~2x faster than crypto/rand (exact
|
||||
@@ -174,9 +206,6 @@ func (s *Span) Finish() {
|
||||
// (see https://github.com/getsentry/sentry-python/blob/f6f3525f8812f609/sentry_sdk/tracing.py#L372)
|
||||
|
||||
hub := hubFromContext(s.ctx)
|
||||
if hub.Scope().Transaction() == "" {
|
||||
Logger.Printf("Missing transaction name for span with op = %q", s.Op)
|
||||
}
|
||||
hub.CaptureEvent(event)
|
||||
}
|
||||
|
||||
@@ -195,12 +224,66 @@ func (s *Span) StartChild(operation string, options ...SpanOption) *Span {
|
||||
// accessing the tags map directly as SetTag takes care of initializing the map
|
||||
// when necessary.
|
||||
func (s *Span) SetTag(name, value string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.Tags == nil {
|
||||
s.Tags = make(map[string]string)
|
||||
}
|
||||
s.Tags[name] = value
|
||||
}
|
||||
|
||||
// SetData sets a data on the span. It is recommended to use SetData instead of
|
||||
// accessing the data map directly as SetData takes care of initializing the map
|
||||
// when necessary.
|
||||
func (s *Span) SetData(name, value string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.Data == nil {
|
||||
s.Data = make(map[string]interface{})
|
||||
}
|
||||
s.Data[name] = value
|
||||
}
|
||||
|
||||
// SetContext sets a context on the span. It is recommended to use SetContext instead of
|
||||
// accessing the contexts map directly as SetContext takes care of initializing the map
|
||||
// when necessary.
|
||||
func (s *Span) SetContext(key string, value Context) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.contexts == nil {
|
||||
s.contexts = make(map[string]Context)
|
||||
}
|
||||
s.contexts[key] = value
|
||||
}
|
||||
|
||||
// IsTransaction checks if the given span is a transaction.
|
||||
func (s *Span) IsTransaction() bool {
|
||||
return s.isTransaction
|
||||
}
|
||||
|
||||
// GetTransaction returns the transaction that contains this span.
|
||||
//
|
||||
// For transaction spans it returns itself. For spans that were created manually
|
||||
// the method returns "nil".
|
||||
func (s *Span) GetTransaction() *Span {
|
||||
spanRecorder := s.spanRecorder()
|
||||
if spanRecorder == nil {
|
||||
// This probably means that the Span was created manually (not via
|
||||
// StartTransaction/StartSpan or StartChild).
|
||||
// Return "nil" to indicate that it's not a normal situation.
|
||||
return nil
|
||||
}
|
||||
recorderRoot := spanRecorder.root()
|
||||
if recorderRoot == nil {
|
||||
// Same as above: manually created Span.
|
||||
return nil
|
||||
}
|
||||
return recorderRoot
|
||||
}
|
||||
|
||||
// TODO(tracing): maybe add shortcuts to get/set transaction name. Right now the
|
||||
// transaction name is in the Scope, as it has existed there historically, prior
|
||||
// to tracing.
|
||||
@@ -210,8 +293,9 @@ func (s *Span) SetTag(name, value string) {
|
||||
// func (s *Span) TransactionName() string
|
||||
// func (s *Span) SetTransactionName(name string)
|
||||
|
||||
// ToSentryTrace returns the trace propagation value used with the sentry-trace
|
||||
// HTTP header.
|
||||
// ToSentryTrace returns the seralized TraceParentContext from a transaction/sapn.
|
||||
// Use this function to propagate the TraceParentContext to a downstream SDK,
|
||||
// either as the value of the "sentry-trace" HTTP header, or as an html "sentry-trace" meta tag.
|
||||
func (s *Span) ToSentryTrace() string {
|
||||
// TODO(tracing): add instrumentation for outgoing HTTP requests using
|
||||
// ToSentryTrace.
|
||||
@@ -226,25 +310,51 @@ func (s *Span) ToSentryTrace() string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// ToBaggage returns the serialized DynamicSamplingContext from a transaction.
|
||||
// Use this function to propagate the DynamicSamplingContext to a downstream SDK,
|
||||
// either as the value of the "baggage" HTTP header, or as an html "baggage" meta tag.
|
||||
func (s *Span) ToBaggage() string {
|
||||
if containingTransaction := s.GetTransaction(); containingTransaction != nil {
|
||||
// In case there is currently no frozen DynamicSamplingContext attached to the transaction,
|
||||
// create one from the properties of the transaction.
|
||||
if !s.dynamicSamplingContext.IsFrozen() {
|
||||
// This will return a frozen DynamicSamplingContext.
|
||||
s.dynamicSamplingContext = DynamicSamplingContextFromTransaction(containingTransaction)
|
||||
}
|
||||
|
||||
return containingTransaction.dynamicSamplingContext.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// SetDynamicSamplingContext sets the given dynamic sampling context on the
|
||||
// current transaction.
|
||||
func (s *Span) SetDynamicSamplingContext(dsc DynamicSamplingContext) {
|
||||
if s.isTransaction {
|
||||
s.dynamicSamplingContext = dsc
|
||||
}
|
||||
}
|
||||
|
||||
// sentryTracePattern matches either
|
||||
//
|
||||
// TRACE_ID - SPAN_ID
|
||||
// [[:xdigit:]]{32}-[[:xdigit:]]{16}
|
||||
// TRACE_ID - SPAN_ID
|
||||
// [[:xdigit:]]{32}-[[:xdigit:]]{16}
|
||||
//
|
||||
// or
|
||||
//
|
||||
// TRACE_ID - SPAN_ID - SAMPLED
|
||||
// [[:xdigit:]]{32}-[[:xdigit:]]{16}-[01]
|
||||
// TRACE_ID - SPAN_ID - SAMPLED
|
||||
// [[:xdigit:]]{32}-[[:xdigit:]]{16}-[01]
|
||||
var sentryTracePattern = regexp.MustCompile(`^([[:xdigit:]]{32})-([[:xdigit:]]{16})(?:-([01]))?$`)
|
||||
|
||||
// updateFromSentryTrace parses a sentry-trace HTTP header (as returned by
|
||||
// ToSentryTrace) and updates fields of the span. If the header cannot be
|
||||
// recognized as valid, the span is left unchanged.
|
||||
func (s *Span) updateFromSentryTrace(header []byte) {
|
||||
// recognized as valid, the span is left unchanged. The returned value indicates
|
||||
// whether the span was updated.
|
||||
func (s *Span) updateFromSentryTrace(header []byte) (updated bool) {
|
||||
m := sentryTracePattern.FindSubmatch(header)
|
||||
if m == nil {
|
||||
// no match
|
||||
return
|
||||
return false
|
||||
}
|
||||
_, _ = hex.Decode(s.TraceID[:], m[1])
|
||||
_, _ = hex.Decode(s.ParentSpanID[:], m[2])
|
||||
@@ -256,6 +366,18 @@ func (s *Span) updateFromSentryTrace(header []byte) {
|
||||
s.Sampled = SampledTrue
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Span) updateFromBaggage(header []byte) {
|
||||
if s.isTransaction {
|
||||
dsc, err := DynamicSamplingContextFromHeader(header)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.dynamicSamplingContext = dsc
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Span) MarshalJSON() ([]byte, error) {
|
||||
@@ -276,45 +398,104 @@ func (s *Span) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (s *Span) sample() Sampled {
|
||||
// https://develop.sentry.dev/sdk/unified-api/tracing/#sampling
|
||||
// #1 explicit sampling decision via StartSpan options.
|
||||
if s.Sampled != SampledUndefined {
|
||||
return s.Sampled
|
||||
}
|
||||
hub := hubFromContext(s.ctx)
|
||||
var clientOptions ClientOptions
|
||||
client := hub.Client()
|
||||
if client != nil {
|
||||
clientOptions = hub.Client().Options()
|
||||
}
|
||||
samplingContext := SamplingContext{Span: s, Parent: s.parent}
|
||||
|
||||
// https://develop.sentry.dev/sdk/performance/#sampling
|
||||
// #1 tracing is not enabled.
|
||||
if !clientOptions.EnableTracing {
|
||||
Logger.Printf("Dropping transaction: EnableTracing is set to %t", clientOptions.EnableTracing)
|
||||
s.sampleRate = 0.0
|
||||
return SampledFalse
|
||||
}
|
||||
|
||||
// #2 explicit sampling decision via StartSpan/StartTransaction options.
|
||||
if s.Sampled != SampledUndefined {
|
||||
Logger.Printf("Using explicit sampling decision from StartSpan/StartTransaction: %v", s.Sampled)
|
||||
switch s.Sampled {
|
||||
case SampledTrue:
|
||||
s.sampleRate = 1.0
|
||||
case SampledFalse:
|
||||
s.sampleRate = 0.0
|
||||
}
|
||||
return s.Sampled
|
||||
}
|
||||
|
||||
// Variant for non-transaction spans: they inherit the parent decision.
|
||||
// TracesSampler only runs for the root span.
|
||||
// Note: non-transaction should always have a parent, but we check both
|
||||
// conditions anyway -- the first for semantic meaning, the second to
|
||||
// avoid a nil pointer dereference.
|
||||
if !s.isTransaction && s.parent != nil {
|
||||
return s.parent.Sampled
|
||||
}
|
||||
// #2 use TracesSampler from ClientOptions.
|
||||
|
||||
// #3 use TracesSampler from ClientOptions.
|
||||
sampler := clientOptions.TracesSampler
|
||||
if sampler != nil {
|
||||
return sampler.Sample(samplingContext)
|
||||
samplingContext := SamplingContext{
|
||||
Span: s,
|
||||
Parent: s.parent,
|
||||
}
|
||||
// #3 inherit parent decision.
|
||||
|
||||
if sampler != nil {
|
||||
tracesSamplerSampleRate := sampler.Sample(samplingContext)
|
||||
s.sampleRate = tracesSamplerSampleRate
|
||||
if tracesSamplerSampleRate < 0.0 || tracesSamplerSampleRate > 1.0 {
|
||||
Logger.Printf("Dropping transaction: Returned TracesSampler rate is out of range [0.0, 1.0]: %f", tracesSamplerSampleRate)
|
||||
return SampledFalse
|
||||
}
|
||||
if tracesSamplerSampleRate == 0 {
|
||||
Logger.Printf("Dropping transaction: Returned TracesSampler rate is: %f", tracesSamplerSampleRate)
|
||||
return SampledFalse
|
||||
}
|
||||
|
||||
if rng.Float64() < tracesSamplerSampleRate {
|
||||
return SampledTrue
|
||||
}
|
||||
Logger.Printf("Dropping transaction: TracesSampler returned rate: %f", tracesSamplerSampleRate)
|
||||
return SampledFalse
|
||||
}
|
||||
// #4 inherit parent decision.
|
||||
if s.parent != nil {
|
||||
Logger.Printf("Using sampling decision from parent: %v", s.parent.Sampled)
|
||||
switch s.parent.Sampled {
|
||||
case SampledTrue:
|
||||
s.sampleRate = 1.0
|
||||
case SampledFalse:
|
||||
s.sampleRate = 0.0
|
||||
}
|
||||
return s.parent.Sampled
|
||||
}
|
||||
// #4 uniform sampling using TracesSampleRate.
|
||||
sampler = UniformTracesSampler(clientOptions.TracesSampleRate)
|
||||
return sampler.Sample(samplingContext)
|
||||
|
||||
// #5 use TracesSampleRate from ClientOptions.
|
||||
sampleRate := clientOptions.TracesSampleRate
|
||||
s.sampleRate = sampleRate
|
||||
if sampleRate < 0.0 || sampleRate > 1.0 {
|
||||
Logger.Printf("Dropping transaction: TracesSamplerRate out of range [0.0, 1.0]: %f", sampleRate)
|
||||
return SampledFalse
|
||||
}
|
||||
if sampleRate == 0.0 {
|
||||
Logger.Printf("Dropping transaction: TracesSampleRate rate is: %f", sampleRate)
|
||||
return SampledFalse
|
||||
}
|
||||
|
||||
if rng.Float64() < sampleRate {
|
||||
return SampledTrue
|
||||
}
|
||||
|
||||
return SampledFalse
|
||||
}
|
||||
|
||||
func (s *Span) toEvent() *Event {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if !s.isTransaction {
|
||||
return nil // only transactions can be transformed into events
|
||||
}
|
||||
hub := hubFromContext(s.ctx)
|
||||
|
||||
children := s.recorder.children()
|
||||
finished := make([]*Span, 0, len(children))
|
||||
@@ -326,17 +507,33 @@ func (s *Span) toEvent() *Event {
|
||||
finished = append(finished, child)
|
||||
}
|
||||
|
||||
// Create and attach a DynamicSamplingContext to the transaction.
|
||||
// If the DynamicSamplingContext is not frozen at this point, we can assume being head of trace.
|
||||
if !s.dynamicSamplingContext.IsFrozen() {
|
||||
s.dynamicSamplingContext = DynamicSamplingContextFromTransaction(s)
|
||||
}
|
||||
|
||||
contexts := map[string]Context{}
|
||||
for k, v := range s.contexts {
|
||||
contexts[k] = v
|
||||
}
|
||||
contexts["trace"] = s.traceContext().Map()
|
||||
|
||||
return &Event{
|
||||
Type: transactionType,
|
||||
Transaction: hub.Scope().Transaction(),
|
||||
Contexts: map[string]Context{
|
||||
"trace": s.traceContext().Map(),
|
||||
Transaction: s.Name,
|
||||
Contexts: contexts,
|
||||
Tags: s.Tags,
|
||||
Extra: s.Data,
|
||||
Timestamp: s.EndTime,
|
||||
StartTime: s.StartTime,
|
||||
Spans: finished,
|
||||
TransactionInfo: &TransactionInfo{
|
||||
Source: s.Source,
|
||||
},
|
||||
sdkMetaData: SDKMetaData{
|
||||
dsc: s.dynamicSamplingContext,
|
||||
},
|
||||
Tags: s.Tags,
|
||||
Extra: s.Data,
|
||||
Timestamp: s.EndTime,
|
||||
StartTime: s.StartTime,
|
||||
Spans: finished,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +551,23 @@ func (s *Span) traceContext() *TraceContext {
|
||||
// spanRecorder stores the span tree. Guaranteed to be non-nil.
|
||||
func (s *Span) spanRecorder() *spanRecorder { return s.recorder }
|
||||
|
||||
// ParseTraceParentContext parses a sentry-trace header and builds a TraceParentContext from the
|
||||
// parsed values. If the header was parsed correctly, the second returned argument
|
||||
// ("valid") will be set to true, otherwise (e.g., empty or malformed header) it will
|
||||
// be false.
|
||||
func ParseTraceParentContext(header []byte) (traceParentContext TraceParentContext, valid bool) {
|
||||
s := Span{}
|
||||
updated := s.updateFromSentryTrace(header)
|
||||
if !updated {
|
||||
return TraceParentContext{}, false
|
||||
}
|
||||
return TraceParentContext{
|
||||
TraceID: s.TraceID,
|
||||
ParentSpanID: s.ParentSpanID,
|
||||
Sampled: s.Sampled,
|
||||
}, true
|
||||
}
|
||||
|
||||
// TraceID identifies a trace.
|
||||
type TraceID [16]byte
|
||||
|
||||
@@ -394,6 +608,18 @@ var (
|
||||
zeroSpanID SpanID
|
||||
)
|
||||
|
||||
// Contains information about how the name of the transaction was determined.
|
||||
type TransactionSource string
|
||||
|
||||
const (
|
||||
SourceCustom TransactionSource = "custom"
|
||||
SourceURL TransactionSource = "url"
|
||||
SourceRoute TransactionSource = "route"
|
||||
SourceView TransactionSource = "view"
|
||||
SourceComponent TransactionSource = "component"
|
||||
SourceTask TransactionSource = "task"
|
||||
)
|
||||
|
||||
// SpanStatus is the status of a span.
|
||||
type SpanStatus uint8
|
||||
|
||||
@@ -530,9 +756,9 @@ type Sampled int8
|
||||
// The possible trace sampling decisions are: SampledFalse, SampledUndefined
|
||||
// (default) and SampledTrue.
|
||||
const (
|
||||
SampledFalse Sampled = -1 + iota
|
||||
SampledUndefined
|
||||
SampledTrue
|
||||
SampledFalse Sampled = -1
|
||||
SampledUndefined Sampled = 0
|
||||
SampledTrue Sampled = 1
|
||||
)
|
||||
|
||||
func (s Sampled) String() string {
|
||||
@@ -561,9 +787,62 @@ type SpanOption func(s *Span)
|
||||
// A span tree has a single transaction name, therefore using this option when
|
||||
// starting a span affects the span tree as a whole, potentially overwriting a
|
||||
// name set previously.
|
||||
//
|
||||
// Deprecated: Use WithTransactionSource() instead.
|
||||
func TransactionName(name string) SpanOption {
|
||||
return WithTransactionName(name)
|
||||
}
|
||||
|
||||
// WithTransactionName option sets the name of the current transaction.
|
||||
//
|
||||
// A span tree has a single transaction name, therefore using this option when
|
||||
// starting a span affects the span tree as a whole, potentially overwriting a
|
||||
// name set previously.
|
||||
func WithTransactionName(name string) SpanOption {
|
||||
return func(s *Span) {
|
||||
hubFromContext(s.Context()).Scope().SetTransaction(name)
|
||||
s.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
// OpName sets the operation name for a given span.
|
||||
//
|
||||
// Deprecated: Use WithOpName() instead.
|
||||
func OpName(name string) SpanOption {
|
||||
return WithOpName(name)
|
||||
}
|
||||
|
||||
// WithOpName sets the operation name for a given span.
|
||||
func WithOpName(name string) SpanOption {
|
||||
return func(s *Span) {
|
||||
s.Op = name
|
||||
}
|
||||
}
|
||||
|
||||
// TransctionSource sets the source of the transaction name.
|
||||
//
|
||||
// Deprecated: Use WithTransactionSource() instead.
|
||||
func TransctionSource(source TransactionSource) SpanOption {
|
||||
return WithTransactionSource(source)
|
||||
}
|
||||
|
||||
// WithTransactionSource sets the source of the transaction name.
|
||||
func WithTransactionSource(source TransactionSource) SpanOption {
|
||||
return func(s *Span) {
|
||||
s.Source = source
|
||||
}
|
||||
}
|
||||
|
||||
// SpanSampled updates the sampling flag for a given span.
|
||||
//
|
||||
// Deprecated: Use WithSpanSampled() instead.
|
||||
func SpanSampled(sampled Sampled) SpanOption {
|
||||
return WithSpanSampled(sampled)
|
||||
}
|
||||
|
||||
// WithSpanSampled updates the sampling flag for a given span.
|
||||
func WithSpanSampled(sampled Sampled) SpanOption {
|
||||
return func(s *Span) {
|
||||
s.Sampled = sampled
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,9 +851,31 @@ func TransactionName(name string) SpanOption {
|
||||
// span will be left unchanged.
|
||||
//
|
||||
// ContinueFromRequest is an alias for:
|
||||
// ContinueFromTrace(r.Header.Get("sentry-trace"))
|
||||
//
|
||||
// ContinueFromHeaders(r.Header.Get(SentryTraceHeader), r.Header.Get(SentryBaggageHeader)).
|
||||
func ContinueFromRequest(r *http.Request) SpanOption {
|
||||
return ContinueFromTrace(r.Header.Get("sentry-trace"))
|
||||
return ContinueFromHeaders(r.Header.Get(SentryTraceHeader), r.Header.Get(SentryBaggageHeader))
|
||||
}
|
||||
|
||||
// ContinueFromHeaders returns a span option that updates the span to continue
|
||||
// an existing TraceID and propagates the Dynamic Sampling context.
|
||||
func ContinueFromHeaders(trace, baggage string) SpanOption {
|
||||
return func(s *Span) {
|
||||
if trace != "" {
|
||||
s.updateFromSentryTrace([]byte(trace))
|
||||
}
|
||||
if baggage != "" {
|
||||
s.updateFromBaggage([]byte(baggage))
|
||||
}
|
||||
|
||||
// In case a sentry-trace header is present but there are no sentry-related
|
||||
// values in the baggage, create an empty, frozen DynamicSamplingContext.
|
||||
if trace != "" && !s.dynamicSamplingContext.HasEntries() {
|
||||
s.dynamicSamplingContext = DynamicSamplingContext{
|
||||
Frozen: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ContinueFromTrace returns a span option that updates the span to continue
|
||||
@@ -612,7 +913,7 @@ func TransactionFromContext(ctx context.Context) *Span {
|
||||
//
|
||||
// Note the equivalence:
|
||||
//
|
||||
// SpanFromContext(ctx).StartChild(...) === StartSpan(ctx, ...)
|
||||
// SpanFromContext(ctx).StartChild(...) === StartSpan(ctx, ...)
|
||||
//
|
||||
// So we don't aim spanFromContext at creating spans, but mutating existing
|
||||
// spans that you'd have no access otherwise (because it was created in code you
|
||||
@@ -626,3 +927,19 @@ func spanFromContext(ctx context.Context) *Span {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartTransaction will create a transaction (root span) if there's no existing
|
||||
// transaction in the context otherwise, it will return the existing transaction.
|
||||
func StartTransaction(ctx context.Context, name string, options ...SpanOption) *Span {
|
||||
currentTransaction, exists := ctx.Value(spanContextKey{}).(*Span)
|
||||
if exists {
|
||||
return currentTransaction
|
||||
}
|
||||
|
||||
options = append(options, WithTransactionName(name))
|
||||
return StartSpan(
|
||||
ctx,
|
||||
"",
|
||||
options...,
|
||||
)
|
||||
}
|
||||
|
||||
59
vendor/github.com/getsentry/sentry-go/transport.go
generated
vendored
59
vendor/github.com/getsentry/sentry-go/transport.go
generated
vendored
@@ -94,26 +94,53 @@ func getRequestBodyFromEvent(event *Event) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func transactionEnvelopeFromBody(eventID EventID, sentAt time.Time, body json.RawMessage) (*bytes.Buffer, error) {
|
||||
func envelopeFromBody(event *Event, dsn *Dsn, sentAt time.Time, body json.RawMessage) (*bytes.Buffer, error) {
|
||||
var b bytes.Buffer
|
||||
enc := json.NewEncoder(&b)
|
||||
// envelope header
|
||||
|
||||
// Construct the trace envelope header
|
||||
var trace = map[string]string{}
|
||||
if dsc := event.sdkMetaData.dsc; dsc.HasEntries() {
|
||||
for k, v := range dsc.Entries {
|
||||
trace[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Envelope header
|
||||
err := enc.Encode(struct {
|
||||
EventID EventID `json:"event_id"`
|
||||
SentAt time.Time `json:"sent_at"`
|
||||
EventID EventID `json:"event_id"`
|
||||
SentAt time.Time `json:"sent_at"`
|
||||
Dsn string `json:"dsn"`
|
||||
Sdk map[string]string `json:"sdk"`
|
||||
Trace map[string]string `json:"trace,omitempty"`
|
||||
}{
|
||||
EventID: eventID,
|
||||
EventID: event.EventID,
|
||||
SentAt: sentAt,
|
||||
Trace: trace,
|
||||
Dsn: dsn.String(),
|
||||
Sdk: map[string]string{
|
||||
"name": event.Sdk.Name,
|
||||
"version": event.Sdk.Version,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// item header
|
||||
|
||||
var itemType string
|
||||
switch event.Type {
|
||||
case transactionType:
|
||||
itemType = transactionType
|
||||
default:
|
||||
itemType = eventType
|
||||
}
|
||||
|
||||
// Item header
|
||||
err = enc.Encode(struct {
|
||||
Type string `json:"type"`
|
||||
Length int `json:"length"`
|
||||
}{
|
||||
Type: transactionType,
|
||||
Type: itemType,
|
||||
Length: len(body),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -124,6 +151,7 @@ func transactionEnvelopeFromBody(eventID EventID, sentAt time.Time, body json.Ra
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &b, nil
|
||||
}
|
||||
|
||||
@@ -137,21 +165,14 @@ func getRequestFromEvent(event *Event, dsn *Dsn) (r *http.Request, err error) {
|
||||
if body == nil {
|
||||
return nil, errors.New("event could not be marshaled")
|
||||
}
|
||||
if event.Type == transactionType {
|
||||
b, err := transactionEnvelopeFromBody(event.EventID, time.Now(), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return http.NewRequest(
|
||||
http.MethodPost,
|
||||
dsn.EnvelopeAPIURL().String(),
|
||||
b,
|
||||
)
|
||||
envelope, err := envelopeFromBody(event, dsn, time.Now(), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return http.NewRequest(
|
||||
http.MethodPost,
|
||||
dsn.StoreAPIURL().String(),
|
||||
bytes.NewReader(body),
|
||||
dsn.GetAPIURL().String(),
|
||||
envelope,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/getsentry/sentry-go/util.go
generated
vendored
2
vendor/github.com/getsentry/sentry-go/util.go
generated
vendored
@@ -35,7 +35,7 @@ func monotonicTimeSince(start time.Time) (end time.Time) {
|
||||
return start.Add(time.Since(start))
|
||||
}
|
||||
|
||||
//nolint: deadcode, unused
|
||||
// nolint: deadcode, unused
|
||||
func prettyPrint(data interface{}) {
|
||||
dbg, _ := json.MarshalIndent(data, "", " ")
|
||||
fmt.Println(string(dbg))
|
||||
|
||||
Reference in New Issue
Block a user