Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
136 lines
4.0 KiB
Markdown
136 lines
4.0 KiB
Markdown
<p align="center">
|
|
<a href="https://sentry.io" target="_blank" align="center">
|
|
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" width="280">
|
|
</a>
|
|
<br />
|
|
</p>
|
|
|
|
# Official Sentry net/http Handler for Sentry-go SDK
|
|
|
|
**go.dev:** https://pkg.go.dev/github.com/getsentry/sentry-go/http
|
|
|
|
**Example:** https://github.com/getsentry/sentry-go/tree/master/_examples/http
|
|
|
|
## Installation
|
|
|
|
```sh
|
|
go get github.com/getsentry/sentry-go/http
|
|
```
|
|
|
|
```go
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/getsentry/sentry-go"
|
|
sentryhttp "github.com/getsentry/sentry-go/http"
|
|
)
|
|
|
|
// To initialize Sentry's handler, you need to initialize Sentry itself beforehand
|
|
if err := sentry.Init(sentry.ClientOptions{
|
|
Dsn: "your-public-dsn",
|
|
}); err != nil {
|
|
fmt.Printf("Sentry initialization failed: %v\n", err)
|
|
}
|
|
|
|
// Create an instance of sentryhttp
|
|
sentryHandler := sentryhttp.New(sentryhttp.Options{})
|
|
|
|
// Once it's done, you can setup routes and attach the handler as one of your middleware
|
|
http.Handle("/", sentryHandler.Handle(&handler{}))
|
|
http.HandleFunc("/foo", sentryHandler.HandleFunc(func(rw http.ResponseWriter, r *http.Request) {
|
|
panic("y tho")
|
|
}))
|
|
|
|
fmt.Println("Listening and serving HTTP on :3000")
|
|
|
|
// And run it
|
|
if err := http.ListenAndServe(":3000", nil); err != nil {
|
|
panic(err)
|
|
}
|
|
```
|
|
|
|
## Configuration
|
|
|
|
`sentryhttp` accepts a struct of `Options` that allows you to configure how the handler will behave.
|
|
|
|
Currently it respects 3 options:
|
|
|
|
```go
|
|
// Whether Sentry should repanic after recovery, in most cases it should be set to true,
|
|
// and you should gracefully handle http responses.
|
|
Repanic bool
|
|
// Whether you want to block the request before moving forward with the response.
|
|
// Useful, when you want to restart the process after it panics.
|
|
WaitForDelivery bool
|
|
// Timeout for the event delivery requests.
|
|
Timeout time.Duration
|
|
```
|
|
|
|
## Usage
|
|
|
|
`sentryhttp` attaches an instance of `*sentry.Hub` (https://pkg.go.dev/github.com/getsentry/sentry-go#Hub) to the request's context, which makes it available throughout the rest of the request's lifetime.
|
|
You can access it by using the `sentry.GetHubFromContext()` method on the request itself in any of your proceeding middleware and routes.
|
|
And it should be used instead of the global `sentry.CaptureMessage`, `sentry.CaptureException`, or any other calls, as it keeps the separation of data between the requests.
|
|
|
|
**Keep in mind that `*sentry.Hub` won't be available in middleware attached before to `sentryhttp`!**
|
|
|
|
```go
|
|
type handler struct{}
|
|
|
|
func (h *handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
|
if hub := sentry.GetHubFromContext(r.Context()); hub != nil {
|
|
hub.WithScope(func(scope *sentry.Scope) {
|
|
scope.SetExtra("unwantedQuery", "someQueryDataMaybe")
|
|
hub.CaptureMessage("User provided unwanted query string, but we recovered just fine")
|
|
})
|
|
}
|
|
rw.WriteHeader(http.StatusOK)
|
|
}
|
|
|
|
func enhanceSentryEvent(handler http.HandlerFunc) http.HandlerFunc {
|
|
return func(rw http.ResponseWriter, r *http.Request) {
|
|
if hub := sentry.GetHubFromContext(r.Context()); hub != nil {
|
|
hub.Scope().SetTag("someRandomTag", "maybeYouNeedIt")
|
|
}
|
|
handler(rw, r)
|
|
}
|
|
}
|
|
|
|
// Later in the code
|
|
|
|
sentryHandler := sentryhttp.New(sentryhttp.Options{
|
|
Repanic: true,
|
|
})
|
|
|
|
http.Handle("/", sentryHandler.Handle(&handler{}))
|
|
http.HandleFunc("/foo", sentryHandler.HandleFunc(
|
|
enhanceSentryEvent(func(rw http.ResponseWriter, r *http.Request) {
|
|
panic("y tho")
|
|
}),
|
|
))
|
|
|
|
fmt.Println("Listening and serving HTTP on :3000")
|
|
|
|
if err := http.ListenAndServe(":3000", nil); err != nil {
|
|
panic(err)
|
|
}
|
|
```
|
|
|
|
### Accessing Request in `BeforeSend` callback
|
|
|
|
```go
|
|
sentry.Init(sentry.ClientOptions{
|
|
Dsn: "your-public-dsn",
|
|
BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
|
|
if hint.Context != nil {
|
|
if req, ok := hint.Context.Value(sentry.RequestContextKey).(*http.Request); ok {
|
|
// You have access to the original Request here
|
|
}
|
|
}
|
|
|
|
return event
|
|
},
|
|
})
|
|
```
|