(db *sql.DB)
| 208 | } |
| 209 | |
| 210 | func handleExceptionDetail(db *sql.DB) http.HandlerFunc { |
| 211 | return func(w http.ResponseWriter, r *http.Request) { |
| 212 | id := r.PathValue("id") |
| 213 | |
| 214 | // Load error event. The URL segment may be an internal id or a Sentry |
| 215 | // event_id (the exceptions list and the frontend route both key on |
| 216 | // event_id), so resolve by either and prefer the internal-id match. |
| 217 | var ( |
| 218 | internalID, eventID, fingerprint, level, receivedAt, payloadStr string |
| 219 | handled sql.NullBool |
| 220 | platform, environment, serverName, txn, release sql.NullString |
| 221 | traceID, spanID sql.NullString |
| 222 | eventTS sql.NullString |
| 223 | ) |
| 224 | err := db.QueryRow( |
| 225 | `SELECT id, event_id, fingerprint, level, handled, platform, environment, server_name, |
| 226 | "transaction", release, trace_id, span_id, received_at, event_ts, payload |
| 227 | FROM sentry_error_events WHERE id = ? OR event_id = ? ORDER BY (id = ?) DESC LIMIT 1`, id, id, id, |
| 228 | ).Scan(&internalID, &eventID, &fingerprint, &level, &handled, &platform, &environment, |
| 229 | &serverName, &txn, &release, &traceID, &spanID, &receivedAt, &eventTS, &payloadStr) |
| 230 | if err == sql.ErrNoRows { |
| 231 | apiError(w, "not found", http.StatusNotFound) |
| 232 | return |
| 233 | } |
| 234 | if err != nil { |
| 235 | apiError(w, err.Error(), http.StatusInternalServerError) |
| 236 | return |
| 237 | } |
| 238 | |
| 239 | // Child tables key on the internal id, not the URL segment. |
| 240 | exceptions := loadExceptions(db, internalID) |
| 241 | breadcrumbs := loadBreadcrumbs(db, internalID) |
| 242 | |
| 243 | // Load trace summary if trace_id exists. |
| 244 | var traceSummary any |
| 245 | if traceID.Valid && traceID.String != "" { |
| 246 | traceSummary = loadTraceSummary(db, traceID.String) |
| 247 | } |
| 248 | |
| 249 | // Parse payload. |
| 250 | var payload json.RawMessage |
| 251 | if payloadStr != "" { |
| 252 | payload = json.RawMessage(payloadStr) |
| 253 | } |
| 254 | |
| 255 | result := map[string]any{ |
| 256 | "id": internalID, |
| 257 | "event_id": eventID, |
| 258 | "fingerprint": fingerprint, |
| 259 | "level": level, |
| 260 | "handled": scanNullBool(handled), |
| 261 | "platform": scanNullString(platform), |
| 262 | "environment": scanNullString(environment), |
| 263 | "server_name": scanNullString(serverName), |
| 264 | "transaction": scanNullString(txn), |
| 265 | "release": scanNullString(release), |
| 266 | "trace_id": scanNullString(traceID), |
| 267 | "span_id": scanNullString(spanID), |
no test coverage detected