1
- // This is an example web server to demonstrate how to instrument web servers
2
- // with Sentry.
1
+ // This is an example web server to demonstrate how to instrument error and
2
+ // performance monitoring with Sentry.
3
3
//
4
4
// Try it by running:
5
5
//
@@ -20,6 +20,8 @@ import (
20
20
"image/png"
21
21
"log"
22
22
"net/http"
23
+ "strings"
24
+ "sync"
23
25
"time"
24
26
25
27
"github.com/getsentry/sentry-go"
@@ -51,6 +53,31 @@ func run() error {
51
53
log .Printf ("BeforeSend event [%s]" , event .EventID )
52
54
return event
53
55
},
56
+ // Specify either TracesSampleRate or set a TracesSampler to
57
+ // enable tracing.
58
+ // TracesSampleRate: 0.5,
59
+ TracesSampler : sentry .TracesSamplerFunc (func (ctx sentry.SamplingContext ) sentry.Sampled {
60
+ // As an example, this custom sampler does not send some
61
+ // transactions to Sentry based on their name.
62
+ hub := sentry .GetHubFromContext (ctx .Span .Context ())
63
+ name := hub .Scope ().Transaction ()
64
+ if name == "GET /favicon.ico" {
65
+ return sentry .SampledFalse
66
+ }
67
+ if strings .HasPrefix (name , "HEAD" ) {
68
+ return sentry .SampledFalse
69
+ }
70
+ // As an example, sample some transactions with a
71
+ // uniform rate.
72
+ if strings .HasPrefix (name , "POST" ) {
73
+ return sentry .UniformTracesSampler (0.2 ).Sample (ctx )
74
+ }
75
+ // Sample all other transactions for testing. On
76
+ // production, use TracesSampleRate with a rate adequate
77
+ // for your traffic, or use the SamplingContext to
78
+ // customize sampling per-transaction.
79
+ return sentry .SampledTrue
80
+ }),
54
81
})
55
82
if err != nil {
56
83
return err
@@ -60,26 +87,62 @@ func run() error {
60
87
defer sentry .Flush (2 * time .Second )
61
88
62
89
// Main HTTP handler, renders an HTML page with a random image.
90
+ //
91
+ // A new transaction is automatically sent to Sentry when the handler is
92
+ // invoked.
63
93
http .HandleFunc ("/" , func (w http.ResponseWriter , r * http.Request ) {
94
+ // Use GetHubFromContext to get a hub associated with the
95
+ // current request. Hubs provide data isolation, such that tags,
96
+ // breadcrumbs and other attributes are never mixed up across
97
+ // requests.
98
+ ctx := r .Context ()
99
+ hub := sentry .GetHubFromContext (ctx )
100
+
64
101
if r .URL .Path != "/" {
65
- // Use GetHubFromContext to get a hub associated with the current
66
- // request. Hubs provide data isolation, such that tags, breadcrumbs
67
- // and other attributes are never mixed up across requests.
68
- hub := sentry .GetHubFromContext (r .Context ())
69
102
hub .Scope ().SetTag ("url" , r .URL .Path )
70
103
hub .CaptureMessage ("Page Not Found" )
71
104
http .NotFound (w , r )
72
105
return
73
106
}
74
107
75
- err := t .Execute (w , time .Now ().UnixNano ())
76
- if err != nil {
77
- log .Printf ("[%s] %s" , r .URL .Path , err )
78
- return
79
- }
108
+ // Set a custom transaction name: use "Home" instead of the
109
+ // default "/" based on r.URL.Path.
110
+ hub .Scope ().SetTransaction ("Home" )
111
+
112
+ // The next block of code shows how to instrument concurrent
113
+ // tasks.
114
+ var wg sync.WaitGroup
115
+ wg .Add (2 )
116
+ go func () {
117
+ defer wg .Done ()
118
+ span := sentry .StartSpan (ctx , "template.execute" )
119
+ defer span .Finish ()
120
+ err := t .Execute (w , time .Now ().UnixNano ())
121
+ if err != nil {
122
+ log .Printf ("[%s] %s" , r .URL .Path , err )
123
+ return
124
+ }
125
+ }()
126
+ go func () {
127
+ defer wg .Done ()
128
+ span := sentry .StartSpan (ctx , "sleep" )
129
+ defer span .Finish ()
130
+ // For demonstration only, ensure homepage loading takes
131
+ // at least 40ms.
132
+ time .Sleep (40 * time .Millisecond )
133
+ }()
134
+ wg .Wait ()
80
135
})
81
136
82
137
// HTTP handler for the random image.
138
+ //
139
+ // A new transaction is automatically sent to Sentry when the handler is
140
+ // invoked. We use sentry.StartSpan and span.Finish to create additional
141
+ // child spans measuring specific parts of the image computation.
142
+ //
143
+ // In general, wrap potentially slow parts of your handlers (external
144
+ // network calls, CPU-intensive tasks, etc) to help identify where time
145
+ // is spent.
83
146
http .HandleFunc ("/random.png" , func (w http.ResponseWriter , r * http.Request ) {
84
147
ctx := r .Context ()
85
148
var cancel context.CancelFunc
@@ -90,9 +153,15 @@ func run() error {
90
153
}
91
154
92
155
q := r .URL .Query ().Get ("q" )
93
- img := NewImage (ctx , 128 , 128 , []byte (q ))
94
156
157
+ span := sentry .StartSpan (ctx , "NewImage" )
158
+ img := NewImage (span .Context (), 128 , 128 , []byte (q ))
159
+ span .Finish ()
160
+
161
+ span = sentry .StartSpan (ctx , "png.Encode" )
95
162
err := png .Encode (w , img )
163
+ span .Finish ()
164
+
96
165
if err != nil {
97
166
log .Printf ("[%s] %s" , r .URL .Path , err )
98
167
hub := sentry .GetHubFromContext (ctx )
@@ -110,10 +179,11 @@ func run() error {
110
179
111
180
log .Printf ("Serving http://%s" , * addr )
112
181
113
- // Wrap the default mux with Sentry to capture panics and report errors.
182
+ // Wrap the default mux with Sentry to capture panics, report errors and
183
+ // measure performance.
114
184
//
115
- // Alternatively, you can also wrap individual handlers if you need to use
116
- // different options for different parts of your app.
185
+ // Alternatively, you can also wrap individual handlers if you need to
186
+ // use different options for different parts of your app.
117
187
handler := sentryhttp .New (sentryhttp.Options {}).Handle (http .DefaultServeMux )
118
188
return http .ListenAndServe (* addr , handler )
119
189
}
@@ -144,17 +214,38 @@ img {
144
214
145
215
// NewImage returns a random image based on seed, with the given width and
146
216
// height.
217
+ //
218
+ // NewImage uses the context to create spans that measure the performance of its
219
+ // internal parts.
147
220
func NewImage (ctx context.Context , width , height int , seed []byte ) image.Image {
221
+ span := sentry .StartSpan (ctx , "sha256" )
148
222
b := sha256 .Sum256 (seed )
223
+ span .Finish ()
149
224
150
225
img := image .NewGray (image .Rect (0 , 0 , width , height ))
151
226
227
+ span = sentry .StartSpan (ctx , "img" )
228
+ defer span .Finish ()
152
229
for i := 0 ; i < len (img .Pix ); i += len (b ) {
153
230
select {
154
231
case <- ctx .Done ():
155
232
// Context canceled, abort image generation.
156
- // Spot the bug: the returned image cannot be encoded as PNG and
157
- // will cause an error that will be reported to Sentry.
233
+
234
+ // Set a tag on the current span.
235
+ span .SetTag ("canceled" , "yes" )
236
+ // Set a tag on the current transaction.
237
+ //
238
+ // Note that spans are not designed to be mutated from
239
+ // concurrent goroutines. If multiple goroutines may try
240
+ // to mutate a span/transaction, for example to set
241
+ // tags, use a mutex to synchronize changes, or use a
242
+ // channel to communicate the desired changes back into
243
+ // the goroutine where the span was created.
244
+ sentry .TransactionFromContext (ctx ).SetTag ("img.canceled" , "yes" )
245
+
246
+ // Spot the bug: the returned image cannot be encoded as
247
+ // PNG and will cause an error that will be reported to
248
+ // Sentry.
158
249
return img .SubImage (image .Rect (0 , 0 , 0 , 0 ))
159
250
default :
160
251
}
0 commit comments