{- | The runtime metric instruments and the typed emit helpers the hot path records
through -- the IO layer over the pure @ecluse.*@ catalogue ("Ecluse.Core.Telemetry.Metrics").

"Ecluse.Core.Telemetry.Metrics" defines /what/ the catalogue is (the names and the closed
set of bounded labels); this module turns that catalogue into live OpenTelemetry
instruments and exposes one typed @record*@ per signal. Each helper takes only the
bounded label values its metric carries -- never a free identifier -- so the
bounded-label discipline is enforced at the call site by the type, and the attribute
set an instrument ever sees is drawn from a small fixed product of the label domains.

== Gating: inert when telemetry is off

'newMetrics' builds the instruments from the 'Telemetry' handle's meter provider when
telemetry is enabled, and from the SDK's __no-op meter provider__ when it is not. A
no-op instrument discards every measurement, so the @record*@ helpers are called
__unconditionally__ on the hot path and are genuinely inert when telemetry is off -- no
per-call branch, no provider fabricated at the edge. The 'Metrics' handle is therefore
total: every signal has a real instrument whichever posture the proxy is in. The no-op
recording function ignores its arguments, so the 'metricAttributes' a call passes is
never forced -- no attribute set is materialised when telemetry is off, only a thunk that
is discarded.

The catalogue and the cardinality rule are described in
@docs\/architecture\/observability.md@.
-}
module Ecluse.Telemetry.Instruments (
    -- * The instrument handle
    Metrics,
    newMetrics,

    -- * The core recording ports
    metricsPortOf,
    workerMetricsPortOf,

    -- * Timing
    timedSeconds,

    -- * Serve decision
    recordServeDecision,
    recordServeAdmissionInFlight,
    recordServeAdmissionQueued,

    -- * Rule gate
    recordRuleDenial,
    recordRuleEvalDuration,
    recordRuleEffectfulFailure,
    recordBreakerState,

    -- * Upstream fetch (data plane)
    recordUpstreamFetch,
    recordUpstreamFetchError,

    -- * Metadata cache
    recordCacheRequest,
    recordCacheEntries,

    -- * Mirror
    recordMirrorEnqueued,
    recordMirrorEnqueueFailure,
    recordMirrorJobProcessed,
    recordMirrorPublishDuration,

    -- * Credentials
    recordCredentialRefresh,
    recordCredentialTokenTtl,
) where

import OpenTelemetry.Metric.Core (
    Counter (counterAdd),
    Gauge (gaugeRecord),
    Histogram (histogramRecord),
    Meter,
    MeterProvider,
    UpDownCounter (upDownCounterAdd),
    defaultAdvisoryParameters,
    getMeter,
    meterCreateCounterInt64,
    meterCreateGaugeInt64,
    meterCreateHistogram,
    meterCreateUpDownCounterInt64,
    noopMeterProvider,
 )

import Ecluse.Core.Telemetry.Metrics (
    BreakerSource,
    BreakerState,
    CacheResult,
    Cause,
    CredentialResult,
    Decision,
    Label (LBreakerSource, LCacheResult, LCause, LCredentialResult, LDecision, LMirrorResult, LProvider, LReasonClass, LRule, LStatusClass, LTier, LUpstream),
    MetricName (..),
    MirrorResult,
    Provider,
    ReasonClass,
    StatusClass,
    Tier,
    Upstream,
    breakerStateCode,
    metricAttributes,
    metricName,
 )
import Ecluse.Core.Telemetry.Record (MetricsPort (..), WorkerMetricsPort (..), timedSeconds)
import Ecluse.Telemetry (Telemetry, telemetryMeterProvider)

{- | The live metric instruments, one per @ecluse.*@ signal, created against a single
meter. Opaque: built with 'newMetrics' and recorded through the @record*@ helpers.
Held in the composition root ("Ecluse.Env") so every layer records through the same
instruments.

@http.server.request.duration@ is __not__ here: the WAI instrumentation emits it from
the server-span meter ("Ecluse.Telemetry.Tracing"), so duplicating it would double the
series. Advisory-sync and breaker instruments the catalogue names are present; their
wiring is layered on as the subsystems that own them are built.
-}
data Metrics = Metrics
    { Metrics -> Counter Int64
mServeDecision :: Counter Int64
    , Metrics -> UpDownCounter Int64
mServeAdmissionInFlight :: UpDownCounter Int64
    , Metrics -> Counter Int64
mServeAdmissionQueued :: Counter Int64
    , Metrics -> Counter Int64
mRuleDenials :: Counter Int64
    , Metrics -> Histogram
mRuleEvalDuration :: Histogram
    , Metrics -> Counter Int64
mRuleEffectfulFailures :: Counter Int64
    , Metrics -> Gauge Int64
mRuleBreakerState :: Gauge Int64
    , Metrics -> Histogram
mUpstreamFetchDuration :: Histogram
    , Metrics -> Counter Int64
mUpstreamFetchErrors :: Counter Int64
    , Metrics -> Counter Int64
mMetadataCacheRequests :: Counter Int64
    , Metrics -> Gauge Int64
mMetadataCacheEntries :: Gauge Int64
    , Metrics -> Gauge Int64
mMetadataCacheResidentBytes :: Gauge Int64
    , Metrics -> Gauge Int64
mSingleVersionCacheResidentBytes :: Gauge Int64
    , Metrics -> Gauge Int64
mAssembledCacheResidentBytes :: Gauge Int64
    , Metrics -> Counter Int64
mMirrorEnqueued :: Counter Int64
    , Metrics -> Counter Int64
mMirrorEnqueueFailures :: Counter Int64
    , Metrics -> Counter Int64
mMirrorJobsProcessed :: Counter Int64
    , Metrics -> Histogram
mMirrorPublishDuration :: Histogram
    , Metrics -> Counter Int64
mCredentialRefresh :: Counter Int64
    , Metrics -> Gauge Int64
mCredentialTokenTtlSeconds :: Gauge Int64
    }

{- | Build the metric instruments from a 'Telemetry' handle. When telemetry is enabled
the instruments are created on the handle's meter provider; when it is disabled they
are created on the SDK's no-op meter provider, so every recorded measurement is
discarded and the @record*@ helpers are inert.

Instruments are created once here (at composition) rather than per measurement, so the
hot path only records.
-}
newMetrics :: Telemetry -> IO Metrics
newMetrics :: Telemetry -> IO Metrics
newMetrics Telemetry
telemetry = do
    let meterProvider :: MeterProvider
        meterProvider :: MeterProvider
meterProvider = MeterProvider -> Maybe MeterProvider -> MeterProvider
forall a. a -> Maybe a -> a
fromMaybe MeterProvider
noopMeterProvider (Telemetry -> Maybe MeterProvider
telemetryMeterProvider Telemetry
telemetry)
    meter <- MeterProvider -> InstrumentationLibrary -> IO Meter
getMeter MeterProvider
meterProvider InstrumentationLibrary
forall s. IsString s => s
ecluseScope
    Metrics
        <$> counter meter ServeDecision "{decision}" "serve decisions by admit/deny/unavailable"
        <*> upDownCounter meter ServeAdmissionInFlight "{request}" "in-flight metadata parses"
        <*> counter meter ServeAdmissionQueued "{request}" "admissions that waited for a slot"
        <*> counter meter RuleDenials "{denial}" "rule denials by rule and reason class"
        <*> histogram meter RuleEvalDuration "rule-evaluation latency by tier"
        <*> counter meter RuleEffectfulFailures "{failure}" "effectful-rule failures by cause"
        <*> gauge meter RuleBreakerState "circuit-breaker state by source (0 closed, 1 half-open, 2 open)"
        <*> histogram meter UpstreamFetchDuration "upstream metadata-fetch latency by upstream and status class"
        <*> counter meter UpstreamFetchErrors "{error}" "upstream metadata-fetch errors by upstream and cause"
        <*> counter meter MetadataCacheRequests "{request}" "metadata-cache lookups by hit/miss"
        <*> gauge meter MetadataCacheEntries "metadata-cache occupancy"
        <*> gauge meter MetadataCacheResidentBytes "full-packument metadata-cache resident bytes"
        <*> gauge meter SingleVersionCacheResidentBytes "single-version metadata-cache resident bytes"
        <*> gauge meter AssembledCacheResidentBytes "assembled-representation store resident bytes"
        <*> counter meter MirrorEnqueued "{job}" "mirror jobs enqueued"
        <*> counter meter MirrorEnqueueFailures "{failure}" "mirror enqueue failures"
        <*> counter meter MirrorJobsProcessed "{job}" "mirror jobs processed by result"
        <*> histogram meter MirrorPublishDuration "mirror publish latency"
        <*> counter meter CredentialRefresh "{refresh}" "credential refreshes by result and provider"
        <*> gauge meter CredentialTokenTtlSeconds "remaining outbound-token lifetime by provider"

counter :: Meter -> MetricName -> Text -> Text -> IO (Counter Int64)
counter :: Meter -> MetricName -> Text -> Text -> IO (Counter Int64)
counter Meter
meter MetricName
name Text
unit Text
description =
    Meter
-> Text
-> Maybe Text
-> Maybe Text
-> AdvisoryParameters
-> IO (Counter Int64)
meterCreateCounterInt64 Meter
meter (MetricName -> Text
metricName MetricName
name) (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
unit) (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
description) AdvisoryParameters
defaultAdvisoryParameters

histogram :: Meter -> MetricName -> Text -> IO Histogram
histogram :: Meter -> MetricName -> Text -> IO Histogram
histogram Meter
meter MetricName
name Text
description =
    Meter
-> Text
-> Maybe Text
-> Maybe Text
-> AdvisoryParameters
-> IO Histogram
meterCreateHistogram Meter
meter (MetricName -> Text
metricName MetricName
name) (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
"s") (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
description) AdvisoryParameters
defaultAdvisoryParameters

upDownCounter :: Meter -> MetricName -> Text -> Text -> IO (UpDownCounter Int64)
upDownCounter :: Meter -> MetricName -> Text -> Text -> IO (UpDownCounter Int64)
upDownCounter Meter
meter MetricName
name Text
unit Text
description =
    Meter
-> Text
-> Maybe Text
-> Maybe Text
-> AdvisoryParameters
-> IO (UpDownCounter Int64)
meterCreateUpDownCounterInt64 Meter
meter (MetricName -> Text
metricName MetricName
name) (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
unit) (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
description) AdvisoryParameters
defaultAdvisoryParameters

gauge :: Meter -> MetricName -> Text -> IO (Gauge Int64)
gauge :: Meter -> MetricName -> Text -> IO (Gauge Int64)
gauge Meter
meter MetricName
name Text
description =
    Meter
-> Text
-> Maybe Text
-> Maybe Text
-> AdvisoryParameters
-> IO (Gauge Int64)
meterCreateGaugeInt64 Meter
meter (MetricName -> Text
metricName MetricName
name) Maybe Text
forall a. Maybe a
Nothing (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
description) AdvisoryParameters
defaultAdvisoryParameters

-- The instrumentation scope the instruments are created under: this service's name,
-- so the metrics are attributed to Écluse (the same scope the hand-added spans use).
-- Kept polymorphic over 'IsString' so the @InstrumentationLibrary@ type need not be
-- named (it is not exported from the metric API surface).
ecluseScope :: (IsString s) => s
ecluseScope :: forall s. IsString s => s
ecluseScope = s
"ecluse"

{- | Project the OpenTelemetry-backed instruments onto the core 'MetricsPort' the
serve path ("Ecluse.Core.Server.Pipeline") records through. Each field is the matching
@record*@ helper partially applied to the instrument handle, so the port is exactly
this module's recording behaviour behind the core interface -- inert when telemetry is
off, since the instruments are. ('timedSeconds' is re-exported from the port module, so
the data-plane timing util has one home beside the durations it feeds.)
-}
metricsPortOf :: Metrics -> MetricsPort
metricsPortOf :: Metrics -> MetricsPort
metricsPortOf Metrics
m =
    MetricsPort
        { mpServeDecision :: Decision -> IO ()
mpServeDecision = Metrics -> Decision -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> Decision -> m ()
recordServeDecision Metrics
m
        , mpServeAdmissionInFlight :: Int -> IO ()
mpServeAdmissionInFlight = Metrics -> Int -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> Int -> m ()
recordServeAdmissionInFlight Metrics
m
        , mpServeAdmissionQueued :: IO ()
mpServeAdmissionQueued = Metrics -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> m ()
recordServeAdmissionQueued Metrics
m
        , mpRuleDenial :: Maybe Text -> ReasonClass -> IO ()
mpRuleDenial = Metrics -> Maybe Text -> ReasonClass -> IO ()
forall (m :: * -> *).
MonadIO m =>
Metrics -> Maybe Text -> ReasonClass -> m ()
recordRuleDenial Metrics
m
        , mpRuleEvalDuration :: Tier -> Double -> IO ()
mpRuleEvalDuration = Metrics -> Tier -> Double -> IO ()
forall (m :: * -> *).
MonadIO m =>
Metrics -> Tier -> Double -> m ()
recordRuleEvalDuration Metrics
m
        , mpRuleEffectfulFailure :: Cause -> IO ()
mpRuleEffectfulFailure = Metrics -> Cause -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> Cause -> m ()
recordRuleEffectfulFailure Metrics
m
        , mpUpstreamFetch :: Upstream -> StatusClass -> Double -> IO ()
mpUpstreamFetch = Metrics -> Upstream -> StatusClass -> Double -> IO ()
forall (m :: * -> *).
MonadIO m =>
Metrics -> Upstream -> StatusClass -> Double -> m ()
recordUpstreamFetch Metrics
m
        , mpUpstreamFetchError :: Upstream -> Cause -> IO ()
mpUpstreamFetchError = Metrics -> Upstream -> Cause -> IO ()
forall (m :: * -> *).
MonadIO m =>
Metrics -> Upstream -> Cause -> m ()
recordUpstreamFetchError Metrics
m
        , mpCacheRequest :: CacheResult -> IO ()
mpCacheRequest = Metrics -> CacheResult -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> CacheResult -> m ()
recordCacheRequest Metrics
m
        , mpCacheEntries :: Int -> IO ()
mpCacheEntries = Metrics -> Int -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> Int -> m ()
recordCacheEntries Metrics
m
        , mpCacheResidentBytes :: Int -> IO ()
mpCacheResidentBytes = Metrics -> Int -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> Int -> m ()
recordCacheResidentBytes Metrics
m
        , mpVersionCacheResidentBytes :: Int -> IO ()
mpVersionCacheResidentBytes = Metrics -> Int -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> Int -> m ()
recordVersionCacheResidentBytes Metrics
m
        , mpAssembledCacheResidentBytes :: Int -> IO ()
mpAssembledCacheResidentBytes = Metrics -> Int -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> Int -> m ()
recordAssembledCacheResidentBytes Metrics
m
        , mpMirrorEnqueued :: IO ()
mpMirrorEnqueued = Metrics -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> m ()
recordMirrorEnqueued Metrics
m
        , mpMirrorEnqueueFailure :: IO ()
mpMirrorEnqueueFailure = Metrics -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> m ()
recordMirrorEnqueueFailure Metrics
m
        }

{- | Project the OpenTelemetry-backed instruments onto the core 'WorkerMetricsPort' the
mirror worker ("Ecluse.Core.Worker") records through. Each field is the matching
@record*@ helper partially applied to the instrument handle, so the port is exactly this
module's recording behaviour behind the core interface -- inert when telemetry is off,
since the instruments are.
-}
workerMetricsPortOf :: Metrics -> WorkerMetricsPort
workerMetricsPortOf :: Metrics -> WorkerMetricsPort
workerMetricsPortOf Metrics
m =
    WorkerMetricsPort
        { wmpMirrorJobProcessed :: MirrorResult -> IO ()
wmpMirrorJobProcessed = Metrics -> MirrorResult -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> MirrorResult -> m ()
recordMirrorJobProcessed Metrics
m
        , wmpMirrorPublishDuration :: Double -> IO ()
wmpMirrorPublishDuration = Metrics -> Double -> IO ()
forall (m :: * -> *). MonadIO m => Metrics -> Double -> m ()
recordMirrorPublishDuration Metrics
m
        }

-- | Record one serve decision (@ecluse.serve.decision@): admit, deny, or unavailable.
recordServeDecision :: (MonadIO m) => Metrics -> Decision -> m ()
recordServeDecision :: forall (m :: * -> *). MonadIO m => Metrics -> Decision -> m ()
recordServeDecision Metrics
m Decision
decision =
    Counter Int64 -> [Label] -> m ()
forall (m :: * -> *). MonadIO m => Counter Int64 -> [Label] -> m ()
addOne (Metrics -> Counter Int64
mServeDecision Metrics
m) [Decision -> Label
LDecision Decision
decision]

-- | Record a change in in-flight metadata parses (@ecluse.serve.admission.in_flight@).
recordServeAdmissionInFlight :: (MonadIO m) => Metrics -> Int -> m ()
recordServeAdmissionInFlight :: forall (m :: * -> *). MonadIO m => Metrics -> Int -> m ()
recordServeAdmissionInFlight Metrics
m Int
delta =
    UpDownCounter Int64 -> Int64 -> [Label] -> m ()
forall (m :: * -> *).
MonadIO m =>
UpDownCounter Int64 -> Int64 -> [Label] -> m ()
addDelta (Metrics -> UpDownCounter Int64
mServeAdmissionInFlight Metrics
m) (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
delta) []

-- | Record one admission that waited for a slot before proceeding (@ecluse.serve.admission.queued@).
recordServeAdmissionQueued :: (MonadIO m) => Metrics -> m ()
recordServeAdmissionQueued :: forall (m :: * -> *). MonadIO m => Metrics -> m ()
recordServeAdmissionQueued Metrics
m =
    Counter Int64 -> [Label] -> m ()
forall (m :: * -> *). MonadIO m => Counter Int64 -> [Label] -> m ()
addOne (Metrics -> Counter Int64
mServeAdmissionQueued Metrics
m) []

{- | Record one rule denial (@ecluse.rule.denials@) by reason class and, for a policy
denial, the deciding rule. A non-policy refusal (a missing-integrity or upstream cause)
carries the reason class alone -- no rule attributed it, so none is labelled.
-}
recordRuleDenial :: (MonadIO m) => Metrics -> Maybe Text -> ReasonClass -> m ()
recordRuleDenial :: forall (m :: * -> *).
MonadIO m =>
Metrics -> Maybe Text -> ReasonClass -> m ()
recordRuleDenial Metrics
m Maybe Text
rule ReasonClass
reasonClass =
    Counter Int64 -> [Label] -> m ()
forall (m :: * -> *). MonadIO m => Counter Int64 -> [Label] -> m ()
addOne (Metrics -> Counter Int64
mRuleDenials Metrics
m) ([Label] -> (Text -> [Label]) -> Maybe Text -> [Label]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (\Text
name -> [Text -> Label
LRule Text
name]) Maybe Text
rule [Label] -> [Label] -> [Label]
forall a. Semigroup a => a -> a -> a
<> [ReasonClass -> Label
LReasonClass ReasonClass
reasonClass])

-- | Record a rule-evaluation latency sample (@ecluse.rule.eval.duration@) by tier.
recordRuleEvalDuration :: (MonadIO m) => Metrics -> Tier -> Double -> m ()
recordRuleEvalDuration :: forall (m :: * -> *).
MonadIO m =>
Metrics -> Tier -> Double -> m ()
recordRuleEvalDuration Metrics
m Tier
tier Double
seconds =
    Histogram -> Double -> [Label] -> m ()
forall (m :: * -> *).
MonadIO m =>
Histogram -> Double -> [Label] -> m ()
record (Metrics -> Histogram
mRuleEvalDuration Metrics
m) Double
seconds [Tier -> Label
LTier Tier
tier]

-- | Record one effectful-rule failure (@ecluse.rule.effectful.failures@) by cause.
recordRuleEffectfulFailure :: (MonadIO m) => Metrics -> Cause -> m ()
recordRuleEffectfulFailure :: forall (m :: * -> *). MonadIO m => Metrics -> Cause -> m ()
recordRuleEffectfulFailure Metrics
m Cause
cause =
    Counter Int64 -> [Label] -> m ()
forall (m :: * -> *). MonadIO m => Counter Int64 -> [Label] -> m ()
addOne (Metrics -> Counter Int64
mRuleEffectfulFailures Metrics
m) [Cause -> Label
LCause Cause
cause]

{- | Record the current circuit-breaker state (@ecluse.rule.breaker.state@) for a
source as the gauge's bounded ordinal (0 closed, 1 half-open, 2 open).
-}
recordBreakerState :: (MonadIO m) => Metrics -> BreakerSource -> BreakerState -> m ()
recordBreakerState :: forall (m :: * -> *).
MonadIO m =>
Metrics -> BreakerSource -> BreakerState -> m ()
recordBreakerState Metrics
m BreakerSource
source BreakerState
breakerState =
    Gauge Int64 -> Int64 -> [Label] -> m ()
forall (m :: * -> *).
MonadIO m =>
Gauge Int64 -> Int64 -> [Label] -> m ()
set (Metrics -> Gauge Int64
mRuleBreakerState Metrics
m) (BreakerState -> Int64
breakerStateCode BreakerState
breakerState) [BreakerSource -> Label
LBreakerSource BreakerSource
source]

{- | Record an upstream metadata-fetch latency sample (@ecluse.upstream.fetch.duration@)
by which upstream was fetched and the response's status class.
-}
recordUpstreamFetch :: (MonadIO m) => Metrics -> Upstream -> StatusClass -> Double -> m ()
recordUpstreamFetch :: forall (m :: * -> *).
MonadIO m =>
Metrics -> Upstream -> StatusClass -> Double -> m ()
recordUpstreamFetch Metrics
m Upstream
upstream StatusClass
statusClass Double
seconds =
    Histogram -> Double -> [Label] -> m ()
forall (m :: * -> *).
MonadIO m =>
Histogram -> Double -> [Label] -> m ()
record (Metrics -> Histogram
mUpstreamFetchDuration Metrics
m) Double
seconds [Upstream -> Label
LUpstream Upstream
upstream, StatusClass -> Label
LStatusClass StatusClass
statusClass]

{- | Record one upstream metadata-fetch error (@ecluse.upstream.fetch.errors@) by
which upstream and the bounded cause.
-}
recordUpstreamFetchError :: (MonadIO m) => Metrics -> Upstream -> Cause -> m ()
recordUpstreamFetchError :: forall (m :: * -> *).
MonadIO m =>
Metrics -> Upstream -> Cause -> m ()
recordUpstreamFetchError Metrics
m Upstream
upstream Cause
cause =
    Counter Int64 -> [Label] -> m ()
forall (m :: * -> *). MonadIO m => Counter Int64 -> [Label] -> m ()
addOne (Metrics -> Counter Int64
mUpstreamFetchErrors Metrics
m) [Upstream -> Label
LUpstream Upstream
upstream, Cause -> Label
LCause Cause
cause]

-- | Record one metadata-cache lookup (@ecluse.metadata_cache.requests@) as a hit or miss.
recordCacheRequest :: (MonadIO m) => Metrics -> CacheResult -> m ()
recordCacheRequest :: forall (m :: * -> *). MonadIO m => Metrics -> CacheResult -> m ()
recordCacheRequest Metrics
m CacheResult
result =
    Counter Int64 -> [Label] -> m ()
forall (m :: * -> *). MonadIO m => Counter Int64 -> [Label] -> m ()
addOne (Metrics -> Counter Int64
mMetadataCacheRequests Metrics
m) [CacheResult -> Label
LCacheResult CacheResult
result]

-- | Record the metadata cache's current occupancy (@ecluse.metadata_cache.entries@).
recordCacheEntries :: (MonadIO m) => Metrics -> Int -> m ()
recordCacheEntries :: forall (m :: * -> *). MonadIO m => Metrics -> Int -> m ()
recordCacheEntries Metrics
m Int
entries =
    Gauge Int64 -> Int64 -> [Label] -> m ()
forall (m :: * -> *).
MonadIO m =>
Gauge Int64 -> Int64 -> [Label] -> m ()
set (Metrics -> Gauge Int64
mMetadataCacheEntries Metrics
m) (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
entries) []

{- | Record the full-packument metadata cache's resident bytes
(@ecluse.metadata_cache.resident_bytes@).
-}
recordCacheResidentBytes :: (MonadIO m) => Metrics -> Int -> m ()
recordCacheResidentBytes :: forall (m :: * -> *). MonadIO m => Metrics -> Int -> m ()
recordCacheResidentBytes Metrics
m Int
bytes =
    Gauge Int64 -> Int64 -> [Label] -> m ()
forall (m :: * -> *).
MonadIO m =>
Gauge Int64 -> Int64 -> [Label] -> m ()
set (Metrics -> Gauge Int64
mMetadataCacheResidentBytes Metrics
m) (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
bytes) []

{- | Record the single-version metadata cache's resident bytes
(@ecluse.metadata_cache.version.resident_bytes@).
-}
recordVersionCacheResidentBytes :: (MonadIO m) => Metrics -> Int -> m ()
recordVersionCacheResidentBytes :: forall (m :: * -> *). MonadIO m => Metrics -> Int -> m ()
recordVersionCacheResidentBytes Metrics
m Int
bytes =
    Gauge Int64 -> Int64 -> [Label] -> m ()
forall (m :: * -> *).
MonadIO m =>
Gauge Int64 -> Int64 -> [Label] -> m ()
set (Metrics -> Gauge Int64
mSingleVersionCacheResidentBytes Metrics
m) (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
bytes) []

{- | Record the assembled-representation store's resident bytes
(@ecluse.metadata_cache.assembled.resident_bytes@).
-}
recordAssembledCacheResidentBytes :: (MonadIO m) => Metrics -> Int -> m ()
recordAssembledCacheResidentBytes :: forall (m :: * -> *). MonadIO m => Metrics -> Int -> m ()
recordAssembledCacheResidentBytes Metrics
m Int
bytes =
    Gauge Int64 -> Int64 -> [Label] -> m ()
forall (m :: * -> *).
MonadIO m =>
Gauge Int64 -> Int64 -> [Label] -> m ()
set (Metrics -> Gauge Int64
mAssembledCacheResidentBytes Metrics
m) (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
bytes) []

-- | Record one mirror job enqueued (@ecluse.mirror.enqueued@).
recordMirrorEnqueued :: (MonadIO m) => Metrics -> m ()
recordMirrorEnqueued :: forall (m :: * -> *). MonadIO m => Metrics -> m ()
recordMirrorEnqueued Metrics
m = Counter Int64 -> [Label] -> m ()
forall (m :: * -> *). MonadIO m => Counter Int64 -> [Label] -> m ()
addOne (Metrics -> Counter Int64
mMirrorEnqueued Metrics
m) []

-- | Record one mirror enqueue failure (@ecluse.mirror.enqueue.failures@).
recordMirrorEnqueueFailure :: (MonadIO m) => Metrics -> m ()
recordMirrorEnqueueFailure :: forall (m :: * -> *). MonadIO m => Metrics -> m ()
recordMirrorEnqueueFailure Metrics
m = Counter Int64 -> [Label] -> m ()
forall (m :: * -> *). MonadIO m => Counter Int64 -> [Label] -> m ()
addOne (Metrics -> Counter Int64
mMirrorEnqueueFailures Metrics
m) []

-- | Record one processed mirror job (@ecluse.mirror.jobs.processed@) by its result.
recordMirrorJobProcessed :: (MonadIO m) => Metrics -> MirrorResult -> m ()
recordMirrorJobProcessed :: forall (m :: * -> *). MonadIO m => Metrics -> MirrorResult -> m ()
recordMirrorJobProcessed Metrics
m MirrorResult
result =
    Counter Int64 -> [Label] -> m ()
forall (m :: * -> *). MonadIO m => Counter Int64 -> [Label] -> m ()
addOne (Metrics -> Counter Int64
mMirrorJobsProcessed Metrics
m) [MirrorResult -> Label
LMirrorResult MirrorResult
result]

-- | Record a mirror publish latency sample (@ecluse.mirror.publish.duration@).
recordMirrorPublishDuration :: (MonadIO m) => Metrics -> Double -> m ()
recordMirrorPublishDuration :: forall (m :: * -> *). MonadIO m => Metrics -> Double -> m ()
recordMirrorPublishDuration Metrics
m Double
seconds =
    Histogram -> Double -> [Label] -> m ()
forall (m :: * -> *).
MonadIO m =>
Histogram -> Double -> [Label] -> m ()
record (Metrics -> Histogram
mMirrorPublishDuration Metrics
m) Double
seconds []

-- | Record one credential refresh (@ecluse.credential.refresh@) by result and provider.
recordCredentialRefresh :: (MonadIO m) => Metrics -> Provider -> CredentialResult -> m ()
recordCredentialRefresh :: forall (m :: * -> *).
MonadIO m =>
Metrics -> Provider -> CredentialResult -> m ()
recordCredentialRefresh Metrics
m Provider
provider CredentialResult
result =
    Counter Int64 -> [Label] -> m ()
forall (m :: * -> *). MonadIO m => Counter Int64 -> [Label] -> m ()
addOne (Metrics -> Counter Int64
mCredentialRefresh Metrics
m) [Provider -> Label
LProvider Provider
provider, CredentialResult -> Label
LCredentialResult CredentialResult
result]

{- | Record an outbound token's remaining lifetime in whole seconds
(@ecluse.credential.token.ttl.seconds@) by provider, so a stuck refresh alarms as the
gauge decays towards zero.
-}
recordCredentialTokenTtl :: (MonadIO m) => Metrics -> Provider -> Int -> m ()
recordCredentialTokenTtl :: forall (m :: * -> *).
MonadIO m =>
Metrics -> Provider -> Int -> m ()
recordCredentialTokenTtl Metrics
m Provider
provider Int
seconds =
    Gauge Int64 -> Int64 -> [Label] -> m ()
forall (m :: * -> *).
MonadIO m =>
Gauge Int64 -> Int64 -> [Label] -> m ()
set (Metrics -> Gauge Int64
mCredentialTokenTtlSeconds Metrics
m) (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
seconds) [Provider -> Label
LProvider Provider
provider]

-- Add one to a counter under the given bounded labels.
addOne :: (MonadIO m) => Counter Int64 -> [Label] -> m ()
addOne :: forall (m :: * -> *). MonadIO m => Counter Int64 -> [Label] -> m ()
addOne Counter Int64
instrument [Label]
labels = IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Counter Int64 -> Int64 -> Attributes -> IO ()
forall a. Counter a -> a -> Attributes -> IO ()
counterAdd Counter Int64
instrument Int64
1 ([Label] -> Attributes
metricAttributes [Label]
labels))

-- Add a delta to an up-down counter under the given bounded labels.
addDelta :: (MonadIO m) => UpDownCounter Int64 -> Int64 -> [Label] -> m ()
addDelta :: forall (m :: * -> *).
MonadIO m =>
UpDownCounter Int64 -> Int64 -> [Label] -> m ()
addDelta UpDownCounter Int64
instrument Int64
delta [Label]
labels = IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (UpDownCounter Int64 -> Int64 -> Attributes -> IO ()
forall a. UpDownCounter a -> a -> Attributes -> IO ()
upDownCounterAdd UpDownCounter Int64
instrument Int64
delta ([Label] -> Attributes
metricAttributes [Label]
labels))

-- Record a histogram measurement under the given bounded labels.
record :: (MonadIO m) => Histogram -> Double -> [Label] -> m ()
record :: forall (m :: * -> *).
MonadIO m =>
Histogram -> Double -> [Label] -> m ()
record Histogram
instrument Double
value [Label]
labels = IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Histogram -> Double -> Attributes -> IO ()
histogramRecord Histogram
instrument Double
value ([Label] -> Attributes
metricAttributes [Label]
labels))

-- Set a gauge to a value under the given bounded labels (last value wins per collect).
set :: (MonadIO m) => Gauge Int64 -> Int64 -> [Label] -> m ()
set :: forall (m :: * -> *).
MonadIO m =>
Gauge Int64 -> Int64 -> [Label] -> m ()
set Gauge Int64
instrument Int64
value [Label]
labels = IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Gauge Int64 -> Int64 -> Attributes -> IO ()
forall a. Gauge a -> a -> Attributes -> IO ()
gaugeRecord Gauge Int64
instrument Int64
value ([Label] -> Attributes
metricAttributes [Label]
labels))