Skip to content

v0.35.0

Choose a tag to compare

@cuonggt cuonggt released this 01 Oct 17:54
· 24 commits to main since this release

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, which APP_DEBUG turns on, as tug new's .env has it, DefaultErrorHandler answers 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.Join joins 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, from APP_EDITOR: each frame's file a link that opens it in the editor, at its line: vscode, cursor, zed, goland or sublime, 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_DEBUG shows; 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.