@@ -10,6 +10,7 @@ import (
1010 "time"
1111
1212 "github.com/node-pulse/agent/internal/config"
13+ "github.com/node-pulse/agent/internal/exporters"
1314 "github.com/node-pulse/agent/internal/logger"
1415 "github.com/node-pulse/agent/internal/pidfile"
1516 "github.com/node-pulse/agent/internal/prometheus"
@@ -80,16 +81,49 @@ func runAgent(cmd *cobra.Command, args []string) error {
8081 }
8182 }()
8283
83- // Create Prometheus scraper
84- scraper := prometheus .NewScraper (& prometheus.ScraperConfig {
85- Endpoint : cfg .Prometheus .Endpoint ,
86- Timeout : cfg .Prometheus .Timeout ,
87- })
84+ // Create exporter registry
85+ registry := exporters .NewRegistry ()
8886
89- // Verify node_exporter is accessible on startup
90- if err := scraper .Verify (); err != nil {
91- logger .Error ("Failed to verify Prometheus exporter - is node_exporter running?" , logger .Err (err ))
92- return fmt .Errorf ("prometheus exporter verification failed: %w\n Please ensure node_exporter is running on %s" , err , cfg .Prometheus .Endpoint )
87+ // Register built-in exporters
88+ registry .Register (exporters .NewNodeExporter ("" , 0 ))
89+ // Future: register other exporters here
90+ // registry.Register(exporters.NewPostgresExporter("", 0))
91+ // registry.Register(exporters.NewMysqlExporter("", 0))
92+
93+ // Initialize enabled exporters from config
94+ activeExporters := []exporters.Exporter {}
95+ for _ , exporterCfg := range cfg .Exporters {
96+ if ! exporterCfg .Enabled {
97+ continue
98+ }
99+
100+ // Create exporter instance with configured endpoint and timeout
101+ var exp exporters.Exporter
102+ switch exporterCfg .Name {
103+ case "node_exporter" :
104+ exp = exporters .NewNodeExporter (exporterCfg .Endpoint , exporterCfg .Timeout )
105+ default :
106+ logger .Warn ("Unknown exporter type, skipping" , logger .String ("name" , exporterCfg .Name ))
107+ continue
108+ }
109+
110+ // Verify exporter is accessible
111+ if err := exp .Verify (); err != nil {
112+ logger .Warn ("Exporter verification failed, skipping" ,
113+ logger .String ("name" , exporterCfg .Name ),
114+ logger .String ("endpoint" , exporterCfg .Endpoint ),
115+ logger .Err (err ))
116+ continue
117+ }
118+
119+ activeExporters = append (activeExporters , exp )
120+ logger .Info ("Exporter initialized" ,
121+ logger .String ("name" , exporterCfg .Name ),
122+ logger .String ("endpoint" , exporterCfg .Endpoint ))
123+ }
124+
125+ if len (activeExporters ) == 0 {
126+ return fmt .Errorf ("no active exporters configured - please configure at least one exporter" )
93127 }
94128
95129 // Create report sender
@@ -123,14 +157,12 @@ func runAgent(cmd *cobra.Command, args []string) error {
123157 logger .Info ("Agent started" ,
124158 logger .String ("server_id" , cfg .Agent .ServerID ),
125159 logger .Duration ("interval" , cfg .Agent .Interval ),
126- logger .String ( "prometheus_endpoint " , cfg . Prometheus . Endpoint ),
160+ logger .Int ( "exporters " , len ( activeExporters ) ),
127161 logger .String ("server_endpoint" , cfg .Server .Endpoint ))
128162
129163 // Scrape immediately on start with aligned timestamp (UTC)
130164 collectionTime := time .Now ().UTC ().Truncate (cfg .Agent .Interval )
131- if err := scrapeAndBuffer (scraper , sender , cfg .Agent .ServerID , collectionTime ); err != nil {
132- logger .Error ("Initial scrape failed" , logger .Err (err ))
133- }
165+ scrapeAllExporters (ctx , activeExporters , sender , cfg .Agent .ServerID , collectionTime )
134166
135167 // Continue with ticker
136168 for {
@@ -140,34 +172,44 @@ func runAgent(cmd *cobra.Command, args []string) error {
140172 case tickTime := <- ticker .C :
141173 // Align collection time to interval boundary (UTC)
142174 collectionTime := tickTime .UTC ().Truncate (cfg .Agent .Interval )
143- if err := scrapeAndBuffer (scraper , sender , cfg .Agent .ServerID , collectionTime ); err != nil {
144- logger .Error ("Scrape failed" , logger .Err (err ))
145- }
175+ scrapeAllExporters (ctx , activeExporters , sender , cfg .Agent .ServerID , collectionTime )
146176 }
147177 }
148178}
149179
150- // scrapeAndBuffer scrapes metrics and saves raw Prometheus text to buffer
151- func scrapeAndBuffer (scraper * prometheus.Scraper , sender * report.Sender , serverID string , collectionTime time.Time ) error {
152- // Scrape Prometheus exporter
153- data , err := scraper .Scrape ()
154- if err != nil {
155- return fmt .Errorf ("failed to scrape prometheus: %w" , err )
156- }
180+ // scrapeAllExporters scrapes all exporters sequentially
181+ func scrapeAllExporters (ctx context.Context , exportersList []exporters.Exporter ,
182+ sender * report.Sender , serverID string , collectionTime time.Time ) {
157183
158- // Add explicit timestamps to metrics (aligned to collection time)
159- // This ensures all agents report metrics at the same logical time boundaries
160- dataWithTimestamp := prometheus .AddTimestamps (data , collectionTime )
184+ for _ , exp := range exportersList {
185+ // Scrape with timeout
186+ scrapeCtx , cancel := context .WithTimeout (ctx , 3 * time .Second )
187+ data , err := exp .Scrape (scrapeCtx )
188+ cancel ()
161189
162- // Save raw Prometheus text to buffer (WAL pattern - parsing happens during drain)
163- if err := sender .BufferPrometheus (dataWithTimestamp , serverID ); err != nil {
164- return fmt .Errorf ("failed to buffer prometheus data: %w" , err )
165- }
190+ if err != nil {
191+ logger .Warn ("Failed to scrape exporter" ,
192+ logger .String ("exporter" , exp .Name ()),
193+ logger .Err (err ))
194+ continue
195+ }
166196
167- logger .Debug ("Prometheus data scraped and buffered" ,
168- logger .Int ("bytes" , len (dataWithTimestamp )),
169- logger .String ("collection_time" , collectionTime .Format (time .RFC3339 )))
170- return nil
197+ // Add explicit timestamps to metrics (aligned to collection time)
198+ dataWithTimestamp := prometheus .AddTimestamps (data , collectionTime )
199+
200+ // Save raw Prometheus text to buffer (WAL pattern)
201+ if err := sender .BufferPrometheus (dataWithTimestamp , serverID , exp .Name ()); err != nil {
202+ logger .Error ("Failed to buffer metrics" ,
203+ logger .String ("exporter" , exp .Name ()),
204+ logger .Err (err ))
205+ continue
206+ }
207+
208+ logger .Debug ("Exporter scraped and buffered" ,
209+ logger .String ("exporter" , exp .Name ()),
210+ logger .Int ("bytes" , len (dataWithTimestamp )),
211+ logger .String ("collection_time" , collectionTime .Format (time .RFC3339 )))
212+ }
171213}
172214
173215func runInBackground () error {
0 commit comments