Make the libvirt trace compact: libvirt API names, one error field, terse test output
The first live run showed the trace working but noisy: testr printed ts/level/msg and the per-run constants on every line, the error text appeared twice, host appeared twice on image-cache lines, and the Go method name was less useful than the libvirt function it maps to. The tracer now logs rpc=virDomainLookupByName (go-libvirt's names are the public API names without the vir prefix), error plus libvirtError (code name) and libvirtCode, and image.go stops re-adding host. The integration test replaces testr with a compact logr sink: time, logger name, the rpc as headline, then fields, with provider/host/name omitted since the first line of the run states them. Docs and execution log updated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -29,10 +29,13 @@ package kvm
|
||||
// one line per libvirt call; 2 adds XML, metadata and
|
||||
// addresses; 0 leaves only the t.Log lines)
|
||||
//
|
||||
// Provider logs go through a testr logger, so they interleave with t.Log
|
||||
// output. A run that hits its timeout may log one more line from the RPC
|
||||
// the pool abandoned, after the test has ended; that is a symptom of the
|
||||
// timeout, not a bug in the tracer.
|
||||
// Provider logs are printed through t.Log in a compact form: time, logger
|
||||
// name, then the libvirt API function (or the message) and its fields.
|
||||
// The provider/host/name values that are the same on every line of a run
|
||||
// are dropped; the first t.Log line of the run states them. A run that
|
||||
// hits its timeout may log one more line from the RPC the pool abandoned,
|
||||
// after the test has ended; that is a symptom of the timeout, not a bug
|
||||
// in the tracer.
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -43,12 +46,12 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
libvirt "github.com/digitalocean/go-libvirt"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/go-logr/logr/testr"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
@@ -127,11 +130,80 @@ func loadIntegrationEnv(t *testing.T) integrationEnv {
|
||||
// print its "SetLogger was never called" warning and swallow the lines).
|
||||
func integrationLogger(t *testing.T, verbosity int) logr.Logger {
|
||||
t.Helper()
|
||||
log := testr.NewWithOptions(t, testr.Options{LogTimestamp: true, Verbosity: verbosity})
|
||||
log := logr.New(&compactSink{t: t, verbosity: verbosity})
|
||||
logf.SetLogger(log)
|
||||
return log
|
||||
}
|
||||
|
||||
// compactSink is a logr sink for test output: one line per record as
|
||||
// "HH:MM:SS.mmm name headline k=v ...", where the headline is the rpc
|
||||
// field when present (the libvirt API function) and the message
|
||||
// otherwise. Keys that are constant for the whole run are omitted.
|
||||
type compactSink struct {
|
||||
t *testing.T
|
||||
verbosity int
|
||||
name string
|
||||
values []any
|
||||
}
|
||||
|
||||
var compactOmit = map[string]bool{"provider": true, "host": true, "name": true}
|
||||
|
||||
func (s *compactSink) Init(logr.RuntimeInfo) {}
|
||||
|
||||
func (s *compactSink) Enabled(level int) bool { return level <= s.verbosity }
|
||||
|
||||
// GetCallStackHelper lets logr mark its frames as helpers, so t.Log
|
||||
// attributes each line to the provider code that logged it.
|
||||
func (s *compactSink) GetCallStackHelper() func() { return s.t.Helper }
|
||||
|
||||
func (s *compactSink) Info(_ int, msg string, kv ...any) {
|
||||
s.t.Helper()
|
||||
s.t.Log(s.format(msg, kv))
|
||||
}
|
||||
|
||||
func (s *compactSink) Error(err error, msg string, kv ...any) {
|
||||
s.t.Helper()
|
||||
s.t.Log(s.format(msg, append(kv, "error", err)))
|
||||
}
|
||||
|
||||
func (s *compactSink) WithValues(kv ...any) logr.LogSink {
|
||||
c := *s
|
||||
c.values = append(append([]any(nil), s.values...), kv...)
|
||||
return &c
|
||||
}
|
||||
|
||||
func (s *compactSink) WithName(name string) logr.LogSink {
|
||||
c := *s
|
||||
if c.name != "" {
|
||||
name = c.name + "/" + name
|
||||
}
|
||||
c.name = name
|
||||
return &c
|
||||
}
|
||||
|
||||
func (s *compactSink) format(msg string, kv []any) string {
|
||||
all := append(append([]any(nil), s.values...), kv...)
|
||||
headline := msg
|
||||
var b strings.Builder
|
||||
for i := 0; i+1 < len(all); i += 2 {
|
||||
key := fmt.Sprint(all[i])
|
||||
if key == "rpc" {
|
||||
headline = fmt.Sprint(all[i+1])
|
||||
continue
|
||||
}
|
||||
if compactOmit[key] {
|
||||
continue
|
||||
}
|
||||
val := fmt.Sprint(all[i+1])
|
||||
if strings.ContainsAny(val, " \t\"=") {
|
||||
val = strconv.Quote(val)
|
||||
}
|
||||
b.WriteString(" " + key + "=" + val)
|
||||
}
|
||||
name := s.name[strings.LastIndex(s.name, "/")+1:]
|
||||
return fmt.Sprintf("%s %s %s%s", time.Now().Format("15:04:05.000"), name, headline, b.String())
|
||||
}
|
||||
|
||||
func randomUID(t *testing.T) string {
|
||||
t.Helper()
|
||||
var b [16]byte
|
||||
|
||||
Reference in New Issue
Block a user