mirror of
https://blitiri.com.ar/repos/chasquid
synced 2026-10-29 15:02:30 +00:00
7a30982bf0
Today, trace.Printf and trace.Debugf format the message more than once: at least once for logging, and once for tracing. Some of that formatting can be expensive for large messages, causing CPU and RAM spikes due to transient allocations. So this patch makes those trace functions only format once, and avoid the logging calls if they're below the logging level.
94 lines
2.2 KiB
Go
94 lines
2.2 KiB
Go
// Package trace extends nettrace with logging.
|
|
package trace
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"blitiri.com.ar/go/chasquid/internal/nettrace"
|
|
"blitiri.com.ar/go/log"
|
|
)
|
|
|
|
func init() {
|
|
}
|
|
|
|
// A Trace represents an active request.
|
|
type Trace struct {
|
|
family string
|
|
title string
|
|
t nettrace.Trace
|
|
}
|
|
|
|
// New trace.
|
|
func New(family, title string) *Trace {
|
|
t := &Trace{family, title, nettrace.New(family, title)}
|
|
|
|
// The default for max events is 10, which is a bit short for a normal
|
|
// SMTP exchange. Expand it to 100 which should be large enough to keep
|
|
// most of the traces.
|
|
t.t.SetMaxEvents(100)
|
|
return t
|
|
}
|
|
|
|
// NewChild creates a new child trace.
|
|
func (t *Trace) NewChild(family, title string) *Trace {
|
|
n := &Trace{family, title, t.t.NewChild(family, title)}
|
|
n.t.SetMaxEvents(100)
|
|
return n
|
|
}
|
|
|
|
// Printf adds this message to the trace's log.
|
|
func (t *Trace) Printf(format string, a ...any) {
|
|
t.printf(log.Info, format, a...)
|
|
}
|
|
|
|
// Debugf adds this message to the trace's log, with a debugging level.
|
|
func (t *Trace) Debugf(format string, a ...any) {
|
|
t.printf(log.Debug, format, a...)
|
|
}
|
|
|
|
func (t *Trace) printf(level log.Level, format string, a ...any) {
|
|
// Format the message only once, as it can be expensive.
|
|
msg := fmt.Sprintf(format, a...)
|
|
t.t.Printf("%s", msg)
|
|
|
|
// Only quote and log the message if it's going to be logged.
|
|
if log.V(level) {
|
|
log.Log(level, 2, "%s %s: %s", t.family, t.title, quote(msg))
|
|
}
|
|
}
|
|
|
|
// Errorf adds this message to the trace's log, with an error level.
|
|
func (t *Trace) Errorf(format string, a ...any) error {
|
|
// Note we can't just call t.Error here, as it breaks caller logging.
|
|
err := fmt.Errorf(format, a...)
|
|
t.t.SetError()
|
|
t.t.Printf("error: %v", err)
|
|
|
|
log.Log(log.Info, 1, "%s %s: error: %s", t.family, t.title,
|
|
quote(err.Error()))
|
|
return err
|
|
}
|
|
|
|
// Error marks the trace as having seen an error, and also logs it to the
|
|
// trace's log.
|
|
func (t *Trace) Error(err error) error {
|
|
t.t.SetError()
|
|
t.t.Printf("error: %v", err)
|
|
|
|
log.Log(log.Info, 1, "%s %s: error: %s", t.family, t.title,
|
|
quote(err.Error()))
|
|
|
|
return err
|
|
}
|
|
|
|
// Finish the trace. It should not be changed after this is called.
|
|
func (t *Trace) Finish() {
|
|
t.t.Finish()
|
|
}
|
|
|
|
func quote(s string) string {
|
|
qs := strconv.Quote(s)
|
|
return qs[1 : len(qs)-1]
|
|
}
|