diff --git a/llm.go b/llm.go
index 648e67c..df7e684 100644
--- a/llm.go
+++ b/llm.go
@@ -25,7 +25,7 @@ type SummaryResult struct {
 
 // articleSummarizer checks for articles without summaries every 10 seconds and processes them in batches
 func articleSummarizer(shutdown chan struct{}, ollamaURL, ollamaModel string) {
-	fmt.Fprintf(os.Stderr, "[summarizer] Starting article summarizer (interval: %s)\n", SUMMARIZE_INTERVAL)
+	logInfo("summarizer", fmt.Sprintf("Starting article summarizer (interval: %s)", SUMMARIZE_INTERVAL), nil)
 
 	ticker := time.NewTicker(SUMMARIZE_INTERVAL)
 	defer ticker.Stop()
@@ -35,7 +35,7 @@ func articleSummarizer(shutdown chan struct{}, ollamaURL, ollamaModel string) {
 		case <-ticker.C:
 			summarizeArticles(ollamaURL, ollamaModel)
 		case <-shutdown:
-			fmt.Fprintf(os.Stderr, "[summarizer] Shutting down article summarizer\n")
+			logInfo("summarizer", "Shutting down article summarizer", nil)
 			return
 		}
 	}
diff --git a/main.go b/main.go
index 1c47ddb..8dcbaa4 100644
--- a/main.go
+++ b/main.go
@@ -3,7 +3,6 @@ package main
 import (
 	"database/sql"
 	"flag"
-	"fmt"
 	"log"
 	"os"
 	"os/signal"
@@ -25,7 +24,9 @@ var (
 )
 
 func main() {
-	fmt.Fprintf(os.Stderr, "[%s] starting gomeshalerter\n", runStart.Format("15:04:05"))
+	logInfo("main", "Starting gomeshalerter", map[string]interface{}{
+		"timestamp": runStart.Format("15:04:05"),
+	})
 	setupLogging()
 	defer flushLog()
 
@@ -37,12 +38,14 @@ func main() {
 
 	// Define a cleanup function to properly close resources
 	cleanup := func() {
-		fmt.Fprintf(os.Stderr, "[shutdown] Closing database...\n")
+		logInfo("shutdown", "Closing database", nil)
 		if err := db.Close(); err != nil {
-			fmt.Fprintf(os.Stderr, "[shutdown] Error closing database: %v\n", err)
+			logInfo("shutdown", "Error closing database", map[string]interface{}{
+				"error": err.Error(),
+			})
 		}
 		flushLog()
-		fmt.Fprintf(os.Stderr, "[shutdown] Cleanup complete\n")
+		logInfo("shutdown", "Cleanup complete", nil)
 	}
 	// Ensure cleanup runs on normal exit
 	defer cleanup()
@@ -57,7 +60,10 @@ func main() {
 		ollamaURL = "http://localhost:11434" // Default Ollama server URL
 	}
 
-	fmt.Fprintf(os.Stderr, "[ollama] Using model: %s at %s\n", ollamaModel, ollamaURL)
+	logInfo("ollama", "Using model", map[string]interface{}{
+		"model": ollamaModel,
+		"url":   ollamaURL,
+	})
 
 	// Replace --broadcast flag with --dry-run flag (default is to broadcast)
 	dryRun := flag.Bool("dry-run", false, "don't actually send to meshtastic, just print what would be sent")
@@ -75,9 +81,9 @@ func main() {
 
 	go func() {
 		<-sigChan
-		fmt.Fprintf(os.Stderr, "[shutdown] Received signal, performing cleanup before exit...\n")
+		logInfo("shutdown", "Received signal, performing cleanup before exit", nil)
 		cleanup()
-		fmt.Fprintf(os.Stderr, "[shutdown] Exiting...\n")
+		logInfo("shutdown", "Exiting", nil)
 		os.Exit(0) // Exit after cleanup
 	}()
 
@@ -118,5 +124,5 @@ func main() {
 
 	// Wait for all goroutines to finish
 	wg.Wait()
-	fmt.Fprintf(os.Stderr, "[shutdown] All goroutines stopped, exiting...\n")
+	logInfo("shutdown", "All goroutines stopped, exiting", nil)
 }
diff --git a/rss.go b/rss.go
index 2a19ebd..99d38a1 100644
--- a/rss.go
+++ b/rss.go
@@ -3,7 +3,6 @@ package main
 import (
 	"fmt"
 	"net/http"
-	"os"
 	"sync"
 	"time"
 
@@ -53,7 +52,7 @@ var feeds = map[string]string{
 
 // rssFeedChecker checks RSS feeds every 15 minutes and adds new articles to the database
 func rssFeedChecker(shutdown chan struct{}, ollamaURL, ollamaModel string) {
-	fmt.Fprintf(os.Stderr, "[rss] Starting RSS feed checker (interval: %s)\n", RSS_CHECK_INTERVAL)
+	logInfo("rss", fmt.Sprintf("Starting RSS feed checker (interval: %s)", RSS_CHECK_INTERVAL), nil)
 
 	// Run immediately on startup
 	checkRSSFeeds()
@@ -67,7 +66,7 @@ func rssFeedChecker(shutdown chan struct{}, ollamaURL, ollamaModel string) {
 		case <-ticker.C:
 			checkRSSFeeds()
 		case <-shutdown:
-			fmt.Fprintf(os.Stderr, "[rss] Shutting down RSS feed checker\n")
+			logInfo("rss", "Shutting down RSS feed checker", nil)
 			return
 		}
 	}
@@ -160,13 +159,23 @@ func fetchAllFeedsParallel(now time.Time) []Article {
 			logEvent("rss_fetch_result", details)
 
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "[rss] FAIL  %-15s (%s) [%.2fs] ERR: %v\n", source, url, duration.Seconds(), err)
+				logInfo("rss", "Feed fetch failed", map[string]interface{}{
+					"source":   source,
+					"url":      url,
+					"duration": duration.Seconds(),
+					"error":    err.Error(),
+				})
 				results <- fetchResult{Source: source, URL: url, Err: err, Duration: duration, HTTPStatus: httpStatus}
 				return
 			}
 
-			fmt.Fprintf(os.Stderr, "[rss] OK    %-15s (%s) [%.2fs] HTTP %d, items: %d\n",
-				source, url, duration.Seconds(), httpStatus, len(feed.Items))
+			logInfo("rss", "Feed fetch succeeded", map[string]interface{}{
+				"source":   source,
+				"url":      url,
+				"duration": duration.Seconds(),
+				"status":   httpStatus,
+				"items":    len(feed.Items),
+			})
 
 			results <- fetchResult{
 				Source:     source,
diff --git a/storage.go b/storage.go
index c26946b..4e6a87c 100644
--- a/storage.go
+++ b/storage.go
@@ -6,6 +6,7 @@ import (
 	"encoding/hex"
 	"encoding/json"
 	"fmt"
+	"log"
 	"os"
 	"sort"
 	"time"
@@ -348,8 +349,7 @@ func setupLogging() {
 	var err error
 	logFile, err = os.Create(logPath)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "could not create log file: %v\n", err)
-		os.Exit(1)
+		log.Fatalf("Could not create log file: %v", err)
 	}
 }
 
@@ -395,7 +395,7 @@ func logEvent(event string, details map[string]interface{}) {
 	// Store log in database
 	logBytes, err := json.Marshal(entry)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error marshaling log entry: %v\n", err)
+		log.Printf("Error marshaling log entry: %v", err)
 		return
 	}
 
@@ -407,7 +407,7 @@ func logEvent(event string, details map[string]interface{}) {
 	_, err = db.Exec("INSERT INTO logs (id, timestamp, log) VALUES (?, ?, ?)",
 		id, timestamp, string(logBytes))
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error storing log in database: %v\n", err)
+		log.Printf("Error storing log in database: %v", err)
 	}
 }
 
@@ -453,7 +453,11 @@ func cleanupOldLogs() error {
 
 	rowsDeleted, _ := result.RowsAffected()
 	if rowsDeleted > 0 {
-		fmt.Fprintf(os.Stderr, "[logs] Deleted %d log entries older than one month\n", rowsDeleted)
+		logInfo("logs", "Deleted old log entries", map[string]interface{}{
+			"count":      rowsDeleted,
+			"olderThan":  "1 month",
+			"cutoffDate": cutoff.Format(time.RFC3339),
+		})
 	}
 
 	return nil