mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
Client:
- ci.yml: patch auto-generated events.ts to rename Event -> _Event
so @typescript-eslint/no-unused-vars does not fail on generated code
Server (gocritic):
- service/channel.go: rangeValCopy (line 74), elseif (line 174)
- service/message.go: rangeValCopy (line 648), elseif (lines 438, 496)
- ws/emit.go: caseOrder — ChannelEvent before BroadcastAllEvent
- ws/handlers_command_test.go: stringXbytes — use bytes.Equal
- ws/pubsub_test.go: stringXbytes — use bytes.Equal
Server (nilerr):
- service/channel.go: nolint:nilerr for intentional silent drops in HandleTyping
Server (gosec):
- plugin/host_ui.go: G703 nolint — path already sanitized above
- plugin/registry.go: G302 — tighten plugin file permissions 0o640 → 0o600
- ws/hub.go, ws/serve.go: G115 nolint — seq counters never reach MaxInt64
Server (unused/unparam):
- plugin/registry.go: nolint:unused for wazero-tagged module field
- telemetry/metrics.go: nolint:unused for otel-tagged resetAppMetricsForInit
- ws/command.go: nolint:unparam for map entries whose error return is always nil
Server (staticcheck ST1000/ST1020):
- Add blank line before package declarations in phase-comment files
(api/plugins_handler.go, plugin/host_{commands,events,http}.go,
telemetry/metrics.go, telemetry/middleware.go,
telemetry/telemetry_default.go, ws/event_persister.go)
- Fix GlobalTracer/GlobalMeter doc comments to start with function name
81 lines
3.5 KiB
Go
81 lines
3.5 KiB
Go
// Phase B Step 8 — declared application metrics.
|
|
//
|
|
// Instruments are constructed lazily against the global Provider so callers
|
|
// don't need to thread a Meter through every constructor. Hot-path callers
|
|
// should cache the returned instrument in a struct field rather than calling
|
|
// these helpers per request — they take a sync.RWMutex to read the global
|
|
// provider and the cost adds up at high throughput.
|
|
|
|
package telemetry
|
|
|
|
import "sync"
|
|
|
|
const (
|
|
scopeWS = "github.com/owncord/server/ws"
|
|
scopeService = "github.com/owncord/server/service"
|
|
scopeDB = "github.com/owncord/server/db"
|
|
scopeVoice = "github.com/owncord/server/voice"
|
|
)
|
|
|
|
// AppMetrics is the canonical bundle of meters used across the server. Build
|
|
// it once at startup with NewAppMetrics() and stash it on the relevant
|
|
// long-lived structs (Hub, services, etc).
|
|
type AppMetrics struct {
|
|
WSMessagesTotal Counter
|
|
WSActiveConnections Gauge
|
|
WSBroadcastLatency Histogram
|
|
WSReconnectTierTotal Counter
|
|
WSEventsPersisted Counter
|
|
WSEventsDropped Counter
|
|
WSEventsPersistErrors Counter
|
|
DBQueryDurationSec Histogram
|
|
VoiceActiveSessions Gauge
|
|
VoiceParticipants Gauge
|
|
ServiceCallDurationSec Histogram
|
|
}
|
|
|
|
var (
|
|
appMetricsMu sync.Mutex
|
|
appMetricsInst *AppMetrics
|
|
)
|
|
|
|
// NewAppMetrics returns a process-wide AppMetrics, lazily constructed against
|
|
// the current global provider. Calling it multiple times returns the same
|
|
// instance until resetAppMetricsForInit() is called (which Init uses after
|
|
// swapping the global provider so instruments re-bind to the new meter).
|
|
func NewAppMetrics() *AppMetrics {
|
|
appMetricsMu.Lock()
|
|
defer appMetricsMu.Unlock()
|
|
if appMetricsInst != nil {
|
|
return appMetricsInst
|
|
}
|
|
ws := GlobalMeter(scopeWS)
|
|
svc := GlobalMeter(scopeService)
|
|
db := GlobalMeter(scopeDB)
|
|
voice := GlobalMeter(scopeVoice)
|
|
appMetricsInst = &AppMetrics{
|
|
WSMessagesTotal: ws.Counter("ws_messages_total", "WebSocket messages broadcast"),
|
|
WSActiveConnections: ws.Gauge("ws_active_connections", "Currently connected WebSocket clients"),
|
|
WSBroadcastLatency: ws.Histogram("ws_broadcast_latency_seconds", "Wall-clock seconds from enqueue to fanout completion", "s"),
|
|
WSReconnectTierTotal: ws.Counter("ws_reconnect_tier_total", "Reconnection replay tier hits, attribute tier=buffer|db|full"),
|
|
WSEventsPersisted: ws.Counter("ws_events_persisted_total", "Events written to the cold-tier event log"),
|
|
WSEventsDropped: ws.Counter("ws_events_dropped_total", "Events dropped because the persister queue was full"),
|
|
WSEventsPersistErrors: ws.Counter("ws_events_persist_errors_total", "PersistEvent calls that returned an error from the underlying store"),
|
|
DBQueryDurationSec: db.Histogram("db_query_duration_seconds", "Per-query wall time", "s"),
|
|
VoiceActiveSessions: voice.Gauge("voice_active_sessions", "Active LiveKit rooms"),
|
|
VoiceParticipants: voice.Gauge("voice_participants", "Connected LiveKit participants across all rooms"),
|
|
ServiceCallDurationSec: svc.Histogram("service_call_duration_seconds", "Service-layer method execution time", "s"),
|
|
}
|
|
return appMetricsInst
|
|
}
|
|
|
|
// resetAppMetricsForInit drops the cached AppMetrics bundle so the next
|
|
// NewAppMetrics() call re-binds instruments against whatever provider is now
|
|
// global. The real OTel Init uses this to migrate from the no-op provider
|
|
// installed by the package-level init() to the SDK-backed one.
|
|
func resetAppMetricsForInit() { //nolint:unused // called by otel-tagged build only
|
|
appMetricsMu.Lock()
|
|
defer appMetricsMu.Unlock()
|
|
appMetricsInst = nil
|
|
}
|