mirror of
https://github.com/fosrl/newt.git
synced 2026-03-26 20:46:41 +00:00
83 lines
2.5 KiB
Go
83 lines
2.5 KiB
Go
package telemetry
|
|
|
|
import (
|
|
"context"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/metric"
|
|
)
|
|
|
|
// StateView provides a read-only view for observable gauges.
|
|
// Implementations must be concurrency-safe and avoid blocking operations.
|
|
// All methods should be fast and use RLocks where applicable.
|
|
type StateView interface {
|
|
// ListSites returns a stable, low-cardinality list of site IDs to expose.
|
|
ListSites() []string
|
|
// Online returns whether the site is online.
|
|
Online(siteID string) (online bool, ok bool)
|
|
// LastHeartbeat returns the last heartbeat time for a site.
|
|
LastHeartbeat(siteID string) (t time.Time, ok bool)
|
|
// ActiveSessions returns the current number of active sessions for a site (across tunnels),
|
|
// or scoped to site if your model is site-scoped.
|
|
ActiveSessions(siteID string) (n int64, ok bool)
|
|
}
|
|
|
|
var (
|
|
stateView atomic.Value // of type StateView
|
|
)
|
|
|
|
// RegisterStateView sets the global StateView used by the default observable callback.
|
|
func RegisterStateView(v StateView) {
|
|
stateView.Store(v)
|
|
// If instruments are registered, ensure a callback exists.
|
|
if v != nil {
|
|
SetObservableCallback(func(ctx context.Context, o metric.Observer) error {
|
|
if any := stateView.Load(); any != nil {
|
|
if sv, ok := any.(StateView); ok {
|
|
for _, siteID := range sv.ListSites() {
|
|
observeSiteOnlineFor(o, sv, siteID)
|
|
observeLastHeartbeatFor(o, sv, siteID)
|
|
observeSessionsFor(o, any)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
}
|
|
|
|
func observeSiteOnlineFor(o metric.Observer, sv StateView, siteID string) {
|
|
if online, ok := sv.Online(siteID); ok {
|
|
val := int64(0)
|
|
if online { val = 1 }
|
|
o.ObserveInt64(mSiteOnline, val, metric.WithAttributes(
|
|
attribute.String("site_id", getSiteID()),
|
|
))
|
|
}
|
|
}
|
|
|
|
func observeLastHeartbeatFor(o metric.Observer, sv StateView, siteID string) {
|
|
if t, ok := sv.LastHeartbeat(siteID); ok {
|
|
secs := time.Since(t).Seconds()
|
|
o.ObserveFloat64(mSiteLastHeartbeat, secs, metric.WithAttributes(
|
|
attribute.String("site_id", getSiteID()),
|
|
))
|
|
}
|
|
}
|
|
|
|
func observeSessionsFor(o metric.Observer, any interface{}) {
|
|
if tm, ok := any.(interface{ SessionsByTunnel() map[string]int64 }); ok {
|
|
for tid, n := range tm.SessionsByTunnel() {
|
|
attrs := []attribute.KeyValue{
|
|
attribute.String("site_id", getSiteID()),
|
|
}
|
|
if ShouldIncludeTunnelID() && tid != "" {
|
|
attrs = append(attrs, attribute.String("tunnel_id", tid))
|
|
}
|
|
o.ObserveInt64(mTunnelSessions, n, metric.WithAttributes(attrs...))
|
|
}
|
|
}
|
|
}
|