Files
vpp-maglev/internal/vpp/apilog.go

133 lines
3.3 KiB
Go

// SPDX-License-Identifier: Apache-2.0
package vpp
import (
"fmt"
"log/slog"
"go.fd.io/govpp/api"
"git.ipng.ch/ipng/vpp-maglev/internal/metrics"
)
// loggedChannel wraps an api.Channel so that every VPP request/reply is
// recorded via slog at DEBUG level. All code in this package MUST send VPP
// messages through a loggedChannel (via Client.apiChannel) so we have a
// complete audit trail of what was sent to the dataplane.
type loggedChannel struct {
ch api.Channel
}
// apiChannel opens a new API channel wrapped in logging. This is the only
// approved way to talk to VPP; do not call conn.NewAPIChannel directly.
func (c *Client) apiChannel() (*loggedChannel, error) {
c.mu.Lock()
conn := c.apiConn
c.mu.Unlock()
if conn == nil {
return nil, errNotConnected
}
ch, err := conn.NewAPIChannel()
if err != nil {
return nil, err
}
return &loggedChannel{ch: ch}, nil
}
// Close closes the underlying channel.
func (lc *loggedChannel) Close() { lc.ch.Close() }
// SendRequest logs the outgoing message and returns a wrapped request context.
func (lc *loggedChannel) SendRequest(msg api.Message) *loggedRequestCtx {
name := msg.GetMessageName()
slog.Debug("vpp-api-send",
"msg", name,
"crc", msg.GetCrcString(),
"payload", fmt.Sprintf("%+v", msg),
)
metrics.VPPAPITotal.WithLabelValues(name, "send", "success").Inc()
return &loggedRequestCtx{
ctx: lc.ch.SendRequest(msg),
name: name,
}
}
// SendMultiRequest logs the outgoing message and returns a wrapped multi-request context.
func (lc *loggedChannel) SendMultiRequest(msg api.Message) *loggedMultiRequestCtx {
name := msg.GetMessageName()
slog.Debug("vpp-api-send-multi",
"msg", name,
"crc", msg.GetCrcString(),
"payload", fmt.Sprintf("%+v", msg),
)
metrics.VPPAPITotal.WithLabelValues(name, "send", "success").Inc()
return &loggedMultiRequestCtx{
ctx: lc.ch.SendMultiRequest(msg),
name: name,
}
}
// loggedRequestCtx wraps api.RequestCtx and logs the reply on ReceiveReply.
type loggedRequestCtx struct {
ctx api.RequestCtx
name string
}
func (r *loggedRequestCtx) ReceiveReply(msg api.Message) error {
err := r.ctx.ReceiveReply(msg)
if err != nil {
slog.Debug("vpp-api-recv",
"req", r.name,
"reply", msg.GetMessageName(),
"err", err,
)
metrics.VPPAPITotal.WithLabelValues(r.name, "recv", "failure").Inc()
return err
}
slog.Debug("vpp-api-recv",
"req", r.name,
"reply", msg.GetMessageName(),
"payload", fmt.Sprintf("%+v", msg),
)
metrics.VPPAPITotal.WithLabelValues(r.name, "recv", "success").Inc()
return nil
}
// loggedMultiRequestCtx wraps api.MultiRequestCtx and logs each reply.
type loggedMultiRequestCtx struct {
ctx api.MultiRequestCtx
name string
seq int
}
func (r *loggedMultiRequestCtx) ReceiveReply(msg api.Message) (bool, error) {
stop, err := r.ctx.ReceiveReply(msg)
if err != nil {
slog.Debug("vpp-api-recv-multi",
"req", r.name,
"reply", msg.GetMessageName(),
"seq", r.seq,
"err", err,
)
metrics.VPPAPITotal.WithLabelValues(r.name, "recv", "failure").Inc()
return stop, err
}
if stop {
slog.Debug("vpp-api-recv-multi-done",
"req", r.name,
"count", r.seq,
)
return stop, nil
}
slog.Debug("vpp-api-recv-multi",
"req", r.name,
"reply", msg.GetMessageName(),
"seq", r.seq,
"payload", fmt.Sprintf("%+v", msg),
)
metrics.VPPAPITotal.WithLabelValues(r.name, "recv", "success").Inc()
r.seq++
return stop, nil
}