Debugging errors
ahall opened this issue · 10 comments
I find it sometimes where hard to find out where particular errors are coming from. Line numbers given in the panic trace don't match up with the template. I think that may be because the error is corresponding to the template after it's being translated from Gold into the Go HTML template. Would it be possible somehow to retrieve the compiled output from the trace so I can display it on my website in order to make a nice error page?
@ahall I intended to display the line numbers of a Gold template. I want to check this issue in more detail. Could you give me your Gold templates which triggers the error?
For example one of my templates has messed up indent and the only error I get is:
"The indent of the line 2 is invalid." - Be nice if it said what file and line it was. This is part of the gold parser so should be very simple to do. Also be nice to print out what line 2 is and as much information as we can get from it.
Here is another example with the following template:
extends ../account/account_base
block title
title Forgot password
block content
p {{.Blah.Blah}}
This gives the error:
template: /path/to/forgot_password.gold:1:1245: executing "/path/to/forgot_password.gold" at <.Blah.Blah>: Blah is not a field of struct type webcommon.TemplateData
This line 1 is probably in the template that gold compiled into Go htm/template I assume which is not very useful. Not sure how we can improve that.
Similar happens even when the error is in the layout, it will not say the error is in the layout, but rather in the template inheriting it, and again on line 1.
Be sweet if we can address this somehow, but I realise its not going to be easy :).
Could perhaps if martini.Env is not martini.Prod do the compilation from Gold to html/template on multiple lines instead of optimising it all in one line and then make the compiled template available in the trace or something.
Let me know if I can assist any further.
Sorry about referencing martini there, not sure what I was thinking, just been using gold exclusively with martini. I meant some kind of development mode which you kind of have, when caching is false I'd say we're in development mode and could use that.
@ahall Hi, I added a debugging feature on Gold.
At first, I will explain how Gold works:
step1. Gold converts Gold templates into intermediate HTML source codes which include Go html/template package's expressions (like "{{.Title}}").
step2. Go html/template package parses the intermediate HTML source codes and writes the result.
The following error
"The indent of the line 2 is invalid."
occurs at step1 of above. I improved the message so that this shows the template path and the line content as follwoing:
The indent of the line 7 is invalid. [template: tpls/child.gold][lineno: 7][line: p This is a test message]
The following error
template: /path/to/forgot_password.gold:1:1245: executing "/path/to/forgot_password.gold" at <.Blah.Blah>: Blah is not a field of struct type webcommon.TemplateData
occurs at step2 of above. Gold tries to compress HTML source codes to a single line and this is the reason the error message said that the error occurred at line no 1.
I added a debugging feature to Gold so that we can inspect the intermediate HTML source codes which Gold generates:
var g = gold.NewGenerator(false).SetPrettyPrint(true).SetDebugWriter(os.Stdout)
SetPrettyPrint(true)
tells the generator to pretty-print the intermediate HTML source codes. SetDebugWriter(os.Stdout)
tells the generator to write the intermediate HTML source codes to os.Stdout (you can set any struct of io.Writer). Here is an example of the intermediate HTML source codes wrote to os.Stdout:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <title>
5 {{.Title.A}}
6 </title>
7 </head>
8 <body>
9 <h1>
10 Gold - Template engine for Golang
11 </h1>
12 <div id="container" class="wrapper">
13 {{if true}}
14 <p>
15 You can use an expression of Golang text/template package in a Gold template.
16 </p>
17 {{end}}
18 <p>
19 Gold is a template engine for Golang.
20 </p>
21 </div>
22 <p>
23 This is a overrided message aaa.
24 </p>
25 <p>
26 ABCDEF
27 </p>
28 <p>
29 This is a test message
30 </p>
31 <div>
32 {{if gt .TestNum 0}}
33 <p>
34 AAA
35 </p>
36 {{end}}
37 </div>
38 <div>
39 {{ .TestNum }}
40 </div>
41 <p>
42 This is an included template.
43 </p>
44 <p>
45 XYZ
46 </p>
47 </body>
48 </html>
Please check this debugging feature out!
Thanks!
Ho @yosssi. This works fine, couple of points
- This pulls in a hell of a lot of dependencies just for printing HTML. Do you think its worth it? My apps all have all dependencies self contained:
"go-net": {
"type": "hg",
"remote": "https://code.google.com/p/go.net",
"local": "code.google.com/p/go.net"
},
"go-text": {
"type": "hg",
"remote": "https://code.google.com/p/go.text",
"local": "code.google.com/p/go.text"
},
"gohtml": {
"type": "git",
"remote": "https://github.com/yosssi/gohtml",
"local": "github.com/yosssi/gohtml"
},
This pretty printing is adding 3 dependencies, just FYI perhaps not that big deal.
- I'd like to be able to make the debug writer write to webpage and if it's on the engine level it cannot be injected into the http response objects. Any idea how that can be solved?
Thanks.
@ahall Thanks for your quick reply.
- This pulls in a hell of a lot of dependencies just for printing HTML. Do you think its worth it? My apps all have all dependencies self contained:
Yes, I know. yosssi/gohtml package depends on code.google.com/p/go.net package to parse HTML source codes.
- I'd like to be able to make the debug writer write to webpage and if it's on the engine level it cannot be injected into the http response objects. Any idea how that can be solved?
Oh, I see. Please give me time to think of it.
Thanks.
- I'd like to be able to make the debug writer write to webpage and if it's on the engine level it cannot be injected into the http response objects. Any idea how that can be solved?
I implemented Generator.ParseFileWithHTML and Generator.ParseStringWithHTML which return intermediate HTML source codes that Gold generates. You can use these HTML source codes for debugging.
Example:
package main
import (
"fmt"
"html/template"
"net/http"
"github.com/yosssi/gohtml"
"github.com/yosssi/gold"
)
var g = gold.NewGenerator(false).SetPrettyPrint(true)
func handler(w http.ResponseWriter, r *http.Request) {
parent := `
doctype html
html
head
title {{.Title}}
body
block content
footer
block footer
`
child := `
extends parent
block content
#container
{{.Msg.NotExistProperty}}
block footer
.footer
| Copyright XXX
`
stringTemplates := map[string]string{"parent": parent, "child": child}
tpl, html, err := g.ParseStringWithHTML(stringTemplates, "child")
if err != nil {
panic(err)
}
data := map[string]interface{}{"Title": "Gold", "Msg": "Hello!"}
err = tpl.Execute(w, data)
if err != nil {
fmt.Fprintf(w, "<pre>Error:\n%s\nHTML:\n%s</pre>", err.Error(), gohtml.AddLineNo(template.HTMLEscapeString(html)))
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Output:
Error:
template: child:10:12: executing "child" at <.Msg.NotExistPropert...>: can't evaluate field NotExistProperty in type interface {}
HTML:
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>
5 {{.Title}}
6 </title>
7 </head>
8 <body>
9 <div id="container">
10 {{.Msg.NotExistProperty}}
11 </div>
12 <footer>
13 <div class="footer">
14 Copyright XXX
15 </div>
16 </footer>
17 </body>
18 </html>
Please check this methods!
Thanks.
@yosssi Thanks for this. I managed to generate a nice error page from when there is an error in the template. Perhaps will look at highlighting the failed error in the future but this is pretty good.
Thanks again for this, closing the issue.
Thanks for your checking up!