Skip to content

Commit e30f4e1

Browse files
authored
Merge pull request #166 from klihub/goresctrl/devel/otel-metrics
pkg/rdt, cmd/rdt: implement OpenTelemetry-based metrics.
2 parents acf9094 + d3f1491 commit e30f4e1

4 files changed

Lines changed: 425 additions & 57 deletions

File tree

cmd/rdt/main.go

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ limitations under the License.
1919
package main
2020

2121
import (
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

3748
var (
@@ -209,23 +220,126 @@ func subCmdConfigure(args []string) error {
209220

210221
func 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 {

go.mod

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,53 @@
11
module github.com/intel/goresctrl
22

3-
go 1.23
3+
go 1.23.0
44

55
require (
6-
github.com/google/go-cmp v0.5.9
6+
github.com/google/go-cmp v0.7.0
77
github.com/opencontainers/runtime-spec v1.0.2
8-
github.com/prometheus/client_golang v1.16.0
9-
github.com/stretchr/testify v1.8.1
8+
github.com/prometheus/client_golang v1.23.0
9+
github.com/stretchr/testify v1.11.1
10+
go.opentelemetry.io/otel v1.38.0
11+
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0
12+
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0
13+
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0
14+
go.opentelemetry.io/otel/metric v1.38.0
15+
go.opentelemetry.io/otel/sdk v1.38.0
16+
go.opentelemetry.io/otel/sdk/metric v1.38.0
1017
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611
11-
golang.org/x/sys v0.11.0
18+
golang.org/x/sys v0.35.0
1219
k8s.io/apimachinery v0.27.4
1320
sigs.k8s.io/yaml v1.3.0
1421
)
1522

1623
require (
1724
github.com/beorn7/perks v1.0.1 // indirect
18-
github.com/cespare/xxhash/v2 v2.2.0 // indirect
25+
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
26+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
1927
github.com/davecgh/go-spew v1.1.1 // indirect
28+
github.com/go-logr/logr v1.4.3 // indirect
29+
github.com/go-logr/stdr v1.2.2 // indirect
2030
github.com/gogo/protobuf v1.3.2 // indirect
21-
github.com/golang/protobuf v1.5.3 // indirect
2231
github.com/google/gofuzz v1.2.0 // indirect
23-
github.com/kr/text v0.2.0 // indirect
24-
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
32+
github.com/google/uuid v1.6.0 // indirect
33+
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
34+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
35+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
2536
github.com/pmezard/go-difflib v1.0.0 // indirect
26-
github.com/prometheus/client_model v0.3.0 // indirect
27-
github.com/prometheus/common v0.42.0 // indirect
28-
github.com/prometheus/procfs v0.10.1 // indirect
29-
github.com/rogpeppe/go-internal v1.9.0 // indirect
30-
google.golang.org/protobuf v1.33.0 // indirect
37+
github.com/prometheus/client_model v0.6.2 // indirect
38+
github.com/prometheus/common v0.65.0 // indirect
39+
github.com/prometheus/otlptranslator v0.0.2 // indirect
40+
github.com/prometheus/procfs v0.17.0 // indirect
41+
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
42+
go.opentelemetry.io/otel/exporters/prometheus v0.60.0 // indirect
43+
go.opentelemetry.io/otel/trace v1.38.0 // indirect
44+
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
45+
golang.org/x/net v0.43.0 // indirect
46+
golang.org/x/text v0.28.0 // indirect
47+
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
48+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
49+
google.golang.org/grpc v1.75.0 // indirect
50+
google.golang.org/protobuf v1.36.8 // indirect
3151
gopkg.in/inf.v0 v0.9.1 // indirect
3252
gopkg.in/yaml.v2 v2.4.0 // indirect
3353
gopkg.in/yaml.v3 v3.0.1 // indirect

0 commit comments

Comments
 (0)