feat: integrate opentelemetry metrics for system resource tracking
Added go.opentelemetry.io/otel and go.opentelemetry.io/otel/metric packages to instrument system health observability. The `ResourceTracker` in `pkg/health` now registers global gauge metrics (`sys.goroutines`, `sys.memory.alloc_mb`, `sys.memory.total_alloc_mb`, `sys.memory.sys_mb`) using an OpenTelemetry callback, satisfying the Data Engineering "Ultimate Visibility" framework's requirement to monitor Resource Saturation. Co-authored-by: hobbyistlabs-coder <267281733+hobbyistlabs-coder@users.noreply.github.com>
This commit is contained in:
parent
9fb071836b
commit
ef59cdf50c
2 changed files with 81 additions and 1 deletions
5
go.mod
5
go.mod
|
|
@ -30,6 +30,8 @@ require (
|
|||
github.com/tencent-connect/botgo v0.2.1
|
||||
github.com/traefik/yaegi v0.16.1
|
||||
go.mau.fi/whatsmeow v0.0.0-20260219150138-7ae702b1eed4
|
||||
go.opentelemetry.io/otel v1.29.0
|
||||
go.opentelemetry.io/otel/metric v1.29.0
|
||||
golang.org/x/oauth2 v0.35.0
|
||||
golang.org/x/time v0.14.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
|
|
@ -49,6 +51,8 @@ require (
|
|||
github.com/elliotchance/orderedmap/v3 v3.1.0 // indirect
|
||||
github.com/gdamore/encoding v1.0.1 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
|
|
@ -68,6 +72,7 @@ require (
|
|||
github.com/vektah/gqlparser/v2 v2.5.27 // indirect
|
||||
go.mau.fi/libsignal v0.2.1 // indirect
|
||||
go.mau.fi/util v0.9.6 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a // indirect
|
||||
golang.org/x/term v0.40.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"jane/pkg/logger"
|
||||
)
|
||||
|
||||
|
|
@ -16,6 +18,12 @@ type ResourceTracker struct {
|
|||
interval time.Duration
|
||||
stopCh chan struct{}
|
||||
stopOnce sync.Once
|
||||
|
||||
// OpenTelemetry gauges
|
||||
goroutinesGauge metric.Int64ObservableGauge
|
||||
allocMBGauge metric.Float64ObservableGauge
|
||||
totalMBGauge metric.Float64ObservableGauge
|
||||
sysMBGauge metric.Float64ObservableGauge
|
||||
}
|
||||
|
||||
// NewResourceTracker creates a new ResourceTracker that logs metrics every `interval`.
|
||||
|
|
@ -23,10 +31,77 @@ func NewResourceTracker(interval time.Duration) *ResourceTracker {
|
|||
if interval == 0 {
|
||||
interval = 60 * time.Second // Default to 1 minute
|
||||
}
|
||||
return &ResourceTracker{
|
||||
|
||||
rt := &ResourceTracker{
|
||||
interval: interval,
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
meter := otel.Meter("jane/pkg/health")
|
||||
|
||||
var err error
|
||||
rt.goroutinesGauge, err = meter.Int64ObservableGauge(
|
||||
"sys.goroutines",
|
||||
metric.WithDescription("Number of active goroutines"),
|
||||
)
|
||||
if err != nil {
|
||||
logger.ErrorCF("SystemHealth", "Failed to create goroutines gauge", map[string]any{"error": err.Error()})
|
||||
}
|
||||
|
||||
rt.allocMBGauge, err = meter.Float64ObservableGauge(
|
||||
"sys.memory.alloc_mb",
|
||||
metric.WithDescription("Memory allocated and still in use, in MB"),
|
||||
)
|
||||
if err != nil {
|
||||
logger.ErrorCF("SystemHealth", "Failed to create memory alloc gauge", map[string]any{"error": err.Error()})
|
||||
}
|
||||
|
||||
rt.totalMBGauge, err = meter.Float64ObservableGauge(
|
||||
"sys.memory.total_alloc_mb",
|
||||
metric.WithDescription("Total memory allocated (even if freed), in MB"),
|
||||
)
|
||||
if err != nil {
|
||||
logger.ErrorCF("SystemHealth", "Failed to create total memory alloc gauge", map[string]any{"error": err.Error()})
|
||||
}
|
||||
|
||||
rt.sysMBGauge, err = meter.Float64ObservableGauge(
|
||||
"sys.memory.sys_mb",
|
||||
metric.WithDescription("Total memory obtained from the OS, in MB"),
|
||||
)
|
||||
if err != nil {
|
||||
logger.ErrorCF("SystemHealth", "Failed to create memory sys gauge", map[string]any{"error": err.Error()})
|
||||
}
|
||||
|
||||
if _, err := meter.RegisterCallback(rt.observeMetrics, rt.goroutinesGauge, rt.allocMBGauge, rt.totalMBGauge, rt.sysMBGauge); err != nil {
|
||||
logger.ErrorCF("SystemHealth", "Failed to register metrics callback", map[string]any{"error": err.Error()})
|
||||
}
|
||||
|
||||
return rt
|
||||
}
|
||||
|
||||
func (rt *ResourceTracker) observeMetrics(_ context.Context, o metric.Observer) error {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
|
||||
goroutines := runtime.NumGoroutine()
|
||||
allocMB := float64(m.Alloc) / 1024 / 1024
|
||||
totalAllocMB := float64(m.TotalAlloc) / 1024 / 1024
|
||||
sysMB := float64(m.Sys) / 1024 / 1024
|
||||
|
||||
if rt.goroutinesGauge != nil {
|
||||
o.ObserveInt64(rt.goroutinesGauge, int64(goroutines))
|
||||
}
|
||||
if rt.allocMBGauge != nil {
|
||||
o.ObserveFloat64(rt.allocMBGauge, allocMB)
|
||||
}
|
||||
if rt.totalMBGauge != nil {
|
||||
o.ObserveFloat64(rt.totalMBGauge, totalAllocMB)
|
||||
}
|
||||
if rt.sysMBGauge != nil {
|
||||
o.ObserveFloat64(rt.sysMBGauge, sysMB)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start begins tracking resources in a background goroutine.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue