Add custom logger with source location tracking and remove verbose database logs

- Create internal/logger package with Logger wrapper around slog
- Logger automatically adds source file, line number, and function name to all log entries
- Use golang.org/x/term to properly detect if stdout is a terminal
- Replace all slog.Logger usage with logger.Logger throughout the codebase
- Remove verbose logging from database GetStats() method
- Update all constructors and dependencies to use the new logger
This commit is contained in:
Jeffrey Paul 2025-07-28 01:14:51 +02:00
parent 3f06955214
commit 67f6b78aaa
22 changed files with 212 additions and 97 deletions

View File

@ -9,16 +9,14 @@ import (
"os/signal" "os/signal"
"syscall" "syscall"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/metrics" "git.eeqj.de/sneak/routewatch/internal/metrics"
"git.eeqj.de/sneak/routewatch/internal/streamer" "git.eeqj.de/sneak/routewatch/internal/streamer"
"log/slog"
) )
func main() { func main() {
// Set up logger to only show errors // Set up logger
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ logger := logger.New()
Level: slog.LevelError,
}))
// Create metrics tracker // Create metrics tracker
metricsTracker := metrics.New() metricsTracker := metrics.New()

1
go.mod
View File

@ -15,4 +15,5 @@ require (
go.uber.org/multierr v1.10.0 // indirect go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.26.0 // indirect go.uber.org/zap v1.26.0 // indirect
golang.org/x/sys v0.34.0 // indirect golang.org/x/sys v0.34.0 // indirect
golang.org/x/term v0.33.0 // indirect
) )

2
go.sum
View File

@ -24,5 +24,7 @@ go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -5,12 +5,12 @@ import (
"database/sql" "database/sql"
_ "embed" _ "embed"
"fmt" "fmt"
"log/slog"
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
"git.eeqj.de/sneak/routewatch/internal/config" "git.eeqj.de/sneak/routewatch/internal/config"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/pkg/asinfo" "git.eeqj.de/sneak/routewatch/pkg/asinfo"
"github.com/google/uuid" "github.com/google/uuid"
_ "github.com/mattn/go-sqlite3" // CGO SQLite driver _ "github.com/mattn/go-sqlite3" // CGO SQLite driver
@ -24,12 +24,12 @@ const dirPermissions = 0750 // rwxr-x---
// Database manages the SQLite database connection and operations. // Database manages the SQLite database connection and operations.
type Database struct { type Database struct {
db *sql.DB db *sql.DB
logger *slog.Logger logger *logger.Logger
path string path string
} }
// New creates a new database connection and initializes the schema. // New creates a new database connection and initializes the schema.
func New(cfg *config.Config, logger *slog.Logger) (*Database, error) { func New(cfg *config.Config, logger *logger.Logger) (*Database, error) {
dbPath := filepath.Join(cfg.GetStateDir(), "db.sqlite") dbPath := filepath.Join(cfg.GetStateDir(), "db.sqlite")
// Log database path // Log database path
@ -348,28 +348,24 @@ func (d *Database) GetStats() (Stats, error) {
var stats Stats var stats Stats
// Count ASNs // Count ASNs
d.logger.Info("Counting ASNs")
err := d.queryRow("SELECT COUNT(*) FROM asns").Scan(&stats.ASNs) err := d.queryRow("SELECT COUNT(*) FROM asns").Scan(&stats.ASNs)
if err != nil { if err != nil {
return stats, err return stats, err
} }
// Count prefixes // Count prefixes
d.logger.Info("Counting prefixes")
err = d.queryRow("SELECT COUNT(*) FROM prefixes").Scan(&stats.Prefixes) err = d.queryRow("SELECT COUNT(*) FROM prefixes").Scan(&stats.Prefixes)
if err != nil { if err != nil {
return stats, err return stats, err
} }
// Count IPv4 and IPv6 prefixes // Count IPv4 and IPv6 prefixes
d.logger.Info("Counting IPv4 prefixes")
const ipVersionV4 = 4 const ipVersionV4 = 4
err = d.queryRow("SELECT COUNT(*) FROM prefixes WHERE ip_version = ?", ipVersionV4).Scan(&stats.IPv4Prefixes) err = d.queryRow("SELECT COUNT(*) FROM prefixes WHERE ip_version = ?", ipVersionV4).Scan(&stats.IPv4Prefixes)
if err != nil { if err != nil {
return stats, err return stats, err
} }
d.logger.Info("Counting IPv6 prefixes")
const ipVersionV6 = 6 const ipVersionV6 = 6
err = d.queryRow("SELECT COUNT(*) FROM prefixes WHERE ip_version = ?", ipVersionV6).Scan(&stats.IPv6Prefixes) err = d.queryRow("SELECT COUNT(*) FROM prefixes WHERE ip_version = ?", ipVersionV6).Scan(&stats.IPv6Prefixes)
if err != nil { if err != nil {
@ -377,14 +373,12 @@ func (d *Database) GetStats() (Stats, error) {
} }
// Count peerings // Count peerings
d.logger.Info("Counting peerings")
err = d.queryRow("SELECT COUNT(*) FROM asn_peerings").Scan(&stats.Peerings) err = d.queryRow("SELECT COUNT(*) FROM asn_peerings").Scan(&stats.Peerings)
if err != nil { if err != nil {
return stats, err return stats, err
} }
// Get database file size // Get database file size
d.logger.Info("Getting database file size")
fileInfo, err := os.Stat(d.path) fileInfo, err := os.Stat(d.path)
if err != nil { if err != nil {
d.logger.Warn("Failed to get database file size", "error", err) d.logger.Warn("Failed to get database file size", "error", err)
@ -393,7 +387,5 @@ func (d *Database) GetStats() (Stats, error) {
stats.FileSizeBytes = fileInfo.Size() stats.FileSizeBytes = fileInfo.Size()
} }
d.logger.Info("Stats collection complete")
return stats, nil return stats, nil
} }

View File

@ -3,14 +3,15 @@ package database
import ( import (
"context" "context"
"database/sql" "database/sql"
"log/slog"
"time" "time"
"git.eeqj.de/sneak/routewatch/internal/logger"
) )
const slowQueryThreshold = 50 * time.Millisecond const slowQueryThreshold = 50 * time.Millisecond
// logSlowQuery logs queries that take longer than slowQueryThreshold // logSlowQuery logs queries that take longer than slowQueryThreshold
func logSlowQuery(logger *slog.Logger, query string, start time.Time) { func logSlowQuery(logger *logger.Logger, query string, start time.Time) {
elapsed := time.Since(start) elapsed := time.Since(start)
if elapsed > slowQueryThreshold { if elapsed > slowQueryThreshold {
logger.Debug("Slow query", "query", query, "duration", elapsed) logger.Debug("Slow query", "query", query, "duration", elapsed)
@ -47,7 +48,7 @@ func (d *Database) exec(query string, args ...interface{}) error {
// loggingTx wraps sql.Tx to log slow queries // loggingTx wraps sql.Tx to log slow queries
type loggingTx struct { type loggingTx struct {
*sql.Tx *sql.Tx
logger *slog.Logger logger *logger.Logger
} }
// QueryRow wraps sql.Tx.QueryRow to log slow queries // QueryRow wraps sql.Tx.QueryRow to log slow queries

150
internal/logger/logger.go Normal file
View File

@ -0,0 +1,150 @@
// Package logger provides a structured logger with source location tracking
package logger
import (
"log/slog"
"os"
"path/filepath"
"runtime"
"strings"
"golang.org/x/term"
)
// Logger wraps slog.Logger to add source location information
type Logger struct {
*slog.Logger
}
// AsSlog returns the underlying slog.Logger
func (l *Logger) AsSlog() *slog.Logger {
return l.Logger
}
// New creates a new logger with appropriate handler based on environment
func New() *Logger {
level := slog.LevelInfo
if debug := os.Getenv("DEBUG"); strings.Contains(debug, "routewatch") {
level = slog.LevelDebug
}
opts := &slog.HandlerOptions{
Level: level,
}
var handler slog.Handler
if term.IsTerminal(int(os.Stdout.Fd())) {
// Terminal, use text
handler = slog.NewTextHandler(os.Stdout, opts)
} else {
// Not a terminal, use JSON
handler = slog.NewJSONHandler(os.Stdout, opts)
}
return &Logger{Logger: slog.New(handler)}
}
const sourceSkipLevel = 2 // Skip levels for source location tracking
// getSourceAttrs returns attributes for the calling source location
func getSourceAttrs() []slog.Attr {
pc, file, line, ok := runtime.Caller(sourceSkipLevel)
if !ok {
return nil
}
// Get just the filename without the full path
file = filepath.Base(file)
// Get the function name
fn := runtime.FuncForPC(pc)
var funcName string
if fn != nil {
funcName = filepath.Base(fn.Name())
}
attrs := []slog.Attr{
slog.String("source", file),
slog.Int("line", line),
}
if funcName != "" {
attrs = append(attrs, slog.String("func", funcName))
}
return attrs
}
// Debug logs at debug level with source location
func (l *Logger) Debug(msg string, args ...any) {
sourceAttrs := getSourceAttrs()
allArgs := make([]any, 0, len(args)+len(sourceAttrs)*2)
// Add source attributes first
for _, attr := range sourceAttrs {
allArgs = append(allArgs, attr)
}
// Add user args
allArgs = append(allArgs, args...)
l.Logger.Debug(msg, allArgs...)
}
// Info logs at info level with source location
func (l *Logger) Info(msg string, args ...any) {
sourceAttrs := getSourceAttrs()
allArgs := make([]any, 0, len(args)+len(sourceAttrs)*2)
// Add source attributes first
for _, attr := range sourceAttrs {
allArgs = append(allArgs, attr)
}
// Add user args
allArgs = append(allArgs, args...)
l.Logger.Info(msg, allArgs...)
}
// Warn logs at warn level with source location
func (l *Logger) Warn(msg string, args ...any) {
sourceAttrs := getSourceAttrs()
allArgs := make([]any, 0, len(args)+len(sourceAttrs)*2)
// Add source attributes first
for _, attr := range sourceAttrs {
allArgs = append(allArgs, attr)
}
// Add user args
allArgs = append(allArgs, args...)
l.Logger.Warn(msg, allArgs...)
}
// Error logs at error level with source location
func (l *Logger) Error(msg string, args ...any) {
sourceAttrs := getSourceAttrs()
allArgs := make([]any, 0, len(args)+len(sourceAttrs)*2)
// Add source attributes first
for _, attr := range sourceAttrs {
allArgs = append(allArgs, attr)
}
// Add user args
allArgs = append(allArgs, args...)
l.Logger.Error(msg, allArgs...)
}
// With returns a new logger with additional attributes
func (l *Logger) With(args ...any) *Logger {
return &Logger{Logger: l.Logger.With(args...)}
}
// WithGroup returns a new logger with a group prefix
func (l *Logger) WithGroup(name string) *Logger {
return &Logger{Logger: l.Logger.WithGroup(name)}
}

View File

@ -5,14 +5,13 @@ package routewatch
import ( import (
"context" "context"
"fmt" "fmt"
"log/slog"
"os" "os"
"strings"
"sync" "sync"
"time" "time"
"git.eeqj.de/sneak/routewatch/internal/config" "git.eeqj.de/sneak/routewatch/internal/config"
"git.eeqj.de/sneak/routewatch/internal/database" "git.eeqj.de/sneak/routewatch/internal/database"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/metrics" "git.eeqj.de/sneak/routewatch/internal/metrics"
"git.eeqj.de/sneak/routewatch/internal/routingtable" "git.eeqj.de/sneak/routewatch/internal/routingtable"
"git.eeqj.de/sneak/routewatch/internal/server" "git.eeqj.de/sneak/routewatch/internal/server"
@ -35,7 +34,7 @@ type Dependencies struct {
RoutingTable *routingtable.RoutingTable RoutingTable *routingtable.RoutingTable
Streamer *streamer.Streamer Streamer *streamer.Streamer
Server *server.Server Server *server.Server
Logger *slog.Logger Logger *logger.Logger
Config *config.Config Config *config.Config
} }
@ -46,7 +45,7 @@ type RouteWatch struct {
streamer *streamer.Streamer streamer *streamer.Streamer
server *server.Server server *server.Server
snapshotter *snapshotter.Snapshotter snapshotter *snapshotter.Snapshotter
logger *slog.Logger logger *logger.Logger
maxRuntime time.Duration maxRuntime time.Duration
shutdown bool shutdown bool
mu sync.Mutex mu sync.Mutex
@ -229,34 +228,11 @@ func (rw *RouteWatch) logRoutingTableStats(ctx context.Context) {
} }
} }
// NewLogger creates a structured logger
func NewLogger() *slog.Logger {
level := slog.LevelInfo
if debug := os.Getenv("DEBUG"); strings.Contains(debug, "routewatch") {
level = slog.LevelDebug
}
opts := &slog.HandlerOptions{
Level: level,
}
var handler slog.Handler
if os.Stdout.Name() != "/dev/stdout" || os.Getenv("TERM") == "" {
// Not a terminal, use JSON
handler = slog.NewJSONHandler(os.Stdout, opts)
} else {
// Terminal, use text
handler = slog.NewTextHandler(os.Stdout, opts)
}
return slog.New(handler)
}
// getModule provides all fx dependencies // getModule provides all fx dependencies
func getModule() fx.Option { func getModule() fx.Option {
return fx.Options( return fx.Options(
fx.Provide( fx.Provide(
NewLogger, logger.New,
config.New, config.New,
metrics.New, metrics.New,
fx.Annotate( fx.Annotate(

View File

@ -9,6 +9,7 @@ import (
"git.eeqj.de/sneak/routewatch/internal/config" "git.eeqj.de/sneak/routewatch/internal/config"
"git.eeqj.de/sneak/routewatch/internal/database" "git.eeqj.de/sneak/routewatch/internal/database"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/metrics" "git.eeqj.de/sneak/routewatch/internal/metrics"
"git.eeqj.de/sneak/routewatch/internal/routingtable" "git.eeqj.de/sneak/routewatch/internal/routingtable"
"git.eeqj.de/sneak/routewatch/internal/server" "git.eeqj.de/sneak/routewatch/internal/server"
@ -164,7 +165,7 @@ func TestRouteWatchLiveFeed(t *testing.T) {
mockDB := newMockStore() mockDB := newMockStore()
defer mockDB.Close() defer mockDB.Close()
logger := NewLogger() logger := logger.New()
// Create metrics tracker // Create metrics tracker
metricsTracker := metrics.New() metricsTracker := metrics.New()

View File

@ -1,12 +1,3 @@
package routewatch package routewatch
import ( // Tests for routewatch package are in app_integration_test.go
"testing"
)
func TestNewLogger(t *testing.T) {
logger := NewLogger()
if logger == nil {
t.Fatal("NewLogger returned nil")
}
}

View File

@ -2,12 +2,12 @@ package routewatch
import ( import (
"context" "context"
"log/slog"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
"time" "time"
"git.eeqj.de/sneak/routewatch/internal/logger"
"go.uber.org/fx" "go.uber.org/fx"
) )
@ -21,7 +21,7 @@ func CLIEntry() {
app := fx.New( app := fx.New(
getModule(), getModule(),
fx.StopTimeout(shutdownTimeout), // Allow 60 seconds for graceful shutdown fx.StopTimeout(shutdownTimeout), // Allow 60 seconds for graceful shutdown
fx.Invoke(func(lc fx.Lifecycle, rw *RouteWatch, logger *slog.Logger) { fx.Invoke(func(lc fx.Lifecycle, rw *RouteWatch, logger *logger.Logger) {
lc.Append(fx.Hook{ lc.Append(fx.Hook{
OnStart: func(_ context.Context) error { OnStart: func(_ context.Context) error {
go func() { go func() {

View File

@ -1,9 +1,8 @@
package routewatch package routewatch
import ( import (
"log/slog"
"git.eeqj.de/sneak/routewatch/internal/database" "git.eeqj.de/sneak/routewatch/internal/database"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/ristypes" "git.eeqj.de/sneak/routewatch/internal/ristypes"
) )
@ -15,13 +14,13 @@ const (
// DatabaseHandler handles BGP messages and stores them in the database // DatabaseHandler handles BGP messages and stores them in the database
type DatabaseHandler struct { type DatabaseHandler struct {
db database.Store db database.Store
logger *slog.Logger logger *logger.Logger
} }
// NewDatabaseHandler creates a new database handler // NewDatabaseHandler creates a new database handler
func NewDatabaseHandler( func NewDatabaseHandler(
db database.Store, db database.Store,
logger *slog.Logger, logger *logger.Logger,
) *DatabaseHandler { ) *DatabaseHandler {
return &DatabaseHandler{ return &DatabaseHandler{
db: db, db: db,

View File

@ -1,11 +1,11 @@
package routewatch package routewatch
import ( import (
"log/slog"
"sync" "sync"
"time" "time"
"git.eeqj.de/sneak/routewatch/internal/database" "git.eeqj.de/sneak/routewatch/internal/database"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/ristypes" "git.eeqj.de/sneak/routewatch/internal/ristypes"
) )
@ -23,7 +23,7 @@ const (
// BatchedDatabaseHandler handles BGP messages and stores them in the database using batched operations // BatchedDatabaseHandler handles BGP messages and stores them in the database using batched operations
type BatchedDatabaseHandler struct { type BatchedDatabaseHandler struct {
db database.Store db database.Store
logger *slog.Logger logger *logger.Logger
// Batching // Batching
mu sync.Mutex mu sync.Mutex
@ -54,7 +54,7 @@ type peeringOp struct {
// NewBatchedDatabaseHandler creates a new batched database handler // NewBatchedDatabaseHandler creates a new batched database handler
func NewBatchedDatabaseHandler( func NewBatchedDatabaseHandler(
db database.Store, db database.Store,
logger *slog.Logger, logger *logger.Logger,
) *BatchedDatabaseHandler { ) *BatchedDatabaseHandler {
h := &BatchedDatabaseHandler{ h := &BatchedDatabaseHandler{
db: db, db: db,

View File

@ -1,19 +1,23 @@
package routewatch package routewatch
import ( import (
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/ristypes" "git.eeqj.de/sneak/routewatch/internal/ristypes"
"log/slog"
) )
// SimpleHandler is a basic implementation of streamer.MessageHandler // SimpleHandler is a basic implementation of streamer.MessageHandler
type SimpleHandler struct { type SimpleHandler struct {
logger *slog.Logger logger *logger.Logger
messageTypes []string messageTypes []string
callback func(*ristypes.RISMessage) callback func(*ristypes.RISMessage)
} }
// NewSimpleHandler creates a handler that accepts specific message types // NewSimpleHandler creates a handler that accepts specific message types
func NewSimpleHandler(logger *slog.Logger, messageTypes []string, callback func(*ristypes.RISMessage)) *SimpleHandler { func NewSimpleHandler(
logger *logger.Logger,
messageTypes []string,
callback func(*ristypes.RISMessage),
) *SimpleHandler {
return &SimpleHandler{ return &SimpleHandler{
logger: logger, logger: logger,
messageTypes: messageTypes, messageTypes: messageTypes,

View File

@ -1,10 +1,10 @@
package routewatch package routewatch
import ( import (
"log/slog"
"strconv" "strconv"
"git.eeqj.de/sneak/routewatch/internal/database" "git.eeqj.de/sneak/routewatch/internal/database"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/ristypes" "git.eeqj.de/sneak/routewatch/internal/ristypes"
) )
@ -16,11 +16,11 @@ const (
// PeerHandler tracks BGP peers from all message types // PeerHandler tracks BGP peers from all message types
type PeerHandler struct { type PeerHandler struct {
db database.Store db database.Store
logger *slog.Logger logger *logger.Logger
} }
// NewPeerHandler creates a new peer tracking handler // NewPeerHandler creates a new peer tracking handler
func NewPeerHandler(db database.Store, logger *slog.Logger) *PeerHandler { func NewPeerHandler(db database.Store, logger *logger.Logger) *PeerHandler {
return &PeerHandler{ return &PeerHandler{
db: db, db: db,
logger: logger, logger: logger,

View File

@ -1,12 +1,12 @@
package routewatch package routewatch
import ( import (
"log/slog"
"strconv" "strconv"
"sync" "sync"
"time" "time"
"git.eeqj.de/sneak/routewatch/internal/database" "git.eeqj.de/sneak/routewatch/internal/database"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/ristypes" "git.eeqj.de/sneak/routewatch/internal/ristypes"
) )
@ -24,7 +24,7 @@ const (
// BatchedPeerHandler tracks BGP peers from all message types using batched operations // BatchedPeerHandler tracks BGP peers from all message types using batched operations
type BatchedPeerHandler struct { type BatchedPeerHandler struct {
db database.Store db database.Store
logger *slog.Logger logger *logger.Logger
// Batching // Batching
mu sync.Mutex mu sync.Mutex
@ -42,7 +42,7 @@ type peerUpdate struct {
} }
// NewBatchedPeerHandler creates a new batched peer tracking handler // NewBatchedPeerHandler creates a new batched peer tracking handler
func NewBatchedPeerHandler(db database.Store, logger *slog.Logger) *BatchedPeerHandler { func NewBatchedPeerHandler(db database.Store, logger *logger.Logger) *BatchedPeerHandler {
h := &BatchedPeerHandler{ h := &BatchedPeerHandler{
db: db, db: db,
logger: logger, logger: logger,

View File

@ -1,9 +1,9 @@
package routewatch package routewatch
import ( import (
"log/slog"
"strconv" "strconv"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/ristypes" "git.eeqj.de/sneak/routewatch/internal/ristypes"
"git.eeqj.de/sneak/routewatch/internal/routingtable" "git.eeqj.de/sneak/routewatch/internal/routingtable"
"github.com/google/uuid" "github.com/google/uuid"
@ -17,11 +17,11 @@ const (
// RoutingTableHandler handles BGP messages and updates the in-memory routing table // RoutingTableHandler handles BGP messages and updates the in-memory routing table
type RoutingTableHandler struct { type RoutingTableHandler struct {
rt *routingtable.RoutingTable rt *routingtable.RoutingTable
logger *slog.Logger logger *logger.Logger
} }
// NewRoutingTableHandler creates a new routing table handler // NewRoutingTableHandler creates a new routing table handler
func NewRoutingTableHandler(rt *routingtable.RoutingTable, logger *slog.Logger) *RoutingTableHandler { func NewRoutingTableHandler(rt *routingtable.RoutingTable, logger *logger.Logger) *RoutingTableHandler {
return &RoutingTableHandler{ return &RoutingTableHandler{
rt: rt, rt: rt,
logger: logger, logger: logger,

View File

@ -5,7 +5,6 @@ import (
"compress/gzip" "compress/gzip"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -14,6 +13,7 @@ import (
"time" "time"
"git.eeqj.de/sneak/routewatch/internal/config" "git.eeqj.de/sneak/routewatch/internal/config"
"git.eeqj.de/sneak/routewatch/internal/logger"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -68,7 +68,7 @@ type RoutingTable struct {
} }
// New creates a new routing table, loading from snapshot if available // New creates a new routing table, loading from snapshot if available
func New(cfg *config.Config, logger *slog.Logger) *RoutingTable { func New(cfg *config.Config, logger *logger.Logger) *RoutingTable {
rt := &RoutingTable{ rt := &RoutingTable{
routes: make(map[RouteKey]*Route), routes: make(map[RouteKey]*Route),
byPrefix: make(map[uuid.UUID]map[RouteKey]*Route), byPrefix: make(map[uuid.UUID]map[RouteKey]*Route),
@ -452,7 +452,7 @@ func isIPv6(prefix string) bool {
} }
// loadFromSnapshot attempts to load the routing table from a snapshot file // loadFromSnapshot attempts to load the routing table from a snapshot file
func (rt *RoutingTable) loadFromSnapshot(logger *slog.Logger) error { func (rt *RoutingTable) loadFromSnapshot(logger *logger.Logger) error {
// If no snapshot directory specified, nothing to load // If no snapshot directory specified, nothing to load
if rt.snapshotDir == "" { if rt.snapshotDir == "" {
return nil return nil

View File

@ -1,18 +1,18 @@
package routingtable package routingtable
import ( import (
"log/slog"
"sync" "sync"
"testing" "testing"
"time" "time"
"git.eeqj.de/sneak/routewatch/internal/config" "git.eeqj.de/sneak/routewatch/internal/config"
"git.eeqj.de/sneak/routewatch/internal/logger"
"github.com/google/uuid" "github.com/google/uuid"
) )
func TestRoutingTable(t *testing.T) { func TestRoutingTable(t *testing.T) {
// Create a test logger // Create a test logger
logger := slog.Default() logger := logger.New()
// Create test config with empty state dir (no snapshot loading) // Create test config with empty state dir (no snapshot loading)
cfg := &config.Config{ cfg := &config.Config{
@ -129,7 +129,7 @@ func TestRoutingTable(t *testing.T) {
func TestRoutingTableConcurrency(t *testing.T) { func TestRoutingTableConcurrency(t *testing.T) {
// Create a test logger // Create a test logger
logger := slog.Default() logger := logger.New()
// Create test config with empty state dir (no snapshot loading) // Create test config with empty state dir (no snapshot loading)
cfg := &config.Config{ cfg := &config.Config{
@ -189,7 +189,7 @@ func TestRoutingTableConcurrency(t *testing.T) {
func TestRouteUpdate(t *testing.T) { func TestRouteUpdate(t *testing.T) {
// Create a test logger // Create a test logger
logger := slog.Default() logger := logger.New()
// Create test config with empty state dir (no snapshot loading) // Create test config with empty state dir (no snapshot loading)
cfg := &config.Config{ cfg := &config.Config{

View File

@ -4,12 +4,12 @@ package server
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"log/slog"
"net/http" "net/http"
"os" "os"
"time" "time"
"git.eeqj.de/sneak/routewatch/internal/database" "git.eeqj.de/sneak/routewatch/internal/database"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/routingtable" "git.eeqj.de/sneak/routewatch/internal/routingtable"
"git.eeqj.de/sneak/routewatch/internal/streamer" "git.eeqj.de/sneak/routewatch/internal/streamer"
"git.eeqj.de/sneak/routewatch/internal/templates" "git.eeqj.de/sneak/routewatch/internal/templates"
@ -23,12 +23,12 @@ type Server struct {
db database.Store db database.Store
routingTable *routingtable.RoutingTable routingTable *routingtable.RoutingTable
streamer *streamer.Streamer streamer *streamer.Streamer
logger *slog.Logger logger *logger.Logger
srv *http.Server srv *http.Server
} }
// New creates a new HTTP server // New creates a new HTTP server
func New(db database.Store, rt *routingtable.RoutingTable, streamer *streamer.Streamer, logger *slog.Logger) *Server { func New(db database.Store, rt *routingtable.RoutingTable, streamer *streamer.Streamer, logger *logger.Logger) *Server {
s := &Server{ s := &Server{
db: db, db: db,
routingTable: rt, routingTable: rt,

View File

@ -7,7 +7,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog" "git.eeqj.de/sneak/routewatch/internal/logger"
"os" "os"
"path/filepath" "path/filepath"
"sync" "sync"
@ -27,7 +27,7 @@ const (
type Snapshotter struct { type Snapshotter struct {
rt *routingtable.RoutingTable rt *routingtable.RoutingTable
stateDir string stateDir string
logger *slog.Logger logger *logger.Logger
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
mu sync.Mutex // Ensures only one snapshot runs at a time mu sync.Mutex // Ensures only one snapshot runs at a time
@ -36,7 +36,7 @@ type Snapshotter struct {
} }
// New creates a new Snapshotter instance // New creates a new Snapshotter instance
func New(rt *routingtable.RoutingTable, cfg *config.Config, logger *slog.Logger) (*Snapshotter, error) { func New(rt *routingtable.RoutingTable, cfg *config.Config, logger *logger.Logger) (*Snapshotter, error) {
stateDir := cfg.GetStateDir() stateDir := cfg.GetStateDir()
// If state directory is specified, ensure it exists // If state directory is specified, ensure it exists

View File

@ -7,13 +7,13 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"os" "os"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/metrics" "git.eeqj.de/sneak/routewatch/internal/metrics"
"git.eeqj.de/sneak/routewatch/internal/ristypes" "git.eeqj.de/sneak/routewatch/internal/ristypes"
) )
@ -63,7 +63,7 @@ type handlerInfo struct {
// Streamer handles streaming BGP updates from RIS Live // Streamer handles streaming BGP updates from RIS Live
type Streamer struct { type Streamer struct {
logger *slog.Logger logger *logger.Logger
client *http.Client client *http.Client
handlers []*handlerInfo handlers []*handlerInfo
rawHandler RawMessageHandler rawHandler RawMessageHandler
@ -75,7 +75,7 @@ type Streamer struct {
} }
// New creates a new RIS streamer // New creates a new RIS streamer
func New(logger *slog.Logger, metrics *metrics.Tracker) *Streamer { func New(logger *logger.Logger, metrics *metrics.Tracker) *Streamer {
return &Streamer{ return &Streamer{
logger: logger, logger: logger,
client: &http.Client{ client: &http.Client{

View File

@ -3,12 +3,12 @@ package streamer
import ( import (
"testing" "testing"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/metrics" "git.eeqj.de/sneak/routewatch/internal/metrics"
"log/slog"
) )
func TestNewStreamer(t *testing.T) { func TestNewStreamer(t *testing.T) {
logger := slog.Default() logger := logger.New()
metricsTracker := metrics.New() metricsTracker := metrics.New()
s := New(logger, metricsTracker) s := New(logger, metricsTracker)