From 052a7dc074441863c68a6369dee2bfaad9f9185a Mon Sep 17 00:00:00 2001 From: Silberengel Date: Sun, 15 Feb 2026 17:55:40 +0100 Subject: [PATCH] implement logger --- cmd/server/main.go | 26 +++++--- go.mod | 1 + go.sum | 3 + internal/logger/logger.go | 101 +++++++++++++++++++++++++++++++ internal/nostr/client.go | 47 +++++++++++---- internal/nostr/ebooks.go | 16 +++-- internal/nostr/feed.go | 27 +++++++-- internal/nostr/profile.go | 8 ++- internal/nostr/wiki.go | 115 ++++++++++++++++++++++++++++-------- internal/server/handlers.go | 20 ++++--- internal/server/server.go | 10 ++-- 11 files changed, 302 insertions(+), 72 deletions(-) create mode 100644 internal/logger/logger.go diff --git a/cmd/server/main.go b/cmd/server/main.go index fd8b68e..5bc7e94 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -3,12 +3,12 @@ package main import ( "context" "flag" - "log" "time" "gitcitadel-online/internal/cache" "gitcitadel-online/internal/config" "gitcitadel-online/internal/generator" + "gitcitadel-online/internal/logger" "gitcitadel-online/internal/nostr" "gitcitadel-online/internal/server" ) @@ -16,20 +16,28 @@ import ( func main() { configPath := flag.String("config", "config.yaml", "Path to configuration file") devMode := flag.Bool("dev", false, "Enable development mode") + logLevel := flag.String("log-level", "info", "Log level (debug, info, warn, error)") flag.Parse() + // Initialize logger + if *devMode { + logger.Init("debug", true) + } else { + logger.Init(*logLevel, false) + } + // Load configuration cfg, err := config.LoadConfig(*configPath) if err != nil { - log.Fatalf("Failed to load config: %v", err) + logger.Fatalf("Failed to load config: %v", err) } if err := cfg.Validate(); err != nil { - log.Fatalf("Invalid config: %v", err) + logger.Fatalf("Invalid config: %v", err) } if *devMode { - log.Println("Development mode enabled") + logger.Info("Development mode enabled") } // Initialize caches @@ -40,7 +48,7 @@ func main() { nostrClient := nostr.NewClient(cfg.Relays.Primary, cfg.Relays.Fallback, cfg.Relays.AdditionalFallback) ctx := context.Background() if err := nostrClient.Connect(ctx); err != nil { - log.Printf("Warning: Failed to connect to relays: %v", err) + logger.Warnf("Failed to connect to relays: %v", err) } // Initialize services @@ -65,7 +73,7 @@ func main() { nostrClient, ) if err != nil { - log.Fatalf("Failed to initialize HTML generator: %v", err) + logger.Fatalf("Failed to initialize HTML generator: %v", err) } // Initialize cache rewarming @@ -93,12 +101,12 @@ func main() { // Start server in goroutine go func() { if err := httpServer.Start(); err != nil { - log.Fatalf("Server failed: %v", err) + logger.Fatalf("Server failed: %v", err) } }() - log.Printf("Server started on port %d", cfg.Server.Port) - log.Println("Waiting for initial cache population...") + logger.Infof("Server started on port %d", cfg.Server.Port) + logger.Info("Waiting for initial cache population...") // Wait a bit for initial cache time.Sleep(5 * time.Second) diff --git a/go.mod b/go.mod index c3a84ef..17b97d6 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.22.2 require ( github.com/nbd-wtf/go-nostr v0.27.0 + github.com/sirupsen/logrus v1.9.3 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 9ab99ff..4589eaf 100644 --- a/go.sum +++ b/go.sum @@ -92,6 +92,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -126,6 +128,7 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= diff --git a/internal/logger/logger.go b/internal/logger/logger.go new file mode 100644 index 0000000..4a289f8 --- /dev/null +++ b/internal/logger/logger.go @@ -0,0 +1,101 @@ +package logger + +import ( + "os" + + "github.com/sirupsen/logrus" +) + +var ( + // Logger is the global logger instance + Logger *logrus.Logger +) + +// Init initializes the logger with the specified log level +func Init(level string, devMode bool) { + Logger = logrus.New() + + // Set output to stdout + Logger.SetOutput(os.Stdout) + + // Set formatter - use JSON in production, text in dev + if devMode { + Logger.SetFormatter(&logrus.TextFormatter{ + FullTimestamp: true, + ForceColors: true, + }) + } else { + Logger.SetFormatter(&logrus.JSONFormatter{ + TimestampFormat: "2006-01-02T15:04:05.000Z07:00", + }) + } + + // Parse and set log level + logLevel, err := logrus.ParseLevel(level) + if err != nil { + // Default to INFO if invalid level + logLevel = logrus.InfoLevel + Logger.Warnf("Invalid log level '%s', defaulting to INFO", level) + } + Logger.SetLevel(logLevel) +} + +// Debug logs a debug message +func Debug(args ...interface{}) { + Logger.Debug(args...) +} + +// Debugf logs a formatted debug message +func Debugf(format string, args ...interface{}) { + Logger.Debugf(format, args...) +} + +// Info logs an info message +func Info(args ...interface{}) { + Logger.Info(args...) +} + +// Infof logs a formatted info message +func Infof(format string, args ...interface{}) { + Logger.Infof(format, args...) +} + +// Warn logs a warning message +func Warn(args ...interface{}) { + Logger.Warn(args...) +} + +// Warnf logs a formatted warning message +func Warnf(format string, args ...interface{}) { + Logger.Warnf(format, args...) +} + +// Error logs an error message +func Error(args ...interface{}) { + Logger.Error(args...) +} + +// Errorf logs a formatted error message +func Errorf(format string, args ...interface{}) { + Logger.Errorf(format, args...) +} + +// Fatal logs a fatal message and exits +func Fatal(args ...interface{}) { + Logger.Fatal(args...) +} + +// Fatalf logs a formatted fatal message and exits +func Fatalf(format string, args ...interface{}) { + Logger.Fatalf(format, args...) +} + +// WithField creates an entry with a single field +func WithField(key string, value interface{}) *logrus.Entry { + return Logger.WithField(key, value) +} + +// WithFields creates an entry with multiple fields +func WithFields(fields logrus.Fields) *logrus.Entry { + return Logger.WithFields(fields) +} diff --git a/internal/nostr/client.go b/internal/nostr/client.go index fae8217..4074a9d 100644 --- a/internal/nostr/client.go +++ b/internal/nostr/client.go @@ -3,10 +3,11 @@ package nostr import ( "context" "fmt" - "log" "sync" "time" + "gitcitadel-online/internal/logger" + "github.com/nbd-wtf/go-nostr" ) @@ -73,12 +74,17 @@ func (c *Client) FetchEvent(ctx context.Context, filter nostr.Filter) (*nostr.Ev wg.Add(1) go func(url string) { defer wg.Done() - log.Printf("Querying relay %s with filter: Kinds=%v, Authors=%v, IDs=%v, Tags=%v", - url, filter.Kinds, filter.Authors, filter.IDs, filter.Tags) + logger.WithFields(map[string]interface{}{ + "relay": url, + "kinds": filter.Kinds, + "authors": filter.Authors, + "ids": filter.IDs, + "tags": filter.Tags, + }).Debug("Querying relay") relay, err := c.connectToRelay(queryCtx, url) if err != nil { - log.Printf("Relay %s: connection failed: %v", url, err) + logger.WithField("relay", url).Debugf("Connection failed: %v", err) results <- result{relay: url, events: nil, err: err} return } @@ -86,12 +92,15 @@ func (c *Client) FetchEvent(ctx context.Context, filter nostr.Filter) (*nostr.Ev events, err := relay.QuerySync(queryCtx, filter) if err != nil { - log.Printf("Relay %s: query failed: %v", url, err) + logger.WithField("relay", url).Debugf("Query failed: %v", err) results <- result{relay: url, events: nil, err: err} return } - log.Printf("Relay %s: returned %d event(s)", url, len(events)) + logger.WithFields(map[string]interface{}{ + "relay": url, + "events": len(events), + }).Debug("Relay returned events") results <- result{relay: url, events: events, err: nil} }(relayURL) } @@ -130,7 +139,10 @@ func (c *Client) FetchEvent(ctx context.Context, filter nostr.Filter) (*nostr.Ev } } - log.Printf("Selected newest event (created_at: %d) from %d total events across all relays", newest.CreatedAt, len(allEvents)) + logger.WithFields(map[string]interface{}{ + "created_at": newest.CreatedAt, + "total": len(allEvents), + }).Debug("Selected newest event from all relays") return newest, nil } @@ -159,12 +171,18 @@ func (c *Client) FetchEvents(ctx context.Context, filter nostr.Filter) ([]*nostr wg.Add(1) go func(url string) { defer wg.Done() - log.Printf("Querying relay %s with filter: Kinds=%v, Authors=%v, IDs=%v, Tags=%v, Limit=%d", - url, filter.Kinds, filter.Authors, filter.IDs, filter.Tags, filter.Limit) + logger.WithFields(map[string]interface{}{ + "relay": url, + "kinds": filter.Kinds, + "authors": filter.Authors, + "ids": filter.IDs, + "tags": filter.Tags, + "limit": filter.Limit, + }).Debug("Querying relay") relay, err := c.connectToRelay(queryCtx, url) if err != nil { - log.Printf("Relay %s: connection failed: %v", url, err) + logger.WithField("relay", url).Debugf("Connection failed: %v", err) results <- result{relay: url, events: nil, err: err} return } @@ -172,12 +190,15 @@ func (c *Client) FetchEvents(ctx context.Context, filter nostr.Filter) ([]*nostr events, err := relay.QuerySync(queryCtx, filter) if err != nil { - log.Printf("Relay %s: query failed: %v", url, err) + logger.WithField("relay", url).Debugf("Query failed: %v", err) results <- result{relay: url, events: nil, err: err} return } - log.Printf("Relay %s: returned %d event(s)", url, len(events)) + logger.WithFields(map[string]interface{}{ + "relay": url, + "events": len(events), + }).Debug("Relay returned events") results <- result{relay: url, events: events, err: nil} }(relayURL) } @@ -217,7 +238,7 @@ func (c *Client) FetchEvents(ctx context.Context, filter nostr.Filter) ([]*nostr events = append(events, event) } - log.Printf("Returning %d unique event(s) from all relays", len(events)) + logger.WithField("events", len(events)).Debug("Returning unique events from all relays") return events, nil } diff --git a/internal/nostr/ebooks.go b/internal/nostr/ebooks.go index 26b29e8..62d0900 100644 --- a/internal/nostr/ebooks.go +++ b/internal/nostr/ebooks.go @@ -3,9 +3,9 @@ package nostr import ( "context" "fmt" - "log" "github.com/nbd-wtf/go-nostr" + "gitcitadel-online/internal/logger" ) // EBooksService handles fetching top-level 30040 events @@ -58,7 +58,10 @@ func (es *EBooksService) FetchTopLevelIndexEvents(ctx context.Context) ([]EBookI return nil, fmt.Errorf("failed to query events: %w", err) } - log.Printf("Fetched %d index events from %s", len(events), es.relayURL) + logger.WithFields(map[string]interface{}{ + "events": len(events), + "relay": es.relayURL, + }).Debug("Fetched index events") // Build a set of all referenced kind:pubkey:dtag from 'a' tags referencedSet := make(map[string]bool) @@ -71,7 +74,7 @@ func (es *EBooksService) FetchTopLevelIndexEvents(ctx context.Context) ([]EBookI } } - log.Printf("Found %d referenced index events", len(referencedSet)) + logger.WithField("count", len(referencedSet)).Debug("Found referenced index events") // Filter to only top-level events (not referenced in any other event) var topLevelEvents []*nostr.Event @@ -96,7 +99,7 @@ func (es *EBooksService) FetchTopLevelIndexEvents(ctx context.Context) ([]EBookI } } - log.Printf("Found %d top-level index events", len(topLevelEvents)) + logger.WithField("count", len(topLevelEvents)).Info("Found top-level index events") // Convert to EBookInfo ebooks := make([]EBookInfo, 0, len(topLevelEvents)) @@ -157,7 +160,10 @@ func (es *EBooksService) FetchTopLevelIndexEvents(ctx context.Context) ([]EBookI naddr, err := CreateNaddr(es.indexKind, ebook.Author, ebook.DTag, relays) if err != nil { - log.Printf("Failed to create naddr for ebook %s: %v", ebook.DTag, err) + logger.WithFields(map[string]interface{}{ + "ebook": ebook.DTag, + "error": err, + }).Warn("Failed to create naddr for ebook, using fallback format") // Fallback to kind:pubkey:dtag format ebook.Naddr = fmt.Sprintf("%d:%s:%s", es.indexKind, ebook.Author, ebook.DTag) } else { diff --git a/internal/nostr/feed.go b/internal/nostr/feed.go index e711722..e4fee13 100644 --- a/internal/nostr/feed.go +++ b/internal/nostr/feed.go @@ -3,9 +3,10 @@ package nostr import ( "context" "fmt" - "log" "time" + "gitcitadel-online/internal/logger" + "github.com/nbd-wtf/go-nostr" ) @@ -41,7 +42,11 @@ func (fs *FeedService) FetchFeedItems(ctx context.Context, feedRelay string, max // Retry up to 3 times for the configured feed relay only for attempt := 1; attempt <= maxRetries; attempt++ { if attempt > 1 { - log.Printf("Retrying feed relay %s (attempt %d/%d)", feedRelay, attempt, maxRetries) + logger.WithFields(map[string]interface{}{ + "relay": feedRelay, + "attempt": attempt, + "max": maxRetries, + }).Debug("Retrying feed relay") // Wait a bit before retrying (exponential backoff: 1s, 2s) waitTime := time.Duration(attempt-1) * time.Second time.Sleep(waitTime) @@ -53,7 +58,11 @@ func (fs *FeedService) FetchFeedItems(ctx context.Context, feedRelay string, max // Connect to the relay relayConn, err := fs.client.ConnectToRelay(attemptCtx, feedRelay) if err != nil { - log.Printf("Failed to connect to feed relay %s (attempt %d/%d): %v", feedRelay, attempt, maxRetries, err) + logger.WithFields(map[string]interface{}{ + "relay": feedRelay, + "attempt": attempt, + "max": maxRetries, + }).Debugf("Failed to connect to feed relay: %v", err) lastErr = err cancel() continue // Try next attempt @@ -65,7 +74,11 @@ func (fs *FeedService) FetchFeedItems(ctx context.Context, feedRelay string, max cancel() if err != nil { - log.Printf("Failed to fetch feed events from %s (attempt %d/%d): %v", feedRelay, attempt, maxRetries, err) + logger.WithFields(map[string]interface{}{ + "relay": feedRelay, + "attempt": attempt, + "max": maxRetries, + }).Debugf("Failed to fetch feed events: %v", err) lastErr = err continue // Try next attempt } @@ -82,7 +95,11 @@ func (fs *FeedService) FetchFeedItems(ctx context.Context, feedRelay string, max items = append(items, item) } - log.Printf("Successfully fetched %d feed items from %s (attempt %d/%d)", len(items), feedRelay, attempt, maxRetries) + logger.WithFields(map[string]interface{}{ + "relay": feedRelay, + "items": len(items), + "attempt": attempt, + }).Info("Successfully fetched feed items") return items, nil } diff --git a/internal/nostr/profile.go b/internal/nostr/profile.go index 9482be6..3a444a6 100644 --- a/internal/nostr/profile.go +++ b/internal/nostr/profile.go @@ -4,7 +4,8 @@ import ( "context" "encoding/json" "fmt" - "log" + + "gitcitadel-online/internal/logger" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip19" @@ -79,7 +80,10 @@ func (c *Client) FetchProfile(ctx context.Context, npub string) (*Profile, error Limit: 1, } - log.Printf("Fetching profile for npub %s (pubkey: %s)", npub, pubkey) + logger.WithFields(map[string]interface{}{ + "npub": npub, + "pubkey": pubkey, + }).Debug("Fetching profile") // Fetch the event event, err := c.FetchEvent(ctx, filter) diff --git a/internal/nostr/wiki.go b/internal/nostr/wiki.go index bb9feb1..b994e91 100644 --- a/internal/nostr/wiki.go +++ b/internal/nostr/wiki.go @@ -3,15 +3,22 @@ package nostr import ( "context" "fmt" - "log" + + "gitcitadel-online/internal/logger" "github.com/nbd-wtf/go-nostr" ) // logFilter logs the exact filter being used for debugging func logFilter(filter nostr.Filter, context string) { - log.Printf("FILTER [%s]: Kinds=%v, Authors=%v, IDs=%v, Tags=%v, Limit=%d", - context, filter.Kinds, filter.Authors, filter.IDs, filter.Tags, filter.Limit) + logger.WithFields(map[string]interface{}{ + "context": context, + "kinds": filter.Kinds, + "authors": filter.Authors, + "ids": filter.IDs, + "tags": filter.Tags, + "limit": filter.Limit, + }).Debug("Nostr filter") } // WikiService handles wiki-specific operations @@ -84,7 +91,10 @@ func (ws *WikiService) FetchWikiEvents(ctx context.Context, index *IndexEvent) ( // If event ID is specified, use it for more reliable fetching if item.EventID != "" { filter.IDs = []string{item.EventID} - log.Printf("Using event ID %s for %s", item.EventID[:16]+"...", item.DTag) + logger.WithFields(map[string]interface{}{ + "event_id": item.EventID[:16] + "...", + "dtag": item.DTag, + }).Debug("Using event ID for wiki event") } logFilter(filter, fmt.Sprintf("wiki event %s", item.DTag)) @@ -94,7 +104,10 @@ func (ws *WikiService) FetchWikiEvents(ctx context.Context, index *IndexEvent) ( var err error if item.RelayHint != "" { // Connect to the relay hint - log.Printf("Trying relay hint %s for %s", item.RelayHint, item.DTag) + logger.WithFields(map[string]interface{}{ + "relay_hint": item.RelayHint, + "dtag": item.DTag, + }).Debug("Trying relay hint") relay, relayErr := ws.client.ConnectToRelay(ctx, item.RelayHint) if relayErr == nil { events, queryErr := relay.QuerySync(ctx, filter) @@ -102,27 +115,40 @@ func (ws *WikiService) FetchWikiEvents(ctx context.Context, index *IndexEvent) ( if queryErr == nil { if len(events) > 0 { event = events[0] - log.Printf("Successfully fetched %s from relay hint %s", item.DTag, item.RelayHint) + logger.WithFields(map[string]interface{}{ + "dtag": item.DTag, + "relay_hint": item.RelayHint, + }).Debug("Successfully fetched from relay hint") } else { - log.Printf("Relay hint %s returned 0 events for %s (event ID: %v)", item.RelayHint, item.DTag, item.EventID != "") + logger.WithFields(map[string]interface{}{ + "relay_hint": item.RelayHint, + "dtag": item.DTag, + "has_event_id": item.EventID != "", + }).Debug("Relay hint returned 0 events") } } else { - log.Printf("Error querying relay hint %s for %s: %v", item.RelayHint, item.DTag, queryErr) + logger.WithFields(map[string]interface{}{ + "relay_hint": item.RelayHint, + "dtag": item.DTag, + }).Debugf("Error querying relay hint: %v", queryErr) } } else { - log.Printf("Error connecting to relay hint %s for %s: %v", item.RelayHint, item.DTag, relayErr) + logger.WithFields(map[string]interface{}{ + "relay_hint": item.RelayHint, + "dtag": item.DTag, + }).Debugf("Error connecting to relay hint: %v", relayErr) } } // Fallback to default client relays if relay hint failed or wasn't provided // Client now queries all three relays automatically if event == nil { - log.Printf("Querying all relays for %s", item.DTag) + logger.WithField("dtag", item.DTag).Debug("Querying all relays") event, err = ws.client.FetchEvent(ctx, filter) if err != nil { // If still not found and event ID was specified, try without event ID to get latest replaceable event if item.EventID != "" { - log.Printf("Trying without event ID for %s to get latest replaceable event", item.DTag) + logger.WithField("dtag", item.DTag).Debug("Trying without event ID to get latest replaceable event") filterWithoutID := nostr.Filter{ Kinds: []int{ws.wikiKind}, Authors: []string{item.Pubkey}, @@ -133,16 +159,20 @@ func (ws *WikiService) FetchWikiEvents(ctx context.Context, index *IndexEvent) ( logFilter(filterWithoutID, fmt.Sprintf("wiki event %s (without event ID)", item.DTag)) event, err = ws.client.FetchEvent(ctx, filterWithoutID) if err == nil { - log.Printf("Successfully fetched %s (latest) from all relays", item.DTag) + logger.WithField("dtag", item.DTag).Debug("Successfully fetched latest from all relays") } } } else { - log.Printf("Successfully fetched %s from all relays", item.DTag) + logger.WithField("dtag", item.DTag).Debug("Successfully fetched from all relays") } if event == nil { // Log error but continue with other events - log.Printf("Error fetching wiki event for %s (pubkey: %s, event ID: %v): %v", item.DTag, item.Pubkey, item.EventID != "", err) + logger.WithFields(map[string]interface{}{ + "dtag": item.DTag, + "pubkey": item.Pubkey, + "has_event_id": item.EventID != "", + }).Warnf("Error fetching wiki event: %v", err) continue } } @@ -150,7 +180,9 @@ func (ws *WikiService) FetchWikiEvents(ctx context.Context, index *IndexEvent) ( // Parse the wiki event wiki, err := ParseWikiEvent(event, ws.wikiKind) if err != nil { - log.Printf("Error parsing wiki event for %s: %v", item.DTag, err) + logger.WithFields(map[string]interface{}{ + "dtag": item.DTag, + }).Warnf("Error parsing wiki event: %v", err) continue } @@ -158,7 +190,7 @@ func (ws *WikiService) FetchWikiEvents(ctx context.Context, index *IndexEvent) ( } if len(wikiEvents) == 0 && len(index.Items) > 0 { - log.Printf("Warning: No wiki events could be fetched from %d index items", len(index.Items)) + logger.WithField("items", len(index.Items)).Warn("No wiki events could be fetched from index items") // Return empty slice instead of error to allow landing page generation } @@ -222,25 +254,44 @@ func (ws *WikiService) FetchIndexEvents(ctx context.Context, index *IndexEvent, if len(relayEvents) > 0 { event = relayEvents[0] } else { - log.Printf("Relay hint %s returned 0 events for %s (kind %d, event ID: %v)", item.RelayHint, item.DTag, targetKind, item.EventID != "") + logger.WithFields(map[string]interface{}{ + "relay_hint": item.RelayHint, + "dtag": item.DTag, + "kind": targetKind, + "has_event_id": item.EventID != "", + }).Debug("Relay hint returned 0 events") } } else { - log.Printf("Error querying relay hint %s for %s (kind %d): %v", item.RelayHint, item.DTag, targetKind, queryErr) + logger.WithFields(map[string]interface{}{ + "relay_hint": item.RelayHint, + "dtag": item.DTag, + "kind": targetKind, + }).Debugf("Error querying relay hint: %v", queryErr) } } else { - log.Printf("Error connecting to relay hint %s for %s (kind %d): %v", item.RelayHint, item.DTag, targetKind, relayErr) + logger.WithFields(map[string]interface{}{ + "relay_hint": item.RelayHint, + "dtag": item.DTag, + "kind": targetKind, + }).Debugf("Error connecting to relay hint: %v", relayErr) } } // Fallback to default client relays if relay hint failed or wasn't provided // Client now queries all three relays automatically if event == nil { - log.Printf("Querying all relays for %s (kind %d)", item.DTag, targetKind) + logger.WithFields(map[string]interface{}{ + "dtag": item.DTag, + "kind": targetKind, + }).Debug("Querying all relays") event, err = ws.client.FetchEvent(ctx, filter) if err != nil { // If still not found and event ID was specified, try without event ID to get latest replaceable event if item.EventID != "" { - log.Printf("Trying without event ID for %s (kind %d) to get latest replaceable event", item.DTag, targetKind) + logger.WithFields(map[string]interface{}{ + "dtag": item.DTag, + "kind": targetKind, + }).Debug("Trying without event ID to get latest replaceable event") filterWithoutID := nostr.Filter{ Kinds: []int{targetKind}, Authors: []string{item.Pubkey}, @@ -251,16 +302,27 @@ func (ws *WikiService) FetchIndexEvents(ctx context.Context, index *IndexEvent, logFilter(filterWithoutID, fmt.Sprintf("index event kind %d %s (without event ID)", targetKind, item.DTag)) event, err = ws.client.FetchEvent(ctx, filterWithoutID) if err == nil { - log.Printf("Successfully fetched %s (kind %d, latest) from all relays", item.DTag, targetKind) + logger.WithFields(map[string]interface{}{ + "dtag": item.DTag, + "kind": targetKind, + }).Debug("Successfully fetched latest from all relays") } } } else { - log.Printf("Successfully fetched %s (kind %d) from all relays", item.DTag, targetKind) + logger.WithFields(map[string]interface{}{ + "dtag": item.DTag, + "kind": targetKind, + }).Debug("Successfully fetched from all relays") } if event == nil { // Log error but continue with other events - log.Printf("Error fetching event for %s (kind %d, pubkey: %s, event ID: %v): %v", item.DTag, targetKind, item.Pubkey, item.EventID != "", err) + logger.WithFields(map[string]interface{}{ + "dtag": item.DTag, + "kind": targetKind, + "pubkey": item.Pubkey, + "has_event_id": item.EventID != "", + }).Warnf("Error fetching event: %v", err) continue } } @@ -269,7 +331,10 @@ func (ws *WikiService) FetchIndexEvents(ctx context.Context, index *IndexEvent, } if len(events) == 0 && len(index.Items) > 0 { - log.Printf("Warning: No events of kind %d could be fetched from %d index items", targetKind, len(index.Items)) + logger.WithFields(map[string]interface{}{ + "kind": targetKind, + "items": len(index.Items), + }).Warn("No events could be fetched from index items") } return events, nil diff --git a/internal/server/handlers.go b/internal/server/handlers.go index b7eb85d..37d067a 100644 --- a/internal/server/handlers.go +++ b/internal/server/handlers.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "log" "net/http" "strings" "time" @@ -13,6 +12,7 @@ import ( "gitcitadel-online/internal/cache" "gitcitadel-online/internal/generator" + "gitcitadel-online/internal/logger" "gitcitadel-online/internal/nostr" ) @@ -115,7 +115,7 @@ func (s *Server) handleContact(w http.ResponseWriter, r *http.Request) { if s.repoAnnouncement != "" { repoAnnouncement, err = s.issueService.FetchRepoAnnouncement(ctx, s.repoAnnouncement) if err != nil { - log.Printf("Failed to fetch repo announcement for contact page: %v", err) + logger.Warnf("Failed to fetch repo announcement for contact page: %v", err) // Continue without repo announcement - form will show error } } @@ -126,7 +126,7 @@ func (s *Server) handleContact(w http.ResponseWriter, r *http.Request) { if s.nostrClient != nil { profile, err = s.nostrClient.FetchProfile(ctx, npub) if err != nil { - log.Printf("Failed to fetch profile for contact page: %v", err) + logger.Warnf("Failed to fetch profile for contact page: %v", err) // Continue without profile - not critical } } @@ -190,7 +190,7 @@ func (s *Server) handleContact(w http.ResponseWriter, r *http.Request) { repoAnnouncement, err := s.issueService.FetchRepoAnnouncement(ctx, s.repoAnnouncement) if err != nil { - log.Printf("Failed to fetch repo announcement: %v", err) + logger.Errorf("Failed to fetch repo announcement: %v", err) formData := map[string]string{ "subject": subject, "content": content, @@ -212,7 +212,7 @@ func (s *Server) handleContact(w http.ResponseWriter, r *http.Request) { // Publish issue (using anonymous key - server generates random key) eventID, err := s.issueService.PublishIssue(ctx, repoAnnouncement, issueReq, "") if err != nil { - log.Printf("Failed to publish issue: %v", err) + logger.Errorf("Failed to publish issue: %v", err) formData := map[string]string{ "subject": subject, "content": content, @@ -265,7 +265,7 @@ func (s *Server) handleContactAPI(w http.ResponseWriter, r *http.Request) { eventID, err := s.issueService.PublishSignedIssue(ctx, req.Event) if err != nil { - log.Printf("Failed to publish signed issue: %v", err) + logger.Errorf("Failed to publish signed issue: %v", err) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, `{"error": "Failed to publish issue: %s"}`, err.Error()) @@ -367,9 +367,13 @@ func (s *Server) middleware(next http.Handler) http.Handler { // CSP header - allow unpkg.com for Lucide icons w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' https://unpkg.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;") - // Log request + // Log request (only in debug mode to reduce noise) start := time.Now() next.ServeHTTP(w, r) - log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start)) + logger.WithFields(map[string]interface{}{ + "method": r.Method, + "path": r.URL.Path, + "duration": time.Since(start), + }).Debug("HTTP request") }) } diff --git a/internal/server/server.go b/internal/server/server.go index dca1ffe..7b1873a 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -3,7 +3,6 @@ package server import ( "context" "fmt" - "log" "net/http" "os" "os/signal" @@ -14,6 +13,7 @@ import ( "gitcitadel-online/internal/cache" "gitcitadel-online/internal/generator" + "gitcitadel-online/internal/logger" "gitcitadel-online/internal/nostr" ) @@ -72,7 +72,7 @@ func NewServer(port int, pageCache *cache.Cache, feedCache *cache.FeedCache, iss // Start starts the HTTP server func (s *Server) Start() error { - log.Printf("Starting server on port %d", s.port) + logger.Infof("Starting server on port %d", s.port) return s.httpServer.ListenAndServe() } @@ -86,16 +86,16 @@ func (s *Server) WaitForShutdown() { quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit - log.Println("Shutting down server...") + logger.Info("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() if err := s.Shutdown(ctx); err != nil { - log.Fatal("Server forced to shutdown:", err) + logger.Fatal("Server forced to shutdown:", err) } - log.Println("Server exited") + logger.Info("Server exited") } // convertFeedItemsToInfo converts cache.FeedItem to generator.FeedItemInfo