@@ -2,20 +2,26 @@ package main
22
33import (
44 "context"
5+ "encoding/json"
56 "fmt"
67 "log"
8+ "log/slog"
79 "net"
810 "net/http"
911 "os"
1012 "os/signal"
13+ "strings"
1114 "syscall"
1215 "time"
1316
1417 "github.com/RandomCodeSpace/Project-Argus/internal/ai"
1518 "github.com/RandomCodeSpace/Project-Argus/internal/api"
1619 "github.com/RandomCodeSpace/Project-Argus/internal/config"
1720 "github.com/RandomCodeSpace/Project-Argus/internal/ingest"
21+ "github.com/RandomCodeSpace/Project-Argus/internal/queue"
22+ "github.com/RandomCodeSpace/Project-Argus/internal/realtime"
1823 "github.com/RandomCodeSpace/Project-Argus/internal/storage"
24+ "github.com/RandomCodeSpace/Project-Argus/internal/telemetry"
1925 "github.com/RandomCodeSpace/Project-Argus/web"
2026
2127 collogspb "go.opentelemetry.io/proto/otlp/collector/logs/v1"
@@ -30,42 +36,102 @@ func main() {
3036
3137 // 0. Load Configuration
3238 cfg := config .Load ()
33- log .Printf ("🚀 Starting Argus in %s mode" , cfg .Env )
3439
35- // 1. Initialize Storage
36- // repository.go already reads DB_DRIVER and DB_DSN from env,
37- // which godotenv has now populated.
40+ // Initialize structured logger
41+ var level slog.Level
42+ switch strings .ToUpper (cfg .LogLevel ) {
43+ case "DEBUG" :
44+ level = slog .LevelDebug
45+ case "WARN" :
46+ level = slog .LevelWarn
47+ case "ERROR" :
48+ level = slog .LevelError
49+ default :
50+ level = slog .LevelInfo
51+ }
52+
53+ logger := slog .New (slog .NewTextHandler (os .Stdout , & slog.HandlerOptions {
54+ Level : level ,
55+ }))
56+ slog .SetDefault (logger )
57+
58+ slog .Info ("🚀 Starting Argus V5.0" , "env" , cfg .Env , "log_level" , level )
59+
60+ // 1. Initialize Internal Telemetry (first — everything registers metrics against this)
61+ metrics := telemetry .New ()
62+ slog .Info ("📊 Internal telemetry initialized" )
63+
64+ // 2. Initialize Storage
3865 repo , err := storage .NewRepository ()
3966 if err != nil {
4067 log .Fatalf ("Failed to initialize repository: %v" , err )
4168 }
69+ slog .Info ("💾 Storage initialized" , "driver" , cfg .DBDriver )
70+
71+ // 3. Initialize DLQ (Dead Letter Queue)
72+ replayInterval , err := time .ParseDuration (cfg .DLQReplayInterval )
73+ if err != nil {
74+ replayInterval = 5 * time .Minute
75+ }
76+
77+ dlq , err := queue .NewDLQ (cfg .DLQPath , replayInterval , func (data []byte ) error {
78+ // Replay handler: try to deserialize and re-insert logs
79+ var logs []storage.Log
80+ if err := json .Unmarshal (data , & logs ); err != nil {
81+ return fmt .Errorf ("DLQ replay unmarshal failed: %w" , err )
82+ }
83+ return repo .BatchCreateLogs (logs )
84+ })
85+ if err != nil {
86+ log .Fatalf ("Failed to initialize DLQ: %v" , err )
87+ }
88+ defer dlq .Stop ()
89+ slog .Info ("🔁 DLQ initialized" , "path" , cfg .DLQPath , "interval" , replayInterval )
90+
91+ // 4. Initialize Real-Time WebSocket Hub
92+ hub := realtime .NewHub (func (count int ) {
93+ metrics .SetActiveConnections (count )
94+ })
95+ go hub .Run ()
96+ defer hub .Stop ()
97+ slog .Info ("🔌 WebSocket hub started" )
4298
43- // 2 . Initialize AI Service
99+ // 5 . Initialize AI Service
44100 aiService := ai .NewService (repo )
45101 defer aiService .Stop ()
46102
47- // 3 . Initialize API Server
48- apiServer := api .NewServer (repo )
103+ // 6 . Initialize API Server
104+ apiServer := api .NewServer (repo , hub , metrics )
49105
50- // 4 . Initialize OTLP Ingestion (gRPC)
51- traceServer := ingest .NewTraceServer (repo )
52- logsServer := ingest .NewLogsServer (repo )
106+ // 7 . Initialize OTLP Ingestion (gRPC)
107+ traceServer := ingest .NewTraceServer (repo , cfg )
108+ logsServer := ingest .NewLogsServer (repo , cfg )
53109
54- // Wire up live log streaming
55- // When a log arrives, ingest -> api.BroadcastLog
110+ // Wire up live log streaming + AI + DLQ metrics
56111 logHandler := func (l storage.Log ) {
57112 start := time .Now ()
58113 apiServer .BroadcastLog (l )
59- // AI Analysis Trigger
60114 aiService .EnqueueLog (l )
61115 if time .Since (start ) > 100 * time .Millisecond {
62- log . Println ("Slow broadcast/enqueue took " , time .Since (start ))
116+ slog . Warn ("Slow broadcast/enqueue" , "duration " , time .Since (start ))
63117 }
64118 }
65119
66120 logsServer .SetLogCallback (logHandler )
67121 traceServer .SetLogCallback (logHandler )
68122
123+ // Update DLQ size metric periodically
124+ go func () {
125+ ticker := time .NewTicker (30 * time .Second )
126+ defer ticker .Stop ()
127+ for {
128+ select {
129+ case <- ticker .C :
130+ metrics .SetDLQSize (dlq .Size ())
131+ }
132+ }
133+ }()
134+
69135 // Start gRPC Server
70136 lis , err := net .Listen ("tcp" , ":" + cfg .GRPCPort )
71137 if err != nil {
@@ -77,13 +143,13 @@ func main() {
77143 reflection .Register (grpcServer )
78144
79145 go func () {
80- log . Printf ( "Starting gRPC OTLP receiver on :%s " , cfg .GRPCPort )
146+ slog . Info ( "📡 gRPC OTLP receiver started" , "port " , cfg .GRPCPort )
81147 if err := grpcServer .Serve (lis ); err != nil {
82148 log .Fatalf ("Failed to serve gRPC: %v" , err )
83149 }
84150 }()
85151
86- // 5 . Start HTTP Server
152+ // 8 . Start HTTP Server
87153 mux := http .NewServeMux ()
88154 apiServer .RegisterRoutes (mux )
89155
@@ -104,13 +170,12 @@ func main() {
104170
105171 f , err := distFS .Open (path )
106172 if err == nil {
107- // File exists, serve it
108173 f .Close ()
109174 fileServer .ServeHTTP (w , r )
110175 return
111176 }
112177
113- // File does not exist, serve index.html (SPA catch-all)
178+ // SPA catch-all → serve index.html
114179 f , err = distFS .Open ("index.html" )
115180 if err != nil {
116181 http .Error (w , "index.html not found" , http .StatusInternalServerError )
@@ -131,39 +196,40 @@ func main() {
131196 }
132197
133198 go func () {
134- log . Printf ( "Starting HTTP Server on :%s " , cfg .HTTPPort )
199+ slog . Info ( "🌐 HTTP server started" , "port " , cfg .HTTPPort )
135200 if err := srv .ListenAndServe (); err != nil && err != http .ErrServerClosed {
136201 log .Fatalf ("HTTP server failed: %v" , err )
137202 }
138203 }()
139204
140- // 6 . Graceful Shutdown
205+ // 9 . Graceful Shutdown
141206 stop := make (chan os.Signal , 1 )
142207 signal .Notify (stop , os .Interrupt , syscall .SIGTERM )
143208 <- stop
144209
145- log . Println ("Shutting down..." )
210+ slog . Info ("Shutting down ARGUS V5.0 ..." )
146211
147- ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
212+ ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Second )
148213 defer cancel ()
149214
150215 grpcServer .GracefulStop ()
151216 if err := srv .Shutdown (ctx ); err != nil {
152- log . Printf ("HTTP server forced to shutdown: %v " , err )
217+ slog . Error ("HTTP server forced shutdown" , "error " , err )
153218 }
154219
155- log . Println ( "Server exited " )
220+ slog . Info ( "✅ ARGUS V5.0 shutdown complete " )
156221}
157222
158223func printBanner () {
159224 banner := `
160- _ ____ ____ _ _ ____ __ ______
161- / \ | _ \ / ___| | | / ___| \ \ / /___ \
162- / _ \ | |_) | | _| | | \___ \ \ \ / / __) |
163- / ___ \| _ <| |_| | |_| |___) | \ V / / __/
164- /_/ \_\_| \_\\____|\___/|____/ \_/ |_____|
165-
166- Project ARGUS V2: Enterprise Edition - All Systems Nominal
225+ _ ____ ____ _ _ ____ __ _____ ___
226+ / \ | _ \ / ___| | | / ___| \ \ / / ___|| _ \
227+ / _ \ | |_) | | _| | | \___ \ \ \ / /|___ \| | | |
228+ / ___ \| _ <| |_| | |_| |___) | \ V / ___) | |_| |
229+ /_/ \_\_| \_\\____|\\___/|____/ \_/ |____/|___/
230+
231+ ARGUS V5.0 (DEV MODE) — Production Hardened Edition
232+ The Eye That Never Sleeps 👁️
167233`
168234 fmt .Println (banner )
169235}
0 commit comments