Repository navigation
v0.35.0
A debug error page: with APP_DEBUG on, a server error is shown on a page of what tug knows of it, where it was the error as plain text, with a panic's stack after it, in a browser, and in the modal Inertia's client opens for a visit that failed. The page has the error and each error it wraps, with its Go type; a panic's stack from the frame that panicked, the app's frames open with the lines of source around them, and tug's and the standard library's folded; the route that answered, and the lines of the app's that added it, which say where an error a handler returns, with no stack, came from; and the request, its secrets [REDACTED].
A handler of an app made with v0.35.0, with a bug:
// stats counts the visits to each page.
func stats(c *tug.Ctx) error {
var visits map[string]int
visits[c.Request().URL.Path]++
return c.JSON(200, visits)
}and the page a browser gets for GET /stats, as text, its file's path shortened and its request's headers left out:
500 INTERNAL SERVER ERROR
panic: assignment to entry in nil map
GET /stats · stats
THE ERROR
*tug.PanicError panic: assignment to entry in nil map
runtime.plainError assignment to entry in nil map
THE STACK
main.stats
/home/you/blog/main.go:148
145 // stats counts the visits to each page.
146 func stats(c *tug.Ctx) error {
147 var visits map[string]int
148 visits[c.Request().URL.Path]++ ← marked
149 return c.JSON(200, visits)
150 }
▸ 27 frames of tug and the standard library
THE ROUTE
Route GET /stats
Name stats
Handler main.stats
Added /home/you/blog/main.go:114
113 app.Post("/hello", hello).Name("hello").Takes(HelloInput{})
114 app.Get("/stats", stats).Name("stats") ← marked
115 app.Get("/report", report).Name("report")
THE REQUEST
Method GET
URL /stats
Request ID ZKDUJ5KKFMGW3LAPSE3ROMJKIV
Go 1.26.0 · tug v0.35.0. This page is shown as APP_DEBUG is on, which a deployed app leaves off.
An error a handler returns, fmt.Errorf("reading today's report: %w", err), as a client that asks for JSON gets it:
{
"message": "reading today's report: open reports/today.csv: no such file or directory",
"errors": [
{ "type": "*fmt.wrapError", "message": "reading today's report: open reports/today.csv: no such file or directory" },
{ "type": "*fs.PathError", "message": "open reports/today.csv: no such file or directory" },
{ "type": "syscall.Errno", "message": "no such file or directory" }
],
"route": "GET /report"
}What's new:
- A page for a server error, while debugging: under
Config.Debug, whichAPP_DEBUGturns on, astug new's.envhas it,DefaultErrorHandleranswers a 5xx with an HTML page, to a client that takes HTML, as a browser does, and Inertia's client, which shows it in its modal over the page the visit left. With Debug off, nothing changes. - The error and what it wraps: each error of its chain, with its Go type, and those
errors.Joinjoins a level further in. - A panic's stack: from the frame that panicked, the app's frames open, each with the lines of source around it, its own marked, and tug's, the standard library's and the dependencies' folded between them.
- Where a returned error came from: an error a handler returns has no stack, so the page shows the route that answered, its name, its handler, and the lines of the app's that added it.
- The request: its method, URL, the route's values, its ID, and its headers, with
Cookie,Authorization, and the tokens and passwords in its query[REDACTED], as Inertia's DevTools keep them. Config.Editor, fromAPP_EDITOR: each frame's file a link that opens it in the editor, at its line:vscode,cursor,zed,golandorsublime, or a link of the editor's with{file}and{line}in it.- JSON and text too: a client that asks for JSON first gets the error, what it wraps, a panic's frames and the route as JSON, and one that takes no HTML, as curl, the text, as before.
- No script: the page is HTML and CSS alone, as Inertia's modal shows it under the Content-Security-Policy of the page the visit left, and its style carries the response's nonce, for a policy that asks for one.
- The guide: Routing, a section on the page, While debugging; Pages, Deployment and Getting started, what
APP_DEBUGshows; and the README.
Upgrading: nothing an app calls changed: go get github.com/cuonggt/tug@v0.35.0, and an app with APP_DEBUG on shows the page from then on, one made before too, with no change of its own, and its tests pass as they did. With it off, as a deployed app has it, its errors are answered as before. APP_EDITOR=vscode in the .env makes the frames links to the editor. A client of a debugging app that read a server error's JSON gets errors, stack and route beside its message, and the message without a panic's stack after it.
The guide is in docs/. tug needs Go 1.26.