instana-haskell-trace-sdk-0.2.0.0: src/Instana/SDK/Internal/Metrics/Deltas.hs
{-# LANGUAGE OverloadedStrings #-}
{-|
Module : Instana.SDK.Internal.Metrics.Delta
Description : Computes deltas between two metrics samples.
-}
module Instana.SDK.Internal.Metrics.Deltas
( deltaKeyList
, enrichWithDeltas
) where
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.Text (Text)
import qualified Data.Text as T
import Instana.SDK.Internal.Metrics.Sample (InstanaMetricValue,
TimedSample)
import qualified Instana.SDK.Internal.Metrics.Sample as Sample
-- |All metric keys from ekg-core that are eligible for being turned into deltas
-- (difference in value/second since the last metric collection).
deltaKeyList :: [Text]
deltaKeyList =
[ "rts.gc.bytes_allocated"
, "rts.gc.num_gcs"
, "rts.gc.num_bytes_usage_samples"
, "rts.gc.cumulative_bytes_used"
, "rts.gc.bytes_copied"
, "rts.gc.init_cpu_ms"
, "rts.gc.init_wall_ms"
, "rts.gc.mutator_cpu_ms"
, "rts.gc.mutator_wall_ms"
, "rts.gc.gc_cpu_ms"
, "rts.gc.gc_wall_ms"
, "rts.gc.cpu_ms"
, "rts.gc.wall_ms"
]
-- |Calculates deltas and adds them to the sample.
enrichWithDeltas :: TimedSample -> TimedSample -> TimedSample
enrichWithDeltas previousSample currentSample =
let
currentTimestamp = Sample.timestamp currentSample
previousTimestamp = Sample.timestamp previousSample
deltaT = currentTimestamp - previousTimestamp
previousMetrics = Sample.sample previousSample
currentMetrics = Sample.sample currentSample
metricsWithDeltas =
HashMap.foldrWithKey
(addDeltaToSample deltaT previousMetrics)
currentMetrics
currentMetrics
in
Sample.mkTimedSample metricsWithDeltas currentTimestamp
addDeltaToSample ::
Int
-> HashMap Text InstanaMetricValue
-> Text
-> InstanaMetricValue
-> HashMap Text InstanaMetricValue
-> HashMap Text InstanaMetricValue
addDeltaToSample deltaT previousMetrics metricKey currentMetricValue currentMetrics =
if not (elem metricKey deltaKeyList) then
currentMetrics
else
let
previousMetricValue = HashMap.lookup metricKey previousMetrics
in
case (previousMetricValue, currentMetricValue) of
-- We are only interested in integer metrics here. Reason: The ekg package
-- only emits integer values and only the deltas are fractional values. We
-- never want to compute a delta of two deltas, that wouldn't make sense.
(Just (Sample.IntegralValue previousValue),
Sample.IntegralValue currentValue) ->
addNormalizedDelta
metricKey
deltaT
(currentValue - previousValue)
currentMetrics
-- The metric is present in the current sample but was not present in the
-- previous sample, that must be the first sample taken ever. Assume zero
-- for the previous value.
(Nothing, Sample.IntegralValue currentValue) ->
addNormalizedDelta
metricKey
deltaT
currentValue
currentMetrics
_ ->
currentMetrics
addNormalizedDelta ::
Text
-> Int
-> Int
-> HashMap Text InstanaMetricValue
-> HashMap Text InstanaMetricValue
addNormalizedDelta metricKey deltaT deltaV metrics =
let
deltaKey = T.append metricKey "_delta"
-- Normalize difference to a one second timespan, no matter how much time
-- elapsed between taking the two samples. The provided timestamps (and thus
-- deltaT) are in milliseconds.
normalizedDeltaV =
Sample.FractionalValue $
(fromIntegral deltaV / fromIntegral deltaT) * 1000
in
HashMap.insert
deltaKey
normalizedDeltaV
metrics