From 0c882e643c36ce45bd0b1bc4ef24a0eee93cfff3 Mon Sep 17 00:00:00 2001 From: Marsell Kukuljevic Date: Fri, 6 Mar 2026 09:31:02 +0100 Subject: [PATCH] Allow Graylog metrics plugin to use its own custom EWMA windows. --- .../2.4/graylog_input_metrics-1.0.0.mkp | Bin 6317 -> 0 bytes .../2.4/graylog_input_metrics-1.1.0.mkp | Bin 0 -> 7873 bytes .../agent_based/graylog_input_metrics.py | 122 ++++++++++++++++-- .../graphing/graylog_input_metrics.py | 36 +++--- .../rulesets/graylog_input_metrics_checks.py | 28 +++- 5 files changed, 148 insertions(+), 38 deletions(-) delete mode 100755 graylog-metrics/2.4/graylog_input_metrics-1.0.0.mkp create mode 100755 graylog-metrics/2.4/graylog_input_metrics-1.1.0.mkp diff --git a/graylog-metrics/2.4/graylog_input_metrics-1.0.0.mkp b/graylog-metrics/2.4/graylog_input_metrics-1.0.0.mkp deleted file mode 100755 index d31a007bc323c1e54af0fce533f6620d23014bda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6317 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 diff --git a/graylog-metrics/2.4/graylog_input_metrics-1.1.0.mkp b/graylog-metrics/2.4/graylog_input_metrics-1.1.0.mkp new file mode 100755 index 0000000000000000000000000000000000000000..6fbcb671bcce89917d7eca4970ada12fe23ab44b GIT binary patch literal 7873 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 A>;M1& literal 0 HcmV?d00001 diff --git a/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/agent_based/graylog_input_metrics.py b/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/agent_based/graylog_input_metrics.py index 04a0f93..d95440b 100644 --- a/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/agent_based/graylog_input_metrics.py +++ b/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/agent_based/graylog_input_metrics.py @@ -15,6 +15,8 @@ # "rs_m15_rate": 163.80530659055356}} import json +import time +import math from cmk.agent_based.v2 import ( Result, Service, @@ -23,11 +25,10 @@ from cmk.agent_based.v2 import ( CheckPlugin, AgentSection, render, + get_value_store, ) - - def parse_graylog_input_metrics(section): if not section: return {} @@ -48,15 +49,91 @@ def render_msgs(num_msgs): check_configs = [ - ("im_m1_rate", render_msgs, "Incoming messages/sec 1m"), - ("im_m5_rate", render_msgs, "5m"), - ("im_m15_rate", render_msgs, "15m"), - ("rs_m1_rate", render.bytes, "Incoming bytes/sec 1m"), - ("rs_m5_rate", render.bytes, "5m"), - ("rs_m15_rate", render.bytes, "15m"), + ("im", "small", render_msgs, "Incoming msgs/sec small window"), + ("im", "medium", render_msgs, "Incoming msgs/sec medium window"), + ("im", "large", render_msgs, "Incoming msgs/sec large window"), + ("rs", "small", render.bytes, "Incoming bytes/sec small window"), + ("rs", "medium", render.bytes, "Incoming bytes/sec medium window"), + ("rs", "large", render.bytes, "Incoming bytes/sec large window"), ] +# Since Graylog only provides 1, 5 and 15 minute windows, when we're attempting +# to (semi-)reconstruct raw values we choose the best Graylog window to use. +# It's a balance between the delay and dilution in each window. We're trying +# to pick the Graylog window with the strongest signal. +def determine_metric_input(store, window_length): + previous_timestamp = store.get("timestamp") + + if previous_timestamp is not None: + barrier = max(window_length, time.time() - previous_timestamp) + else: + barrier = window_length + + if window_length < 2.5: + return 1 + elif window_length < 10: + return 5 + else: + return 15 + + +# We take two window lengths: the window length the user specificed in the +# check rule (which can be an arbitrary length), and Graylog's native window +# (which is 1, 5 or 15 minutes). We then attempt to pull out the most recent +# value by effectively reversing the exponentially-weighted moving average +# (EMWA) window math on the Graylog window, and then creating our own EWMA +# value on our own window size. +# +# The rest of the logic is there for the various gotchas that come with these +# two transforms. +def calculate_ewma(store, window_name, window_length, graylog_window_length, newest_value): + now = time.time() + + previous_timestamp = store.get("timestamp") + previous_ewma = store.get(f"{window_name}_ewma") + previous_graylog = store.get(f"{window_name}_graylog") + + # We need to store both the time delta and the prior EWMA values for both + # Graylog and our own window so the math can work -- we're adding new + # values to a moving *average* after all. + store["timestamp"] = now + store[f"{window_name}_graylog"] = newest_value + + if previous_ewma is None: + # Provide a seed on first run to speed up convergence. + store[f"{window_name}_ewma"] = newest_value + return newest_value + + if previous_timestamp is None or previous_graylog is None: + return newest_value + + # Since both our and Graylog's windows are both in minutes, not seconds, + # the delta is in minutes too. + time_delta = (now - previous_timestamp) / 60.0 + + # Reverse Graylog's EWMA + raw_alpha = math.exp(- time_delta / graylog_window_length) + raw_value = (newest_value - raw_alpha * previous_graylog) / (1 - raw_alpha) + + # Since the above transform magnifies noise, we need to clamp here since + # the noise can cause us to drop below zero. + if raw_value < 0: + raw_value = 0 + + # Create our own EWMA + ewma_alpha = math.exp(- time_delta / window_length) + ewma = ewma_alpha * previous_ewma + (1 - ewma_alpha) * raw_value + + store[f"{window_name}_ewma"] = ewma + + # Another clamp, although probably not necessary. + if ewma >= 0: + return ewma + else: + return 0 + + def check_graylog_input_metrics(item, params, section): item_id = item.split()[-1][1:-1] input_info = section.get(item_id) @@ -75,16 +152,35 @@ def check_graylog_input_metrics(item, params, section): if input_info["input_port"]: yield Result(state=State.OK, summary="Port: %s" % input_info["input_port"]) - for metric_name, render_func, label in check_configs: - value = input_info.get(metric_name) + store = get_value_store() + + for prefix, window_name, render_func, label in check_configs: + metric_name = f"{prefix}_{window_name}_rate" + + config = params.get(metric_name, {}) + window_length = config.get("window") + levels_upper = config.get("upper") + levels_lower = config.get("lower") + + if window_length is None: + continue + + graylog_window_length = determine_metric_input(store, window_length) + metric_input = f"{prefix}_m{graylog_window_length}_rate" + + value = input_info.get(metric_input) if value is None: continue - levels_upper = params.get(metric_name, {}).get("upper") - levels_lower = params.get(metric_name, {}).get("lower") + # Since Graylog natively gives us 1m, 5m, 15m, there's no need to + # crunch math for them; we just pass those values through. + if window_length in [1, 5, 15]: + ewma_value = value + else: + ewma_value = calculate_ewma(store, metric_name, window_length, graylog_window_length, value) yield from check_levels( - value, + ewma_value, levels_upper = levels_upper, levels_lower = levels_lower, metric_name = metric_name, diff --git a/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/graphing/graylog_input_metrics.py b/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/graphing/graylog_input_metrics.py index c00edba..3e75a50 100644 --- a/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/graphing/graylog_input_metrics.py +++ b/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/graphing/graylog_input_metrics.py @@ -7,40 +7,40 @@ UNIT_BYTES = metrics.Unit(metrics.IECNotation("bytes/sec")) UNIT_MSGS = metrics.Unit(metrics.IECNotation("msgs/sec")) -metric_graylog_input_metrics_im_m1_rate = metrics.Metric( - title = Title("Incoming messages/sec (1 min)"), - name = "im_m1_rate", +metric_graylog_input_metrics_im_small_rate = metrics.Metric( + title = Title("Incoming messages/sec (small window)"), + name = "im_small_rate", unit = UNIT_BYTES, color = metrics.Color.LIGHT_GREEN, ) -metric_graylog_input_metrics_im_m5_rate = metrics.Metric( - title = Title("Incoming messages/sec (5 min)"), - name = "im_m5_rate", +metric_graylog_input_metrics_im_medium_rate = metrics.Metric( + title = Title("Incoming messages/sec (medium window)"), + name = "im_medium_rate", unit = UNIT_BYTES, color = metrics.Color.GREEN, ) -metric_graylog_input_metrics_im_m15_rate = metrics.Metric( - title = Title("Incoming messages/sec (15 min)"), - name = "im_m15_rate", +metric_graylog_input_metrics_im_large_rate = metrics.Metric( + title = Title("Incoming messages/sec (large window)"), + name = "im_large_rate", unit = UNIT_BYTES, color = metrics.Color.DARK_GREEN, ) -metric_graylog_input_metrics_rs_m1_rate = metrics.Metric( - title = Title("Incoming bytes/sec (1 min)"), - name = "rs_m1_rate", +metric_graylog_input_metrics_rs_small_rate = metrics.Metric( + title = Title("Incoming bytes/sec (small window)"), + name = "rs_small_rate", unit = UNIT_MSGS, color = metrics.Color.LIGHT_BLUE, ) -metric_graylog_input_metrics_rs_m5_rate = metrics.Metric( - title = Title("Incoming bytes/sec (5 min)"), - name = "rs_m5_rate", +metric_graylog_input_metrics_rs_medium_rate = metrics.Metric( + title = Title("Incoming bytes/sec (medium window)"), + name = "rs_medium_rate", unit = UNIT_MSGS, color = metrics.Color.BLUE, ) -metric_graylog_input_metrics_rs_m15_rate = metrics.Metric( - title = Title("Incoming bytes/sec (15 min)"), - name = "rs_m15_rate", +metric_graylog_input_metrics_rs_large_rate = metrics.Metric( + title = Title("Incoming bytes/sec (large window)"), + name = "rs_large_rate", unit = UNIT_MSGS, color = metrics.Color.DARK_BLUE, ) diff --git a/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/rulesets/graylog_input_metrics_checks.py b/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/rulesets/graylog_input_metrics_checks.py index 3241407..3d3cb80 100644 --- a/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/rulesets/graylog_input_metrics_checks.py +++ b/graylog-metrics/2.4/local/lib/python3/cmk_addons/plugins/graylog_input_metrics/rulesets/graylog_input_metrics_checks.py @@ -1,9 +1,11 @@ # Copyright (C) 2026 Spearhead Systems SRL +from cmk.rulesets.v1.form_specs.validators import NumberInRange from cmk.rulesets.v1.form_specs import ( Dictionary, DictElement, Float, + Integer, DefaultValue, LevelDirection, SimpleLevels, @@ -29,7 +31,7 @@ titles = { } -def _rate(metric, minutes, level): +def _rate(metric, window, level): unit_name = titles[metric] def_value = default_levels[level] @@ -38,7 +40,7 @@ def _rate(metric, minutes, level): title = Title(f"{level.value.capitalize()} level"), level_direction = level, form_spec_template = Float( - title = Title(f"{unit_name}/{minutes}min"), + title = Title(f"{unit_name} {window} time window"), ), prefill_fixed_levels = DefaultValue(value=(def_value, def_value)) ), @@ -49,13 +51,25 @@ def _parameter_valuespec_graylog_input_metrics(): elements = {} for metric in ["im", "rs"]: - for minutes in [1, 5, 15]: - elements[f"{metric}_m{minutes}_rate"] = DictElement( + for window, default in [("small", 1), ("medium", 5), ("large", 15)]: + elements[f"{metric}_{window}_rate"] = DictElement( parameter_form = Dictionary( - title = Title(f"Incoming {titles[metric]} for past {minutes} minute(s)"), + title = Title(f"Incoming {titles[metric]} over {window} time window"), elements = { - "upper": _rate(metric, minutes, LevelDirection.UPPER), - "lower": _rate(metric, minutes, LevelDirection.LOWER) + "window": DictElement( + required = True, + parameter_form = Integer( + title = Title("Minutes"), + help_text = Help( + "Set how many minutes this Range Weighted Moving Average " + "window should use." + ), + prefill = DefaultValue(default), + custom_validate = (NumberInRange(min_value=1),), + ), + ), + "upper": _rate(metric, window, LevelDirection.UPPER), + "lower": _rate(metric, window, LevelDirection.LOWER), } ) )