{-# 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