@@ -19,6 +19,7 @@ limitations under the License.
1919package main
2020
2121import (
22+ "context"
2223 "flag"
2324 "fmt"
2425 "log/slog"
@@ -32,6 +33,16 @@ import (
3233 "github.com/intel/goresctrl/pkg/rdt"
3334 "github.com/prometheus/client_golang/prometheus"
3435 "github.com/prometheus/client_golang/prometheus/promhttp"
36+
37+ "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
38+ "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
39+
40+ otelprom "go.opentelemetry.io/otel/exporters/prometheus"
41+
42+ "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
43+ "go.opentelemetry.io/otel/sdk/metric"
44+ "go.opentelemetry.io/otel/sdk/resource"
45+ semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
3546)
3647
3748var (
@@ -209,23 +220,126 @@ func subCmdConfigure(args []string) error {
209220
210221func subCmdMonitor (args []string ) error {
211222 // Parse command line args
212- flags := flag .NewFlagSet ("configure" , flag .ExitOnError )
213- addGlobalFlags (flags )
223+ var (
224+ flags = flag .NewFlagSet ("configure" , flag .ExitOnError )
225+ port = flags .Int ("port" , 8080 , "port to serve metrics on" )
226+ ohttp = flags .Bool ("otel-http" , false , "enable OpenTelemetry/HTTP export" )
227+ ogrpc = flags .Bool ("otel-grpc" , false , "enable OpenTelemetry/gRPC export" )
228+ otext = flags .Duration ("otel-text" , 0 , "OpenTelemetry/stdout export period" )
229+ oprom = flags .Bool ("otel-prom" , false , "enable OpenTelemetry/Prometheus export" )
230+ )
214231
215- port := flags . Int ( "port" , 8080 , "port to serve metrics on" )
232+ addGlobalFlags ( flags )
216233
217234 if err := flags .Parse (args ); err != nil {
218235 return err
219236 }
220237
238+ const (
239+ nativePrometheus = "/metrics"
240+ otelPrometheus = "/otel-metrics"
241+ )
242+
221243 // Run sub-command
222244 if err := rdt .Initialize (groupPrefix ); err != nil {
223245 return fmt .Errorf ("RDT is not enabled: %v" , err )
224246 }
225247
226248 prometheusRegistry := prometheus .NewRegistry ()
227249 prometheusRegistry .MustRegister (rdt .NewCollector ())
228- http .Handle ("/metrics" , promhttp .HandlerFor (prometheusRegistry , promhttp.HandlerOpts {}))
250+ http .Handle (nativePrometheus , promhttp .HandlerFor (prometheusRegistry , promhttp.HandlerOpts {}))
251+
252+ if * ohttp || * ogrpc || * otext != 0 || * oprom {
253+ resource , err := resource .Merge (
254+ resource .Default (),
255+ resource .NewWithAttributes (
256+ semconv .SchemaURL ,
257+ semconv .ServiceName ("rdt.monitor" ),
258+ ),
259+ )
260+ if err != nil {
261+ return fmt .Errorf ("failed to create OpenTelemetry resource: %w" , err )
262+ }
263+
264+ var (
265+ ctx = context .Background ()
266+ readers = []metric.Option {}
267+ )
268+
269+ if * ohttp {
270+ fmt .Printf ("Setting up OpenTelemetry/HTTP metric exporter...\n " )
271+
272+ exp , err := otlpmetrichttp .New (ctx )
273+ if err != nil {
274+ return fmt .Errorf ("failed to create OpenTelemetry HTTP exporter: %w" , err )
275+ }
276+ readers = append (readers , metric .WithReader (metric .NewPeriodicReader (exp )))
277+ }
278+
279+ if * ogrpc {
280+ fmt .Printf ("Setting up OpenTelemetry/gRPC metric exporter...\n " )
281+
282+ exp , err := otlpmetricgrpc .New (ctx )
283+ if err != nil {
284+ return fmt .Errorf ("failed to create OpenTelemetry gRPC exporter: %w" , err )
285+ }
286+ readers = append (readers , metric .WithReader (metric .NewPeriodicReader (exp )))
287+ }
288+
289+ if * otext != 0 {
290+ fmt .Printf ("Setting up OpenTelemetry/stdout metric exporter...\n " )
291+
292+ exp , err := stdoutmetric .New (
293+ stdoutmetric .WithPrettyPrint (),
294+ stdoutmetric .WithoutTimestamps (),
295+ )
296+ if err != nil {
297+ return fmt .Errorf ("failed to create OpenTelemetry stdout exporter: %w" , err )
298+ }
299+ readers = append (readers ,
300+ metric .WithReader (
301+ metric .NewPeriodicReader (exp , metric .WithInterval (* otext )),
302+ ),
303+ )
304+ }
305+
306+ if * oprom {
307+ fmt .Printf ("Setting up OpenTelemetry Prometheus exporter (HTTP %q)...\n " , otelPrometheus )
308+ registry := prometheus .NewRegistry ()
309+
310+ exp , err := otelprom .New (
311+ otelprom .WithNamespace ("" ),
312+ otelprom .WithoutScopeInfo (),
313+ otelprom .WithoutTargetInfo (),
314+ otelprom .WithRegisterer (registry ),
315+ )
316+ if err != nil {
317+ return fmt .Errorf ("failed to create OpenTelemetry Prometheus exporter: %w" , err )
318+ }
319+
320+ readers = append (readers , metric .WithReader (exp ))
321+
322+ handlerOpts := promhttp.HandlerOpts {
323+ ErrorHandling : promhttp .ContinueOnError ,
324+ }
325+ http .Handle (otelPrometheus , promhttp .HandlerFor (registry , handlerOpts ))
326+ }
327+
328+ provider := metric .NewMeterProvider (
329+ append ([]metric.Option {metric .WithResource (resource )}, readers ... )... ,
330+ )
331+ defer func () {
332+ if err := provider .Shutdown (ctx ); err != nil {
333+ slog .Error ("failed to shutdown OpenTelemetry provider" ,
334+ slog .String ("error" , err .Error ()))
335+ }
336+ }()
337+
338+ meter := provider .Meter ("rtd-monitor" )
339+ if err := rdt .RegisterOpenTelemetryInstruments (meter ); err != nil {
340+ return fmt .Errorf ("failed to register OpenTelemetry instruments: %w" , err )
341+ }
342+ }
229343
230344 fmt .Printf ("Serving prometheus metrics at :%d/metrics\n " , * port )
231345 if err := http .ListenAndServe (fmt .Sprintf (":%d" , * port ), nil ); err != nil {
0 commit comments