Files
netbird/proxy/internal/accesslog/logger.go
2026-01-26 09:28:46 +00:00

90 lines
2.5 KiB
Go

package accesslog
import (
"context"
"log/slog"
"github.com/netbirdio/netbird/shared/management/proto"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/timestamppb"
)
type gRPCClient interface {
SendAccessLog(ctx context.Context, in *proto.SendAccessLogRequest, opts ...grpc.CallOption) (*proto.SendAccessLogResponse, error)
}
type errorLogger interface {
ErrorContext(ctx context.Context, msg string, args ...any)
}
type Logger struct {
client gRPCClient
errorLog errorLogger
}
func NewLogger(client gRPCClient, errorLog errorLogger) *Logger {
if errorLog == nil {
errorLog = slog.New(slog.DiscardHandler)
}
return &Logger{
client: client,
errorLog: errorLog,
}
}
type logEntry struct {
ServiceId string
Host string
Path string
DurationMs int64
Method string
ResponseCode int32
SourceIp string
AuthMechanism string
UserId string
AuthSuccess bool
}
func (l *Logger) log(ctx context.Context, log logEntry) {
// Fire off the log request in a separate routine.
// This increases the possibility of losing a log message
// (although it should still get logged in the event of an error),
// but it will reduce latency returning the request in the
// middleware.
// There is also a chance that log messages will arrive at
// the server out of order; however, the timestamp should
// allow for resolving that on the server.
now := timestamppb.Now() // Grab the timestamp before launching the goroutine to try to prevent weird timing issues. This is probably unnecessary.
go func() {
if _, err := l.client.SendAccessLog(ctx, &proto.SendAccessLogRequest{
Log: &proto.AccessLog{
Timestamp: now,
ServiceId: log.ServiceId,
Host: log.Host,
Path: log.Path,
DurationMs: log.DurationMs,
Method: log.Method,
ResponseCode: log.ResponseCode,
SourceIp: log.SourceIp,
AuthMechanism: log.AuthMechanism,
UserId: log.UserId,
AuthSuccess: log.AuthSuccess,
},
}); err != nil {
// If it fails to send on the gRPC connection, then at least log it to the error log.
l.errorLog.ErrorContext(ctx, "Error sending access log on gRPC connection",
"service_id", log.ServiceId,
"host", log.Host,
"path", log.Path,
"duration", log.DurationMs,
"method", log.Method,
"response_code", log.ResponseCode,
"source_ip", log.SourceIp,
"auth_mechanism", log.AuthMechanism,
"user_id", log.UserId,
"auth_success", log.AuthSuccess,
"error", err)
}
}()
}