The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/util/errutil/template.go

121 lines
2.9 KiB

package errutil
import (
"bytes"
"fmt"
"text/template"
)
// Template is an extended Base for when using templating to construct
// error messages.
type Template struct {
Base Base
logTemplate *template.Template
publicTemplate *template.Template
}
// TemplateData contains data for constructing an Error based on a
// Template.
type TemplateData struct {
Private map[string]any
Public map[string]any
Error error
}
// Template provides templating for converting Base to Error.
// This is useful where the public payload is populated with fields that
// should be present in the internal error representation.
func (b Base) Template(pattern string, opts ...TemplateOpt) (Template, error) {
tmpl, err := template.New(b.messageID + "~private").Parse(pattern)
if err != nil {
return Template{}, err
}
t := Template{
Base: b,
logTemplate: tmpl,
}
for _, o := range opts {
t, err = o(t)
if err != nil {
return Template{}, err
}
}
return t, nil
}
type TemplateOpt func(Template) (Template, error)
// MustTemplate panics if the template for Template cannot be compiled.
//
// Only useful for global or package level initialization of [Template].
func (b Base) MustTemplate(pattern string, opts ...TemplateOpt) Template {
res, err := b.Template(pattern, opts...)
if err != nil {
panic(err)
}
return res
}
// WithPublic provides templating for the user facing error message based
// on only the fields available in TemplateData.Public.
//
// Used as a functional option to [Base.Template].
func WithPublic(pattern string) TemplateOpt {
return func(t Template) (Template, error) {
var err error
t.publicTemplate, err = template.New(t.Base.messageID + "~public").Parse(pattern)
return t, err
}
}
// WithPublicFromLog copies over the template for the log message to be
// used for the user facing error message.
// TemplateData.Error and TemplateData.Private will not be populated
// when rendering the public message.
//
// Used as a functional option to [Base.Template].
func WithPublicFromLog() TemplateOpt {
return func(t Template) (Template, error) {
t.publicTemplate = t.logTemplate
return t, nil
}
}
// Build returns a new [Error] based on the base [Template] and the
// provided [TemplateData], wrapping the error in TemplateData.Error.
//
// Build can fail and return an error that is not of type Error.
func (t Template) Build(data TemplateData) error {
if t.logTemplate == nil {
return fmt.Errorf("cannot initialize error using missing template")
}
buf := bytes.Buffer{}
err := t.logTemplate.Execute(&buf, data)
if err != nil {
return err
}
pubBuf := bytes.Buffer{}
if t.publicTemplate != nil {
err := t.publicTemplate.Execute(&pubBuf, TemplateData{Public: data.Public})
if err != nil {
return err
}
}
e := t.Base.Errorf("%s", buf.String())
e.PublicMessage = pubBuf.String()
e.PublicPayload = data.Public
e.Underlying = data.Error
return e
}
func (t Template) Error() string {
return t.Base.Error()
}