Merge remote-tracking branch 'origin/topic/awelzel/telemetry-script-land-v0'

* origin/topic/awelzel/telemetry-script-land-v0:
  Introduce telemetry framework
  Bump broker submodule to master.
This commit is contained in:
Robin Sommer 2022-08-05 16:50:18 +02:00
commit 686e740bbe
No known key found for this signature in database
GPG key ID: 6BEDA4DA6B8B23E3
36 changed files with 1972 additions and 38 deletions

12
CHANGES
View file

@ -1,3 +1,15 @@
5.1.0-dev.335 | 2022-08-05 16:50:18 +0200
* Introduce telemetry framework (Arne Welzel, Corelight)
Adds base/frameworks/telemetry with wrappers around telemetry.bif
and updates telemetry/Manager to support collecting metrics from
script land.
Adds policy/frameworks/telemetry/log for logging of metrics data
into a new telemetry.log and telemetry_histogram.log and add into
local.zeek by default.
5.1.0-dev.331 | 2022-08-03 10:27:28 -0700 5.1.0-dev.331 | 2022-08-03 10:27:28 -0700
* input/config: Use thread-safe Fmt() rather than util::fmt() for regex formatting (Arne Welzel, Corelight) * input/config: Use thread-safe Fmt() rather than util::fmt() for regex formatting (Arne Welzel, Corelight)

17
NEWS
View file

@ -28,6 +28,23 @@ New Functionality
patterns in Zeek scripts will cause the '.' character to also match newline patterns in Zeek scripts will cause the '.' character to also match newline
characters. characters.
- Added a new telemetry framework for providing high-level access to Zeek's
metric subsystem. This framework allows script writers to use different
metric types (counters, gauges and histograms) for tracking metrics without
using lower-level BiFs from ``telemetry.bif``. Additionally, metrics can
now be accessed from script land using ``Telemetry::collect_metrics() and
``Telemetry::collect_histogram_metrics()``.
The framework is located in ``base/frameworks/telemetry``.
In addition to the Prometheus endpoint for metrics export that has existed
since Zeek 4.1, two new log streams, ``telemetry.log`` and ``telemetry_histogram.log``,
can be enabled by loading ``policy/frameworks/telemetry/log``. This policy
script is included in ``local.zeek`` by default.
For further details on the framework and examples, please refer to the
Zeek documentation.
Changed Functionality Changed Functionality
--------------------- ---------------------

View file

@ -1 +1 @@
5.1.0-dev.331 5.1.0-dev.335

@ -1 +1 @@
Subproject commit dc7b266e8289472096828ad51547237f49a91841 Subproject commit 1084bd43ce92483450d3e47ee5dcf172252e814a

2
doc

@ -1 +1 @@
Subproject commit c2fd82d369d389898dd2f5eca1a4572f4d6d8148 Subproject commit 6478a68605e92ff8e99250d455511a118e8c30af

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,609 @@
##! Module for recording and querying metrics. This modules wraps
##! the lower-level telemetry.bif functions.
##!
##! Metrics will be exposed through a Prometheus HTTP endpoint when
##! enabled by setting :zeek:see:`Broker::metrics_port` or using the
##! `BROKER_METRICS_PORT` environment variable.
@load base/misc/version
module Telemetry;
export {
## Alias for a vector of label values.
type labels_vector: vector of string;
## Type that captures options used to create metrics.
type MetricOpts: record {
## The prefix (namespace) of the metric.
prefix: string;
## The human-readable name of the metric.
name: string;
## The unit of the metric. Use the pseudo-unit "1" if this is a unit-less metric.
unit: string;
## Documentation for this metric.
help_text: string;
## The label names (also called dimensions) of the metric. When
## instantiating or working with concrete metrics, corresponding
## label values have to be provided.
labels: vector of string &default=vector();
## Whether the metric represents something that is accumulating.
## Defaults to ``T`` for counters and ``F`` for gauges and
## histograms.
is_total: bool &optional;
## When creating a :zeek:see:`Telemetry::HistogramFamily`,
## describes the number and bounds of the individual buckets.
bounds: vector of double &optional;
## The same meaning as *bounds*, but as :zeek:type:`count`.
## Only set in the return value of
## :zeek:see:`Telemetry::collect_histogram_metrics`.
## for histograms when the underlying type is ``int64_t``,
## otherwise ignored.
count_bounds: vector of count &optional;
## Describes the underlying metric type.
## Only set in the return value of
## :zeek:see:`Telemetry::collect_metrics` or
## :zeek:see:`Telemetry::collect_histogram_metrics`,
## otherwise ignored.
metric_type: MetricType &optional;
};
## Type representing a family of counters with uninitialized label values.
##
## To create concrete :zeek:see:`Telemetry::Counter` instances, use
## :zeek:see:`Telemetry::counter_with`. To modify counters directly
## use :zeek:see:`Telemetry::counter_family_inc`.
type CounterFamily: record {
__family: opaque of dbl_counter_metric_family;
__labels: vector of string;
};
## Type representing a counter metric with initialized label values.
##
## Counter metrics only ever go up and reset when the process
## restarts. Use :zeek:see:`Telemetry::counter_inc` or
## :zeek:see:`Telemetry::counter_set` to modify counters.
## An example for a counter is the number of log writes
## per :zeek:see:`Log::Stream` or number connections broken down
## by protocol and service.
type Counter: record {
__metric: opaque of dbl_counter_metric;
};
## Register a counter family.
global register_counter_family: function(opts: MetricOpts): CounterFamily;
## Get a :zeek:see:`Telemetry::Counter` instance given family and label values.
global counter_with: function(cf: CounterFamily,
label_values: labels_vector &default=vector()): Counter;
## Increment a :zeek:see:`Telemetry::Counter` by `amount`.
## Using a negative `amount` is an error.
##
## c: The counter instance.
##
## amount: The amount by which to increment the counter.
##
## Returns: True if the counter was incremented successfully.
global counter_inc: function(c: Counter, amount: double &default=1.0): bool;
## Helper to set a :zeek:see:`Telemetry::Counter` to the given `value`.
## This can be useful for mirroring counter metrics in an
## :zeek:see:`Telemetry::sync` hook implementation.
## Setting a value that is less than the current value of the
## metric is an error and will be ignored.
##
## c: The counter instance.
##
## value: The value to set the counter to.
##
## Returns: True if the counter value was set successfully.
global counter_set: function(c: Counter, value: double): bool;
## Increment a :zeek:see:`Telemetry::Counter` through the :zeek:see:`Telemetry::CounterFamily`.
## This is a short-cut for :zeek:see:`Telemetry::counter_inc`.
## Using a negative amount is an error.
##
## cf: The counter family to use.
##
## label_values: The label values to use for the counter.
##
## amount: The amount by which to increment the counter.
##
## Returns: True if the counter was incremented successfully.
global counter_family_inc: function(cf: CounterFamily,
label_values: labels_vector &default=vector(),
amount: double &default=1.0): bool;
## Set a :zeek:see:`Telemetry::Counter` through the :zeek:see:`Telemetry::CounterFamily`.
## This is a short-cut for :zeek:see:`Telemetry::counter_set`.
## Setting a value that is less than the current value of the
## metric is an error and will be ignored.
##
## cf: The counter family to use.
##
## label_values: The label values to use for the counter.
##
## value: The value to set the counter to.
##
## Returns: True if the counter value was set successfully.
global counter_family_set: function(cf: CounterFamily,
label_values: labels_vector,
value: double): bool;
## Type representing a family of gauges with uninitialized label values.
##
## Create concrete :zeek:see:`Telemetry::Gauge` instances with
## :zeek:see:`Telemetry::gauge_with`, or use
## :zeek:see:`Telemetry::gauge_family_inc` or
## :zeek:see:`Telemetry::gauge_family_set` directly.
type GaugeFamily: record {
__family: opaque of dbl_gauge_metric_family;
__labels: vector of string;
};
## Type representing a gauge metric with initialized label values.
##
## Use :zeek:see:`Telemetry::gauge_inc`, :zeek:see:`Telemetry::gauge_dec`,
## or :zeek:see:`Telemetry::gauge_set` to modify the gauge.
## Example for gauges are process memory usage, table sizes
## or footprints of long-lived values as determined by
## :zeek:see:`val_footprint`.
type Gauge: record {
__metric: opaque of dbl_gauge_metric;
};
## Register a gauge family.
global register_gauge_family: function(opts: MetricOpts): GaugeFamily;
## Get a :zeek:see:`Telemetry::Gauge` instance given family and label values.
global gauge_with: function(gf: GaugeFamily,
label_values: labels_vector &default=vector()): Gauge;
## Increment a :zeek:see:`Telemetry::Gauge` by `amount`.
##
## g: The gauge instance.
##
## amount: The amount by which to increment the gauge.
##
## Returns: True if the gauge was incremented successfully.
global gauge_inc: function(g: Gauge, amount: double &default=1.0): bool;
## Decrement a :zeek:see:`Telemetry::Gauge` by `amount`.
##
## g: The gauge instance.
##
## amount: The amount by which to decrement the gauge.
##
## Returns: True if the gauge was incremented successfully.
global gauge_dec: function(g: Gauge, amount: double &default=1.0): bool;
## Helper to set a :zeek:see:`Telemetry::Gauge` to the given `value`.
##
## g: The gauge instance.
##
## value: The value to set the gauge to.
##
## Returns: True if the gauge value was set successfully.
global gauge_set: function(g: Gauge, value: double): bool;
## Increment a :zeek:see:`Telemetry::Gauge` by the given `amount` through
## the :zeek:see:`Telemetry::GaugeFamily`.
## This is a short-cut for :zeek:see:`Telemetry::gauge_inc`.
## Using a negative amount is an error.
##
## gf: The gauge family to use.
##
## label_values: The label values to use for the gauge.
##
## amount: The amount by which to increment the gauge.
##
## Returns: True if the gauge was incremented successfully.
global gauge_family_inc: function(gf: GaugeFamily,
label_values: labels_vector &default=vector(),
amount: double &default=1.0): bool;
## Decrement a :zeek:see:`Telemetry::Gauge` by the given `amount` through
## the :zeek:see:`Telemetry::GaugeFamily`.
## This is a short-cut for :zeek:see:`Telemetry::gauge_dec`.
##
## gf: The gauge family to use.
##
## label_values: The label values to use for the gauge.
##
## amount: The amount by which to increment the gauge.
##
## Returns: True if the gauge was incremented successfully.
global gauge_family_dec: function(gf: GaugeFamily,
label_values: labels_vector &default=vector(),
amount: double &default=1.0): bool;
## Set a :zeek:see:`Telemetry::Gauge` to the given `value` through
## the :zeek:see:`Telemetry::GaugeFamily`.
## This is a short-cut for :zeek:see:`Telemetry::gauge_set`.
##
## gf: The gauge family to use.
##
## label_values: The label values to use for the gauge.
##
## value: The value to set the gauge to.
##
## Returns: True if the gauge value was set successfully.
global gauge_family_set: function(g: GaugeFamily,
label_values: labels_vector,
value: double): bool;
## Type representing a family of histograms with uninitialized label values.
## Create concrete :zeek:see:`Telemetry::Histogram` instances with
## :zeek:see:`Telemetry::histogram_with` or use
## :zeek:see:`Telemetry::histogram_family_observe` directly.
type HistogramFamily: record {
__family: opaque of dbl_histogram_metric_family;
__labels: vector of string;
};
## Type representing a histogram metric with initialized label values.
## Use :zeek:see:`Telemetry::histogram_observe` to make observations.
type Histogram: record {
__metric: opaque of dbl_histogram_metric;
};
## Register a histogram family.
global register_histogram_family: function(opts: MetricOpts): HistogramFamily;
## Get a :zeek:see:`Telemetry::Histogram` instance given family and label values.
global histogram_with: function(hf: HistogramFamily,
label_values: labels_vector &default=vector()): Histogram;
## Observe a measurement for a :zeek:see:`Telemetry::Histogram`.
##
## h: The histogram instance.
##
## measurement: The value for this observations.
##
## Returns: True if measurement was observed successfully.
global histogram_observe: function(h: Histogram, measurement: double): bool;
## Observe a measurement for a :zeek:see:`Telemetry::Histogram` through
## the :zeek:see:`Telemetry::HistogramFamily`.
## This is a short-cut for :zeek:see:`Telemetry::histogram_observe`.
##
## hf: The histogram family to use.
##
## label_values: The label values to use for the histogram.
##
## measurement: The value for this observations.
##
## Returns: True if measurement was observed successfully.
global histogram_family_observe: function(hf: HistogramFamily,
label_values: labels_vector,
measurement: double): bool;
## Telemetry sync hook.
##
## This hook is invoked every :zeek:see:`Telemetry::sync_interval`
## for script writers to synchronize or mirror metrics with the
## telemetry subsystem. For example, when tracking table or value
## footprints with gauges, the value in question can be set on an actual
## :zeek:see:`Telemetry::Gauge` instance during execution of this hook.
##
## Implementations should be lightweight, this hook may be called
## multiple times per minute. The interval can increased by changing
## :zeek:see:`Telemetry::sync_interval` at the cost of delaying
## metric updates and thereby reducing granularity.
global sync: hook();
## Interval at which the :zeek:see:`Telemetry::sync` hook is invoked.
option sync_interval = 10sec;
## Type of elements returned by the :zeek:see:`Telemetry::collect_metrics` function.
type Metric: record {
## A :zeek:see:`Telemetry::MetricOpts` record describing this metric.
opts: MetricOpts;
## The label values associated with this metric, if any.
labels: vector of string;
## The value of gauge or counter cast to a double
## independent of the underlying data type.
## This value is set for all counter and gauge metrics,
## it is unset for histograms.
value: double &optional;
## The value of the underlying gauge or counter as a double
## if the underlying metric type uses ``int64_t``.
## Only counters and gauges created with the C++ API may
## have this value set.
count_value: count &optional;
};
## Type of elements returned by the :zeek:see:`Telemetry::collect_histogram_metrics` function.
type HistogramMetric: record {
## A :zeek:see:`Telemetry::MetricOpts` record describing this histogram.
opts: MetricOpts;
## The label values associated with this histogram, if any.
labels: vector of string;
## Individual counters for each of the buckets as
## described by the *bounds* field in *opts*;
values: vector of double;
## If the underlying data type of the histogram is ``int64_t``,
## this vector will hold the values as counts, otherwise it
## is unset. Only histograms created with the C++ API have
## may have this value set.
count_values: vector of count &optional;
## The number of observations made for this histogram.
observations: double;
## The sum of all observations for this histogram.
sum: double;
## If the underlying data type of the histogram is ``int64_t``,
## the number of observations as :zeek:type:`count`, otherwise
## unset.
count_observations: count &optional;
## If the underlying data type of the histogram is ``int64_t``,
## the sum of all observations as :zeek:type:`count`, otherwise
## unset.
count_sum: count &optional;
};
## Collect all counter and gauge metrics matching the given *name* and *prefix*.
##
## For histogram metrics, use the :zeek:see:`Telemetry::collect_histogram_metrics`.
##
## The *prefix* and *name* parameters support globbing. By default,
## all counters and gauges are returned.
global collect_metrics: function(prefix: string &default="*",
name: string &default="*"): vector of Metric;
## Collect all histograms and their observations matching the given
## *prefix* and *name*.
##
## The *prefix* and *name* parameters support globbing. By default,
## all histogram metrics are returned.
global collect_histogram_metrics: function(prefix: string &default="*",
name: string &default="*"): vector of HistogramMetric;
}
## Internal helper to create the labels table.
function make_labels(keys: vector of string, values: labels_vector): table[string] of string
{
local labels: table[string] of string;
for ( i in keys )
labels[keys[i]] = values[i];
return labels;
}
function register_counter_family(opts: MetricOpts): CounterFamily
{
local f = Telemetry::__dbl_counter_family(
opts$prefix,
opts$name,
opts$labels,
opts$help_text,
opts$unit,
opts?$is_total ? opts$is_total : T
);
return CounterFamily($__family=f, $__labels=opts$labels);
}
# Fallback Counter returned when there are issues with the labels.
global error_counter_cf = register_counter_family([
$prefix="zeek",
$name="telemetry_counter_usage_error",
$unit="1",
$help_text="This counter is returned when label usage for counters is wrong. Check reporter.log if non-zero."
]);
function counter_with(cf: CounterFamily, label_values: labels_vector): Counter
{
if ( |cf$__labels| != |label_values| )
{
Reporter::error(fmt("Invalid label values expected %s, have %s", |cf$__labels|, |label_values|));
return counter_with(error_counter_cf);
}
local labels = make_labels(cf$__labels, label_values);
local m = Telemetry::__dbl_counter_metric_get_or_add(cf$__family, labels);
return Counter($__metric=m);
}
function counter_inc(c: Counter, amount: double): bool
{
return Telemetry::__dbl_counter_inc(c$__metric, amount);
}
function counter_set(c: Counter, value: double): bool
{
local cur_value: double = Telemetry::__dbl_counter_value(c$__metric);
if (value < cur_value)
{
Reporter::error(fmt("Attempted to set lower counter value=%s cur_value=%s", value, cur_value));
return F;
}
return Telemetry::__dbl_counter_inc(c$__metric, value - cur_value);
}
function counter_family_inc(cf: CounterFamily, label_values: labels_vector, amount: double): bool
{
return counter_inc(counter_with(cf, label_values), amount);
}
function counter_family_set(cf: CounterFamily, label_values: labels_vector, value: double): bool
{
return counter_set(counter_with(cf, label_values), value);
}
function register_gauge_family(opts: MetricOpts): GaugeFamily
{
local f = Telemetry::__dbl_gauge_family(
opts$prefix,
opts$name,
opts$labels,
opts$help_text,
opts$unit,
opts?$is_total ? opts$is_total : F
);
return GaugeFamily($__family=f, $__labels=opts$labels);
}
# Fallback Gauge returned when there are issues with the label usage.
global error_gauge_cf = register_gauge_family([
$prefix="zeek",
$name="telemetry_gauge_usage_error",
$unit="1",
$help_text="This gauge is returned when label usage for gauges is wrong. Check reporter.log if non-zero."
]);
function gauge_with(gf: GaugeFamily, label_values: labels_vector): Gauge
{
if ( |gf$__labels| != |label_values| )
{
Reporter::error(fmt("Invalid label values expected %s, have %s", |gf$__labels|, |label_values|));
return gauge_with(error_gauge_cf);
}
local labels = make_labels(gf$__labels, label_values);
local m = Telemetry::__dbl_gauge_metric_get_or_add(gf$__family, labels);
return Gauge($__metric=m);
}
function gauge_inc(g: Gauge, amount: double &default=1.0): bool
{
return Telemetry::__dbl_gauge_inc(g$__metric, amount);
}
function gauge_dec(g: Gauge, amount: double &default=1.0): bool
{
return Telemetry::__dbl_gauge_dec(g$__metric, amount);
}
function gauge_set(g: Gauge, value: double): bool
{
# Telemetry currently does not implement __dbl_gauge_set(), do
# it by hand here.
local cur_value: double = Telemetry::__dbl_gauge_value(g$__metric);
if (value > cur_value)
return Telemetry::__dbl_gauge_inc(g$__metric, value - cur_value);
return Telemetry::__dbl_gauge_dec(g$__metric, cur_value - value);
}
function gauge_family_inc(gf: GaugeFamily, label_values: labels_vector, value: double): bool
{
return gauge_inc(gauge_with(gf, label_values), value);
}
function gauge_family_dec(gf: GaugeFamily, label_values: labels_vector, value: double): bool
{
return gauge_dec(gauge_with(gf, label_values), value);
}
function gauge_family_set(gf: GaugeFamily, label_values: labels_vector, value: double): bool
{
return gauge_set(gauge_with(gf, label_values), value);
}
function register_histogram_family(opts: MetricOpts): HistogramFamily
{
local f = Telemetry::__dbl_histogram_family(
opts$prefix,
opts$name,
opts$labels,
opts$bounds,
opts$help_text,
opts$unit,
opts?$is_total ? opts$is_total : F
);
return HistogramFamily($__family=f, $__labels=opts$labels);
}
# Fallback Histogram when there are issues with the labels.
global error_histogram_hf = register_histogram_family([
$prefix="zeek",
$name="telemetry_histogram_usage_error",
$unit="1",
$help_text="This histogram is returned when label usage for histograms is wrong. Check reporter.log if non-zero.",
$bounds=vector(1.0)
]);
function histogram_with(hf: HistogramFamily, label_values: labels_vector): Histogram
{
if ( |hf$__labels| != |label_values| )
{
Reporter::error(fmt("Invalid label values expected %s, have %s", |hf$__labels|, |label_values|));
return histogram_with(error_histogram_hf);
}
local labels = make_labels(hf$__labels, label_values);
local m = Telemetry::__dbl_histogram_metric_get_or_add(hf$__family, labels);
return Histogram($__metric=m);
}
function histogram_observe(h: Histogram, measurement: double): bool
{
return Telemetry::__dbl_histogram_observe(h$__metric, measurement);
}
function histogram_family_observe(hf: HistogramFamily, label_values: labels_vector, measurement: double): bool
{
return histogram_observe(histogram_with(hf, label_values), measurement);
}
function collect_metrics(prefix: string, name: string): vector of Metric
{
return Telemetry::__collect_metrics(prefix, name);
}
function collect_histogram_metrics(prefix: string, name: string): vector of HistogramMetric
{
return Telemetry::__collect_histogram_metrics(prefix, name);
}
event run_sync_hook()
{
hook Telemetry::sync();
schedule sync_interval { run_sync_hook() };
}
event zeek_init()
{
schedule sync_interval { run_sync_hook() };
}
# Expose the Zeek version as Prometheus style info metric
global version_gauge_family = Telemetry::register_gauge_family([
$prefix="zeek",
$name="version_info",
$unit="1",
$help_text="The Zeek version",
$labels=vector("version_number", "major", "minor", "patch", "commit",
"beta", "debug","version_string")
]);
event zeek_init()
{
local v = Version::info;
local labels = vector(cat(v$version_number),
cat(v$major), cat(v$minor), cat (v$patch),
cat(v$commit),
v$beta ? "true" : "false",
v$debug ? "true" : "false",
v$version_string);
Telemetry::gauge_family_set(version_gauge_family, labels, 1.0);
}

View file

@ -43,6 +43,7 @@
@load base/frameworks/tunnels @load base/frameworks/tunnels
@load base/frameworks/openflow @load base/frameworks/openflow
@load base/frameworks/netcontrol @load base/frameworks/netcontrol
@load base/frameworks/telemetry
@load base/protocols/conn @load base/protocols/conn
@load base/protocols/dce-rpc @load base/protocols/dce-rpc

View file

@ -0,0 +1,199 @@
##! Implementation of a telemetry.log and telemetry_histogram.log file
##! using metrics accessible via the Telemetry module.
@load base/frameworks/telemetry
module Telemetry;
export {
redef enum Log::ID += { LOG, LOG_HISTOGRAM };
## How often metrics are reported.
option log_interval = 60sec;
## Only metrics with prefixes in this set will be included in the
## `telemetry.log` and `telemetry_histogram.log` files by default.
## Setting this option to an empty set includes all prefixes.
##
## For more fine-grained customization, setting this option to an
## empty set and implementing the :zeek:see:`Telemetry::log_policy`
## and :zeek:see:`Telemetry::log_policy_histogram` hooks to filter
## individual records is recommended.
option log_prefixes: set[string] = {"process", "zeek"};
## Record type used for logging counter and gauge metrics.
type Info: record {
## Timestamp of reporting.
ts: time &log;
## Peer that generated this log.
peer: string &log;
## Contains the value "counter" or "gauge" depending on
## the underlying metric type.
metric_type: string &log;
## The prefix (namespace) of the metric.
prefix: string &log;
## The name of the metric.
name: string &log;
## The unit of this metric, or unset if unit-less.
unit: string &log &optional;
## The names of the individual labels.
labels: vector of string &log;
## The values of the labels as listed in ``labels``.
label_values: vector of string &log;
## The value of this metric.
value: double &log;
};
## Record type used for logging histogram metrics.
type HistogramInfo: record {
## Timestamp of reporting.
ts: time &log;
## Peer that generated this log.
peer: string &log;
## The prefix (namespace) of the metric.
prefix: string &log;
## The name of the metric.
name: string &log;
## The unit of this metric, or unset if unit-less.
unit: string &log &optional;
## The names of the individual labels.
labels: vector of string &log;
## The values of the labels as listed in ``labels``.
label_values: vector of string &log;
## The bounds of the individual buckets
bounds: vector of double &log;
## The number of observations within each individual bucket.
values: vector of double &log;
## The sum over all observations
sum: double &log;
## The total number of observations.
observations: double &log;
};
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
## A default logging policy hook for the histogram stream.
global log_policy_histogram: Log::PolicyHook;
## Event triggered for every record in the stream.
global log_telemetry: event(rec: Info);
## Event triggered for every record in the histogram stream.
global log_telemetry_histogram: event(rec: HistogramInfo);
}
function do_log()
{
local ts = network_time();
local metrics = Telemetry::collect_metrics();
for ( i in metrics )
{
local m = metrics[i];
# Histograms don't have single values, skip over them.
if ( m$opts$metric_type == DOUBLE_HISTOGRAM || m$opts$metric_type == INT_HISTOGRAM )
next;
if ( |log_prefixes| > 0 && m$opts$prefix !in log_prefixes )
next;
# Render the metric_type as a short string. Unknown
# shouldn't really happen, but lets have a fallback.
local metric_type = "unknown";
switch ( m$opts$metric_type ) {
case DOUBLE_COUNTER, INT_COUNTER:
metric_type = "counter";
break;
case DOUBLE_GAUGE, INT_GAUGE:
metric_type = "gauge";
break;
}
local rec = Info($ts=ts,
$peer=peer_description,
$metric_type=metric_type,
$prefix=m$opts$prefix,
$name=m$opts$name,
$labels=m$opts$labels,
$label_values=m$labels,
$value=m$value);
if ( m$opts$unit != "1" )
rec$unit = m$opts$unit;
Log::write(LOG, rec);
}
# Logging of histograms.
ts = network_time();
local histogram_metrics = Telemetry::collect_histogram_metrics();
for ( i in histogram_metrics )
{
local hm = histogram_metrics[i];
if ( |log_prefixes| > 0 && hm$opts$prefix !in log_prefixes )
next;
local hrec = HistogramInfo($ts=ts,
$peer=peer_description,
$prefix=hm$opts$prefix,
$name=hm$opts$name,
$labels=hm$opts$labels,
$label_values=hm$labels,
$bounds=hm$opts$bounds,
$values=hm$values,
$sum=hm$sum,
$observations=hm$observations);
if ( hm$opts$unit != "1" )
hrec$unit = hm$opts$unit;
Log::write(LOG_HISTOGRAM, hrec);
}
}
event Telemetry::log()
{
# We explicitly log once during zeek_done(), so short-circuit
# here when we're already in the process of shutting down.
if ( zeek_is_terminating() )
return;
do_log();
schedule log_interval { Telemetry::log() };
}
event zeek_init() &priority=5
{
Log::create_stream(LOG, [$columns=Info, $ev=log_telemetry, $path="telemetry", $policy=log_policy]);
Log::create_stream(LOG_HISTOGRAM, [$columns=HistogramInfo, $ev=log_telemetry_histogram, $path="telemetry_histogram", $policy=log_policy_histogram]);
schedule log_interval { Telemetry::log() };
}
# Log late during zeek_done() once more. Any metric updates
# afterwards won't be visible in the log.
event zeek_done() &priority=-1000
{
do_log();
}

View file

@ -89,6 +89,10 @@ redef digest_salt = "Please change this value.";
# Extend email alerting to include hostnames # Extend email alerting to include hostnames
@load policy/frameworks/notice/extend-email/hostnames @load policy/frameworks/notice/extend-email/hostnames
# Enable logging of telemetry data into telemetry.log and
# telemetry_histogram.log.
@load frameworks/telemetry/log
# Uncomment the following line to enable detection of the heartbleed attack. Enabling # Uncomment the following line to enable detection of the heartbleed attack. Enabling
# this might impact performance a bit. # this might impact performance a bit.
# @load policy/protocols/ssl/heartbleed # @load policy/protocols/ssl/heartbleed

View file

@ -71,6 +71,7 @@
@load frameworks/software/version-changes.zeek @load frameworks/software/version-changes.zeek
@load frameworks/software/vulnerable.zeek @load frameworks/software/vulnerable.zeek
@load frameworks/software/windows-version-detection.zeek @load frameworks/software/windows-version-detection.zeek
@load frameworks/telemetry/log.zeek
@load integration/barnyard2/__load__.zeek @load integration/barnyard2/__load__.zeek
@load integration/barnyard2/main.zeek @load integration/barnyard2/main.zeek
@load integration/barnyard2/types.zeek @load integration/barnyard2/types.zeek

View file

@ -12,4 +12,4 @@ set(telemetry_SRCS
bif_target(telemetry.bif) bif_target(telemetry.bif)
bro_add_subdir_library(telemetry ${telemetry_SRCS}) bro_add_subdir_library(telemetry ${telemetry_SRCS})
add_dependencies(bro_telemetry generate_outputs)

View file

@ -2,11 +2,15 @@
#include "zeek/telemetry/Manager.h" #include "zeek/telemetry/Manager.h"
#include <fnmatch.h>
#include <thread> #include <thread>
#include <variant>
#include "zeek/3rdparty/doctest.h" #include "zeek/3rdparty/doctest.h"
#include "zeek/ID.h"
#include "zeek/broker/Manager.h" #include "zeek/broker/Manager.h"
#include "zeek/telemetry/Timer.h" #include "zeek/telemetry/Timer.h"
#include "zeek/telemetry/telemetry.bif.h"
#include "broker/telemetry/metric_registry.hh" #include "broker/telemetry/metric_registry.hh"
@ -15,6 +19,33 @@ namespace
using NativeManager = broker::telemetry::metric_registry; using NativeManager = broker::telemetry::metric_registry;
using NativeManagerImpl = broker::telemetry::metric_registry_impl; using NativeManagerImpl = broker::telemetry::metric_registry_impl;
using NativeManagerImplPtr = zeek::IntrusivePtr<NativeManagerImpl>; using NativeManagerImplPtr = zeek::IntrusivePtr<NativeManagerImpl>;
using DoubleValPtr = zeek::IntrusivePtr<zeek::DoubleVal>;
std::vector<std::string_view> extract_label_values(broker::telemetry::const_label_list labels)
{
auto get_value = [](const auto& label)
{
return label.second;
};
std::vector<std::string_view> v;
std::transform(labels.begin(), labels.end(), std::back_inserter(v), get_value);
return v;
}
// Convert an int64_t or double to a DoubleValPtr. int64_t is casted.
template <typename T> DoubleValPtr as_double_val(T val)
{
if constexpr ( std::is_same_v<T, int64_t> )
{
return zeek::make_intrusive<zeek::DoubleVal>(static_cast<double>(val));
}
else
{
static_assert(std::is_same_v<T, double>);
return zeek::make_intrusive<zeek::DoubleVal>(val);
}
};
} }
namespace zeek::telemetry namespace zeek::telemetry
@ -38,6 +69,432 @@ void Manager::InitPostBrokerSetup(broker::endpoint& ep)
pimpl.swap(ptr); pimpl.swap(ptr);
} }
// -- collect metric stuff -----------------------------------------------------
template <typename T>
zeek::RecordValPtr Manager::GetMetricOptsRecord(Manager::MetricType metric_type,
const broker::telemetry::metric_family_hdl* family)
{
static auto string_vec_type = zeek::id::find_type<zeek::VectorType>("string_vec");
static auto metric_opts_type = zeek::id::find_type<zeek::RecordType>("Telemetry::MetricOpts");
static auto prefix_idx = metric_opts_type->FieldOffset("prefix");
static auto name_idx = metric_opts_type->FieldOffset("name");
static auto help_text_idx = metric_opts_type->FieldOffset("help_text");
static auto unit_idx = metric_opts_type->FieldOffset("unit");
static auto is_total_idx = metric_opts_type->FieldOffset("is_total");
static auto labels_idx = metric_opts_type->FieldOffset("labels");
static auto bounds_idx = metric_opts_type->FieldOffset("bounds");
static auto metric_type_idx = metric_opts_type->FieldOffset("metric_type");
if ( const auto& it = metric_opts_cache.find(family); it != metric_opts_cache.end() )
return it->second;
auto r = make_intrusive<zeek::RecordVal>(metric_opts_type);
r->Assign(prefix_idx, make_intrusive<zeek::StringVal>(broker::telemetry::prefix(family)));
r->Assign(name_idx, make_intrusive<zeek::StringVal>(broker::telemetry::name(family)));
r->Assign(help_text_idx, make_intrusive<zeek::StringVal>(broker::telemetry::helptext(family)));
r->Assign(unit_idx, make_intrusive<zeek::StringVal>(broker::telemetry::unit(family)));
r->Assign(is_total_idx, val_mgr->Bool(broker::telemetry::is_sum(family)));
auto label_names_vec = make_intrusive<zeek::VectorVal>(string_vec_type);
for ( const auto& l : broker::telemetry::label_names(family) )
label_names_vec->Append(make_intrusive<StringVal>(l));
r->Assign(labels_idx, label_names_vec);
// This is mapping Manager.h enums to bif values depending on
// the template type and whether this is a counter, gauge or
// histogram.
zeek_int_t metric_type_int = -1;
if constexpr ( std::is_same<T, double>::value )
{
switch ( metric_type )
{
case MetricType::Counter:
metric_type_int = BifEnum::Telemetry::MetricType::DOUBLE_COUNTER;
break;
case MetricType::Gauge:
metric_type_int = BifEnum::Telemetry::MetricType::DOUBLE_GAUGE;
break;
case MetricType::Histogram:
metric_type_int = BifEnum::Telemetry::MetricType::DOUBLE_HISTOGRAM;
break;
}
}
else
{
switch ( metric_type )
{
case MetricType::Counter:
metric_type_int = BifEnum::Telemetry::MetricType::INT_COUNTER;
break;
case MetricType::Gauge:
metric_type_int = BifEnum::Telemetry::MetricType::INT_GAUGE;
break;
case MetricType::Histogram:
metric_type_int = BifEnum::Telemetry::MetricType::INT_HISTOGRAM;
break;
}
}
if ( metric_type_int < 0 )
reporter->FatalError("Unable to lookup metric type %d", int(metric_type));
r->Assign(metric_type_idx,
zeek::BifType::Enum::Telemetry::MetricType->GetEnumVal(metric_type_int));
metric_opts_cache.insert({family, r});
return r;
}
zeek::RecordValPtr Manager::CollectedValueMetric::AsMetricRecord() const
{
static auto string_vec_type = zeek::id::find_type<zeek::VectorType>("string_vec");
static auto metric_record_type = zeek::id::find_type<zeek::RecordType>("Telemetry::Metric");
static auto opts_idx = metric_record_type->FieldOffset("opts");
static auto labels_idx = metric_record_type->FieldOffset("labels");
static auto value_idx = metric_record_type->FieldOffset("value");
static auto count_value_idx = metric_record_type->FieldOffset("count_value");
auto r = make_intrusive<zeek::RecordVal>(metric_record_type);
auto label_values_vec = make_intrusive<zeek::VectorVal>(string_vec_type);
for ( const auto& l : label_values )
label_values_vec->Append(make_intrusive<StringVal>(l));
r->Assign(labels_idx, label_values_vec);
auto fn = [&](auto val)
{
using val_t = decltype(val);
auto opts_record = telemetry_mgr->GetMetricOptsRecord<val_t>(metric_type, family);
r->Assign(opts_idx, opts_record);
r->Assign(value_idx, as_double_val(val));
if constexpr ( std::is_same_v<val_t, int64_t> )
r->Assign(count_value_idx, val_mgr->Count(val));
};
std::visit(fn, value);
return r;
}
zeek::RecordValPtr Manager::CollectedHistogramMetric::AsHistogramMetricRecord() const
{
static auto string_vec_type = zeek::id::find_type<zeek::VectorType>("string_vec");
static auto double_vec_type = zeek::id::find_type<zeek::VectorType>("double_vec");
static auto count_vec_type = zeek::id::find_type<zeek::VectorType>("index_vec");
static auto histogram_metric_type = zeek::id::find_type<zeek::RecordType>(
"Telemetry::HistogramMetric");
static auto opts_idx = histogram_metric_type->FieldOffset("opts");
static auto labels_idx = histogram_metric_type->FieldOffset("labels");
static auto values_idx = histogram_metric_type->FieldOffset("values");
static auto count_values_idx = histogram_metric_type->FieldOffset("count_values");
static auto observations_idx = histogram_metric_type->FieldOffset("observations");
static auto sum_idx = histogram_metric_type->FieldOffset("sum");
static auto count_observations_idx = histogram_metric_type->FieldOffset("count_observations");
static auto count_sum_idx = histogram_metric_type->FieldOffset("count_sum");
zeek::RecordValPtr opts_record;
auto r = make_intrusive<zeek::RecordVal>(histogram_metric_type);
auto label_values_vec = make_intrusive<zeek::VectorVal>(string_vec_type);
for ( const auto& l : label_values )
label_values_vec->Append(make_intrusive<StringVal>(l));
r->Assign(labels_idx, label_values_vec);
auto fn = [&](const auto& histogram_data)
{
using val_t = std::decay_t<decltype(histogram_data.sum)>;
opts_record = telemetry_mgr->GetMetricOptsRecord<val_t>(MetricType::Histogram, family);
r->Assign(opts_idx, opts_record);
val_t observations = 0;
auto values_vec = make_intrusive<zeek::VectorVal>(double_vec_type);
auto count_values_vec = make_intrusive<zeek::VectorVal>(count_vec_type);
for ( const auto& b : histogram_data.buckets )
{
observations += b.count;
values_vec->Append(as_double_val(b.count));
if constexpr ( std::is_same_v<val_t, int64_t> )
count_values_vec->Append(val_mgr->Count(b.count));
}
r->Assign(values_idx, values_vec);
r->Assign(sum_idx, as_double_val(histogram_data.sum));
r->Assign(observations_idx, as_double_val(observations));
// Add extra fields just for int64_t based histograms with type count
if constexpr ( std::is_same_v<val_t, int64_t> )
{
r->Assign(count_values_idx, count_values_vec);
r->Assign(count_sum_idx, val_mgr->Count(histogram_data.sum));
r->Assign(count_observations_idx, val_mgr->Count(observations));
}
};
std::visit(fn, histogram);
// This is a bit annoying. The bounds / upper bounds of the individual
// buckets are not accessible through the family hdl. We set the
// opts$bounds field based on bounds collected from an individual
// metric instance.
//
// As the opts_record is cached by the manager this should only happen
// once and not over and over again.
//
// This is probably working around something that should be fixed
// on the broker::telemetry level.
static auto opts_rt = zeek::id::find_type<zeek::RecordType>("Telemetry::MetricOpts");
static auto opts_rt_off_bounds = opts_rt->FieldOffset("bounds");
static auto opts_rt_off_count_bounds = opts_rt->FieldOffset("count_bounds");
static auto bounds_default_val = opts_rt->FieldDefault(opts_rt_off_bounds);
auto bounds_field_val = opts_record->GetField(opts_rt_off_bounds);
if ( ! bounds_field_val || bounds_field_val == bounds_default_val )
{
auto fn = [&opts_record](const auto& histogram_data)
{
auto bounds_vec = make_intrusive<zeek::VectorVal>(double_vec_type);
for ( const auto& b : histogram_data.buckets )
bounds_vec->Append(as_double_val(b.upper_bound));
opts_record->Assign(opts_rt_off_bounds, bounds_vec);
// If this is an int64_t histogram, also fill count_bounds
using HistogramDataType = std::decay_t<decltype(histogram_data)>;
if constexpr ( std::is_same_v<HistogramDataType, IntHistogramData> )
{
auto count_bounds_vec = make_intrusive<zeek::VectorVal>(count_vec_type);
for ( const auto& b : histogram_data.buckets )
count_bounds_vec->Append(val_mgr->Count(b.upper_bound));
opts_record->Assign(opts_rt_off_count_bounds, count_bounds_vec);
}
};
std::visit(fn, histogram);
}
return r;
}
/**
* Encapsulate matching of prefix and name against a broker::telemetry::metric_family_hdl
*/
class MetricFamilyMatcher
{
public:
MetricFamilyMatcher(std::string_view prefix, std::string_view name)
: prefix_pattern(prefix), name_pattern(name)
{
}
/**
* @return true if the given family's prefix and name match, else false;
*/
bool operator()(const broker::telemetry::metric_family_hdl* family)
{
auto prefix = std::string{broker::telemetry::prefix(family)};
auto name = std::string{broker::telemetry::name(family)};
return fnmatch(prefix_pattern.c_str(), prefix.c_str(), 0) != FNM_NOMATCH &&
fnmatch(name_pattern.c_str(), name.c_str(), 0) != FNM_NOMATCH;
}
private:
std::string prefix_pattern;
std::string name_pattern;
};
/**
* A collector implementation for counters and gauges.
*/
class MetricsCollector : public broker::telemetry::metrics_collector
{
using MetricType = Manager::MetricType;
public:
MetricsCollector(std::string_view prefix, std::string_view name) : matches(prefix, name) { }
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::dbl_counter_hdl* counter,
broker::telemetry::const_label_list labels)
{
if ( matches(family) )
metrics.emplace_back(MetricType::Counter, family, extract_label_values(labels),
broker::telemetry::value(counter));
}
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::int_counter_hdl* counter,
broker::telemetry::const_label_list labels)
{
if ( matches(family) )
metrics.emplace_back(MetricType::Counter, family, extract_label_values(labels),
broker::telemetry::value(counter));
}
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::dbl_gauge_hdl* gauge,
broker::telemetry::const_label_list labels)
{
if ( matches(family) )
metrics.emplace_back(MetricType::Gauge, family, extract_label_values(labels),
broker::telemetry::value(gauge));
}
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::int_gauge_hdl* gauge,
broker::telemetry::const_label_list labels)
{
if ( matches(family) )
metrics.emplace_back(MetricType::Gauge, family, extract_label_values(labels),
broker::telemetry::value(gauge));
}
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::dbl_histogram_hdl* histogram,
broker::telemetry::const_label_list labels)
{
// Ignored
}
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::int_histogram_hdl* histogram,
broker::telemetry::const_label_list labels)
{
// Ignored
}
std::vector<Manager::CollectedValueMetric>& GetResult() { return metrics; }
private:
MetricFamilyMatcher matches;
std::vector<Manager::CollectedValueMetric> metrics;
};
std::vector<Manager::CollectedValueMetric> Manager::CollectMetrics(std::string_view prefix,
std::string_view name)
{
auto collector = MetricsCollector(prefix, name);
pimpl->collect(collector);
return std::move(collector.GetResult());
}
/**
* A collector implementation for histograms.
*/
class HistogramMetricsCollector : public broker::telemetry::metrics_collector
{
using MetricType = Manager::MetricType;
public:
HistogramMetricsCollector(std::string_view prefix, std::string_view name)
: matches(prefix, name)
{
}
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::dbl_counter_hdl* counter,
broker::telemetry::const_label_list labels)
{
// Ignored
}
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::int_counter_hdl* counter,
broker::telemetry::const_label_list labels)
{
// Ignored
}
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::dbl_gauge_hdl* gauge,
broker::telemetry::const_label_list labels)
{
// Ignored
}
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::int_gauge_hdl* gauge,
broker::telemetry::const_label_list labels)
{
// Ignored
}
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::dbl_histogram_hdl* histogram,
broker::telemetry::const_label_list labels)
{
if ( ! matches(family) )
return;
size_t num_buckets = broker::telemetry::num_buckets(histogram);
Manager::CollectedHistogramMetric::DblHistogramData histogram_data;
histogram_data.buckets.reserve(num_buckets);
for ( size_t i = 0; i < num_buckets; i++ )
{
double c = broker::telemetry::count_at(histogram, i);
double ub = broker::telemetry::upper_bound_at(histogram, i);
histogram_data.buckets.emplace_back(c, ub);
}
histogram_data.sum = broker::telemetry::sum(histogram);
metrics.emplace_back(family, extract_label_values(labels), std::move(histogram_data));
}
void operator()(const broker::telemetry::metric_family_hdl* family,
const broker::telemetry::int_histogram_hdl* histogram,
broker::telemetry::const_label_list labels)
{
if ( ! matches(family) )
return;
size_t num_buckets = broker::telemetry::num_buckets(histogram);
Manager::CollectedHistogramMetric::IntHistogramData histogram_data;
histogram_data.buckets.reserve(num_buckets);
for ( size_t i = 0; i < num_buckets; i++ )
{
int64_t c = broker::telemetry::count_at(histogram, i);
int64_t ub = broker::telemetry::upper_bound_at(histogram, i);
histogram_data.buckets.emplace_back(c, ub);
}
histogram_data.sum = broker::telemetry::sum(histogram);
metrics.emplace_back(family, extract_label_values(labels), std::move(histogram_data));
}
std::vector<Manager::CollectedHistogramMetric>& GetResult() { return metrics; }
private:
MetricFamilyMatcher matches;
std::vector<Manager::CollectedHistogramMetric> metrics;
};
std::vector<Manager::CollectedHistogramMetric>
Manager::CollectHistogramMetrics(std::string_view prefix, std::string_view name)
{
auto collector = HistogramMetricsCollector(prefix, name);
pimpl->collect(collector);
return std::move(collector.GetResult());
}
} // namespace zeek::telemetry } // namespace zeek::telemetry
// -- unit tests --------------------------------------------------------------- // -- unit tests ---------------------------------------------------------------

View file

@ -5,6 +5,8 @@
#include <cstdint> #include <cstdint>
#include <initializer_list> #include <initializer_list>
#include <string_view> #include <string_view>
#include <unordered_map>
#include <variant>
#include <vector> #include <vector>
#include "zeek/IntrusivePtr.h" #include "zeek/IntrusivePtr.h"
@ -20,6 +22,12 @@ namespace broker
class endpoint; class endpoint;
} }
namespace zeek
{
class RecordVal;
using RecordValPtr = IntrusivePtr<RecordVal>;
}
namespace zeek::Broker namespace zeek::Broker
{ {
class Manager; class Manager;
@ -50,6 +58,128 @@ public:
*/ */
virtual void InitPostScript(); virtual void InitPostScript();
/**
* Supported metric types.
*/
enum class MetricType
{
Counter,
Gauge,
Histogram
};
/**
* Captures information about counter and gauge metrics.
*/
struct CollectedValueMetric
{
/**
* Constructor.
* @param metric_type The type of this metric.
* @param family Broker layer family handle for this metric.
* @param label_values The string values for each of the metric's labels.
* @param value The metric's current value.
*/
CollectedValueMetric(MetricType metric_type,
const broker::telemetry::metric_family_hdl* family,
std::vector<std::string_view> label_values,
std::variant<double, int64_t> value)
: metric_type(metric_type), family(family), label_values(std::move(label_values)),
value(value)
{
}
/**
* @return A script layer Telemetry::Metric record for this metric.
*/
zeek::RecordValPtr AsMetricRecord() const;
enum MetricType metric_type;
const broker::telemetry::metric_family_hdl* family;
std::vector<std::string_view> label_values;
std::variant<double, int64_t> value;
};
/**
* Captures information about histogram metrics.
*/
struct CollectedHistogramMetric
{
/**
* Helper struct representing a single bucket of a histogram.
* @tparam T The data type used by the histogram (double or int64_t).
*/
template <class T> struct Bucket
{
Bucket(T count, T upper_bound) : count(count), upper_bound(upper_bound) { }
T count;
T upper_bound;
};
/**
* Helper struct representing a histogram as sum and buckets.
* @tparam T The data type used by the histogram (double or int64_t).
*/
template <class T> struct HistogramData
{
T sum;
std::vector<Bucket<T>> buckets;
};
using DblHistogramData = HistogramData<double>;
using IntHistogramData = HistogramData<int64_t>;
/**
* Constructor.
* @param family Broker layer family handle for this metric.
* @param label_values The string values for each of the metric's labels.
* @param histogram The histogram's data (sum and individual buckets).
*/
CollectedHistogramMetric(const broker::telemetry::metric_family_hdl* family,
std::vector<std::string_view> label_values,
std::variant<DblHistogramData, IntHistogramData> histogram)
: family(family), label_values(std::move(label_values)), histogram(std::move(histogram))
{
}
const broker::telemetry::metric_family_hdl* family;
std::vector<std::string_view> label_values;
std::variant<DblHistogramData, IntHistogramData> histogram;
/**
* @return A script layer Telemetry::HistogramMetric record for this histogram.
*/
zeek::RecordValPtr AsHistogramMetricRecord() const;
};
/**
* @return A script layer Telemetry::MetricOpts record for the given metric family.
* @param metric_typ The type of metric.
* @param family Broker layer family handle for the family.
* @tparam T The underlying data type (double or int64_t)
*/
template <typename T>
zeek::RecordValPtr GetMetricOptsRecord(MetricType metric_type,
const broker::telemetry::metric_family_hdl* family);
/**
* @return All counter and gauge metrics and their values matching prefix and name.
* @param prefix The prefix pattern to use for filtering. Supports globbing.
* @param name The name pattern to use for filtering. Supports globbing.
*/
std::vector<CollectedValueMetric> CollectMetrics(std::string_view prefix,
std::string_view name);
/**
* @return All histogram metrics and their data matching prefix and name.
* @param prefix The prefix pattern to use for filtering. Supports globbing.
* @param name The name pattern to use for filtering. Supports globbing.
*/
std::vector<CollectedHistogramMetric> CollectHistogramMetrics(std::string_view prefix,
std::string_view name);
/** /**
* @return A counter metric family. Creates the family lazily if necessary. * @return A counter metric family. Creates the family lazily if necessary.
* @param prefix The prefix (namespace) this family belongs to. * @param prefix The prefix (namespace) this family belongs to.
@ -67,14 +197,14 @@ public:
{ {
if constexpr ( std::is_same<ValueType, int64_t>::value ) if constexpr ( std::is_same<ValueType, int64_t>::value )
{ {
auto fam = int_counter_fam(ptr(), prefix, name, labels, helptext, unit, is_sum); auto fam = int_counter_fam(Ptr(), prefix, name, labels, helptext, unit, is_sum);
return IntCounterFamily{fam}; return IntCounterFamily{fam};
} }
else else
{ {
static_assert(std::is_same<ValueType, double>::value, static_assert(std::is_same<ValueType, double>::value,
"metrics only support int64_t and double values"); "metrics only support int64_t and double values");
auto fam = dbl_counter_fam(ptr(), prefix, name, labels, helptext, unit, is_sum); auto fam = dbl_counter_fam(Ptr(), prefix, name, labels, helptext, unit, is_sum);
return DblCounterFamily{fam}; return DblCounterFamily{fam};
} }
} }
@ -163,14 +293,14 @@ public:
{ {
if constexpr ( std::is_same<ValueType, int64_t>::value ) if constexpr ( std::is_same<ValueType, int64_t>::value )
{ {
auto fam = int_gauge_fam(ptr(), prefix, name, labels, helptext, unit, is_sum); auto fam = int_gauge_fam(Ptr(), prefix, name, labels, helptext, unit, is_sum);
return IntGaugeFamily{fam}; return IntGaugeFamily{fam};
} }
else else
{ {
static_assert(std::is_same<ValueType, double>::value, static_assert(std::is_same<ValueType, double>::value,
"metrics only support int64_t and double values"); "metrics only support int64_t and double values");
auto fam = dbl_gauge_fam(ptr(), prefix, name, labels, helptext, unit, is_sum); auto fam = dbl_gauge_fam(Ptr(), prefix, name, labels, helptext, unit, is_sum);
return DblGaugeFamily{fam}; return DblGaugeFamily{fam};
} }
} }
@ -281,7 +411,7 @@ public:
{ {
if constexpr ( std::is_same<ValueType, int64_t>::value ) if constexpr ( std::is_same<ValueType, int64_t>::value )
{ {
auto fam = int_histogram_fam(ptr(), prefix, name, labels, default_upper_bounds, auto fam = int_histogram_fam(Ptr(), prefix, name, labels, default_upper_bounds,
helptext, unit, is_sum); helptext, unit, is_sum);
return IntHistogramFamily{fam}; return IntHistogramFamily{fam};
} }
@ -289,7 +419,7 @@ public:
{ {
static_assert(std::is_same<ValueType, double>::value, static_assert(std::is_same<ValueType, double>::value,
"metrics only support int64_t and double values"); "metrics only support int64_t and double values");
auto fam = dbl_histogram_fam(ptr(), prefix, name, labels, default_upper_bounds, auto fam = dbl_histogram_fam(Ptr(), prefix, name, labels, default_upper_bounds,
helptext, unit, is_sum); helptext, unit, is_sum);
return DblHistogramFamily{fam}; return DblHistogramFamily{fam};
} }
@ -396,6 +526,7 @@ protected:
std::string_view buf[10]; std::string_view buf[10];
for ( size_t index = 0; index < xs.size(); ++index ) for ( size_t index = 0; index < xs.size(); ++index )
buf[index] = xs[index].first; buf[index] = xs[index].first;
return continuation(Span{buf, xs.size()}); return continuation(Span{buf, xs.size()});
} }
else else
@ -403,17 +534,23 @@ protected:
std::vector<std::string_view> buf; std::vector<std::string_view> buf;
for ( auto x : xs ) for ( auto x : xs )
buf.emplace_back(x.first, x.second); buf.emplace_back(x.first, x.second);
return continuation(Span{buf}); return continuation(Span{buf});
} }
} }
broker::telemetry::metric_registry_impl* ptr() { return pimpl.get(); } broker::telemetry::metric_registry_impl* Ptr() { return pimpl.get(); }
// Connects all the dots after the Broker Manager constructed the endpoint // Connects all the dots after the Broker Manager constructed the endpoint
// for this Zeek instance. Called from Broker::Manager::InitPostScript(). // for this Zeek instance. Called from Broker::Manager::InitPostScript().
void InitPostBrokerSetup(broker::endpoint&); void InitPostBrokerSetup(broker::endpoint&);
IntrusivePtr<broker::telemetry::metric_registry_impl> pimpl; IntrusivePtr<broker::telemetry::metric_registry_impl> pimpl;
private:
// Caching of metric_family_hdl instances to their Zeek record representation.
std::unordered_map<const broker::telemetry::metric_family_hdl*, zeek::RecordValPtr>
metric_opts_cache;
}; };
} // namespace zeek::telemetry } // namespace zeek::telemetry

View file

@ -1,8 +1,16 @@
##! Functions for accessing counter metrics from script land. ##! Functions for accessing counter metrics from script land.
module Telemetry; module Telemetry;
enum MetricType %{
DOUBLE_COUNTER,
INT_COUNTER,
DOUBLE_GAUGE,
INT_GAUGE,
DOUBLE_HISTOGRAM,
INT_HISTOGRAM,
%}
%%{ %%{
#include "zeek/telemetry/Counter.h" #include "zeek/telemetry/Counter.h"
#include "zeek/telemetry/Gauge.h" #include "zeek/telemetry/Gauge.h"
@ -548,3 +556,29 @@ function Telemetry::__dbl_histogram_sum%(val: opaque of dbl_histogram_metric%):
return zeek::make_intrusive<DoubleVal>(0.0); return zeek::make_intrusive<DoubleVal>(0.0);
} }
%} %}
function Telemetry::__collect_metrics%(prefix: string, name: string%): any_vec
%{
auto metrics = telemetry_mgr->CollectMetrics(sv(prefix), sv(name));
static auto metrics_vector_type = zeek::id::find_type<VectorType>("any_vec");
auto vec = zeek::make_intrusive<VectorVal>(metrics_vector_type);
for ( const auto& m : metrics )
vec->Append(m.AsMetricRecord());
return vec;
%}
function Telemetry::__collect_histogram_metrics%(prefix: string, name: string%): any_vec
%{
auto metrics = telemetry_mgr->CollectHistogramMetrics(sv(prefix), sv(name));
static auto metrics_vector_type = zeek::id::find_type<VectorType>("any_vec");
auto vec = zeek::make_intrusive<VectorVal>(metrics_vector_type);
for ( const auto& m : metrics )
vec->Append(m.AsHistogramMetricRecord());
return vec;
%}

View file

@ -336,6 +336,9 @@ scripts/base/init-default.zeek
scripts/base/frameworks/netcontrol/drop.zeek scripts/base/frameworks/netcontrol/drop.zeek
scripts/base/frameworks/netcontrol/shunt.zeek scripts/base/frameworks/netcontrol/shunt.zeek
scripts/base/frameworks/netcontrol/non-cluster.zeek scripts/base/frameworks/netcontrol/non-cluster.zeek
scripts/base/frameworks/telemetry/__load__.zeek
scripts/base/frameworks/telemetry/main.zeek
scripts/base/misc/version.zeek
scripts/base/protocols/conn/__load__.zeek scripts/base/protocols/conn/__load__.zeek
scripts/base/protocols/conn/main.zeek scripts/base/protocols/conn/main.zeek
scripts/base/protocols/conn/contents.zeek scripts/base/protocols/conn/contents.zeek
@ -445,7 +448,6 @@ scripts/base/init-default.zeek
scripts/base/misc/find-checksum-offloading.zeek scripts/base/misc/find-checksum-offloading.zeek
scripts/base/misc/find-filtered-trace.zeek scripts/base/misc/find-filtered-trace.zeek
build/scripts/base/misc/installation.zeek build/scripts/base/misc/installation.zeek
scripts/base/misc/version.zeek
build/scripts/builtin-plugins/__preload__.zeek build/scripts/builtin-plugins/__preload__.zeek
build/scripts/builtin-plugins/Zeek_Spicy/__preload__.zeek build/scripts/builtin-plugins/Zeek_Spicy/__preload__.zeek
build/scripts/builtin-plugins/__load__.zeek build/scripts/builtin-plugins/__load__.zeek

View file

@ -57,6 +57,8 @@ ssh
ssl ssl
stats stats
syslog syslog
telemetry
telemetry_histogram
traceroute traceroute
tunnel tunnel
unified2 unified2

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,25 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
### zeek_session_metrics |2|
Telemetry::INT_GAUGE, zeek, active-sessions, [protocol], [tcp], 500.0
count_value, 500
Telemetry::INT_COUNTER, zeek, total-sessions, [protocol], [tcp], 500.0
count_value, 500
### bt* metrics |5|
Telemetry::DOUBLE_COUNTER, btest, a_test, [x, y], [a, b], 1.0
Telemetry::DOUBLE_COUNTER, btest, a_test, [x, y], [a, c], 2.0
Telemetry::DOUBLE_COUNTER, btest, b_test, [x, y], [a, b], 10.0
Telemetry::DOUBLE_COUNTER, btest, b_test, [x, y], [a, c], 20.0
Telemetry::DOUBLE_COUNTER, btest, c_test, [x, y], [a, b], 200.0
### btest_a_metrics |2|
Telemetry::DOUBLE_COUNTER, btest, a_test, [x, y], [a, b], 1.0
Telemetry::DOUBLE_COUNTER, btest, a_test, [x, y], [a, c], 2.0
### btest_b_metrics |2|
Telemetry::DOUBLE_COUNTER, btest, b_test, [x, y], [a, b], 10.0
Telemetry::DOUBLE_COUNTER, btest, b_test, [x, y], [a, c], 20.0
### system_metrics |3|
Telemetry::DOUBLE_GAUGE, system, sensor_temperature, [name], [cpu0], 43.0
Telemetry::DOUBLE_GAUGE, system, sensor_temperature, [name], [cpu1], 44.1
Telemetry::DOUBLE_GAUGE, system, sensor_temperature, [name], [cpu3], 42.2
### btest_histogram_metrics |2|
Telemetry::DOUBLE_HISTOGRAM, btest, sample_histogram, [1.0, 2.0, 3.0, 4.0, 5.0, inf], [dim], [a], [2.0, 2.0, 0.0, 0.0, 0.0, 1.0], 11.5, 5.0
Telemetry::DOUBLE_HISTOGRAM, btest, sample_histogram, [1.0, 2.0, 3.0, 4.0, 5.0, inf], [dim], [b], [1.0, 0.0, 0.0, 0.0, 0.0, 1.0], 7.5, 2.0

View file

@ -0,0 +1,13 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
Telemetry::DOUBLE_HISTOGRAM, zeek, connection_duration
[]
[]
[2.0, 3.0, 4.0, 5.0, 6.0, 10.0, inf]
[0.0, 322.0, 90.0, 5.0, 76.0, 7.0, 0.0]
500.0, 1650.264644
Telemetry::DOUBLE_HISTOGRAM, zeek, realistic_connection_duration
[proto]
[tcp]
[0.1, 1.0, 10.0, 30.0, 60.0, 120.0, 300.0, 900.0, 1800.0, inf]
[0.0, 0.0, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
500.0, 1650.264644

View file

@ -0,0 +1,26 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
### broker |5|
Telemetry::INT_COUNTER, broker, processed-elements, [type], [data], 0.0
count_value, 0
Telemetry::INT_COUNTER, broker, processed-elements, [type], [command], 0.0
count_value, 0
Telemetry::INT_COUNTER, broker, processed-elements, [type], [routing-update], 0.0
count_value, 0
Telemetry::INT_COUNTER, broker, processed-elements, [type], [ping], 0.0
count_value, 0
Telemetry::INT_COUNTER, broker, processed-elements, [type], [pong], 0.0
count_value, 0
### caf |5|
Telemetry::INT_COUNTER, caf.system, rejected-messages, [], [], 0.0
count_value, 0
Telemetry::INT_COUNTER, caf.system, processed-messages, [], [], 7.0
count_value, 7
Telemetry::INT_GAUGE, caf.system, running-actors, [], [], 2.0
count_value, 2
Telemetry::INT_GAUGE, caf.system, queued-messages, [], [], 0.0
count_value, 0
Telemetry::INT_GAUGE, caf.actor, mailbox-size, [name], [broker.core], 0.0
count_value, 0
### caf |2|
Telemetry::DOUBLE_HISTOGRAM, caf.actor, processing-time, [0.00001, 0.0001, 0.0005, 0.001, 0.01, 0.1, 0.5, 1.0, 5.0, inf], [name], [broker.core]
Telemetry::DOUBLE_HISTOGRAM, caf.actor, mailbox-time, [0.00001, 0.0001, 0.0005, 0.001, 0.01, 0.1, 0.5, 1.0, 5.0, inf], [name], [broker.core]

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path telemetry
#open XXXX-XX-XX-XX-XX-XX
#fields ts peer metric_type prefix name unit labels label_values value
#types time string string string string string vector[string] vector[string] double
XXXXXXXXXX.XXXXXX zeek counter btest connections - proto tcp 500.0
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,12 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path telemetry_histogram
#open XXXX-XX-XX-XX-XX-XX
#fields ts peer prefix name unit labels label_values bounds values sum observations
#types time string string string string vector[string] vector[string] vector[double] vector[double] double double
XXXXXXXXXX.XXXXXX zeek btest connection_duration seconds (empty) (empty) 2.0,3.0,4.0,5.0,6.0,10.0,inf 0.0,0.0,0.0,0.0,0.0,0.0,0.0 0.0 0.0
XXXXXXXXXX.XXXXXX zeek btest connection_duration seconds (empty) (empty) 2.0,3.0,4.0,5.0,6.0,10.0,inf 0.0,322.0,90.0,5.0,76.0,7.0,0.0 1650.264644 500.0
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
XXXXXXXXXX.XXXXXX - _zeek connection_duration (empty) (empty) 2.0,3.0,4.0,5.0,6.0,10.0,inf 0.0,0.0,0.0,0.0,0.0,0.0,0.0 0.0 0.0
XXXXXXXXXX.XXXXXX - _zeek connection_duration (empty) (empty) 2.0,3.0,4.0,5.0,6.0,10.0,inf 0.0,322.0,90.0,5.0,76.0,7.0,0.0 1650.264644 500.0

View file

@ -0,0 +1,5 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
XXXXXXXXXX.XXXXXX zeek gauge zeek active-sessions - protocol tcp 1.0
XXXXXXXXXX.XXXXXX zeek counter zeek total-sessions - protocol tcp 1.0
XXXXXXXXXX.XXXXXX zeek gauge zeek active-sessions - protocol tcp 500.0
XXXXXXXXXX.XXXXXX zeek counter zeek total-sessions - protocol tcp 500.0

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
XXXXXXXXXX.XXXXXX zeek zeek connection_duration seconds (empty) (empty) 2.0,3.0,4.0,5.0,6.0,10.0,inf 0.0,0.0,0.0,0.0,0.0,0.0,0.0 0.0 0.0
XXXXXXXXXX.XXXXXX zeek zeek connection_duration seconds (empty) (empty) 2.0,3.0,4.0,5.0,6.0,10.0,inf 0.0,322.0,90.0,5.0,76.0,7.0,0.0 1650.264644 500.0

View file

@ -1,4 +1,3 @@
#include "Plugin.h" #include "Plugin.h"
#include <Conn.h> #include <Conn.h>
@ -8,6 +7,7 @@
#include <RunState.h> #include <RunState.h>
#include <threading/Formatter.h> #include <threading/Formatter.h>
#include <cstring> #include <cstring>
#include <set>
namespace btest::plugin::Demo_Hooks namespace btest::plugin::Demo_Hooks
{ {
@ -16,6 +16,22 @@ Plugin plugin;
using namespace btest::plugin::Demo_Hooks; using namespace btest::plugin::Demo_Hooks;
// Sanitize arguments for the following functions with (...). These
// receiving the current version string or parts of it and make the
// baseline non-deterministic.
static std::set<std::string> sanitized_functions = {
"Version::parse",
"gsub",
"split_string1",
"lstrip",
"to_count",
"cat",
"Telemetry::__dbl_gauge_metric_get_or_add",
"Telemetry::gauge_with",
"Telemetry::make_labels",
"Telemetry::gauge_family_set",
};
zeek::plugin::Configuration Plugin::Configure() zeek::plugin::Configuration Plugin::Configure()
{ {
EnableHook(zeek::plugin::HOOK_LOAD_FILE); EnableHook(zeek::plugin::HOOK_LOAD_FILE);
@ -58,11 +74,7 @@ static void describe_hook_args(const zeek::plugin::HookArgumentList& args, zeek:
// For function calls we remove args for unstable arguments // For function calls we remove args for unstable arguments
// from parsing the version in `base/misc/version`. // from parsing the version in `base/misc/version`.
if ( i->GetType() == zeek::plugin::HookArgument::FUNC && if ( i->GetType() == zeek::plugin::HookArgument::FUNC &&
(::strcmp(d->Description(), "Version::parse") == 0 || sanitized_functions.count(d->Description()) != 0 )
::strcmp(d->Description(), "gsub") == 0 ||
::strcmp(d->Description(), "split_string1") == 0 ||
::strcmp(d->Description(), "lstrip") == 0 ||
::strcmp(d->Description(), "to_count") == 0) )
serialize_args = false; serialize_args = false;
continue; continue;
@ -105,10 +117,8 @@ std::pair<bool, zeek::ValPtr> Plugin::HookFunctionCall(const zeek::Func* func,
// For function calls we remove args for unstable arguments // For function calls we remove args for unstable arguments
// from parsing the version in `base/misc/version`. // from parsing the version in `base/misc/version`.
if ( ::strcmp(d.Description(), "Version::parse") == 0 || //
::strcmp(d.Description(), "gsub") == 0 || if ( sanitized_functions.count(d.Description()) != 0 )
::strcmp(d.Description(), "split_string1") == 0 ||
::strcmp(d.Description(), "lstrip") == 0 || ::strcmp(d.Description(), "to_count") == 0 )
d.Add("(...)"); d.Add("(...)");
else else
zeek::plugin::HookArgument(args).Describe(&d); zeek::plugin::HookArgument(args).Describe(&d);

View file

@ -6,5 +6,4 @@
# @TEST-EXEC: hexdump -C unprocessed.pcap > unprocessed.pcap.hex # @TEST-EXEC: hexdump -C unprocessed.pcap > unprocessed.pcap.hex
# @TEST-EXEC: btest-diff unprocessed.pcap.hex # @TEST-EXEC: btest-diff unprocessed.pcap.hex
@unload base/misc/version
@load base/init-default @load base/init-default

View file

@ -0,0 +1,116 @@
# @TEST-DOC: Using and listing of counters and gauges using the telemetry module.
# @TEST-EXEC: zcat <$TRACES/echo-connections.pcap.gz | zeek -b -Cr - %INPUT > out
# @TEST-EXEC: btest-diff out
# @TEST-EXEC-FAIL: test -f reporter.log
@load base/frameworks/telemetry
global btest_a_cf = Telemetry::register_counter_family([
$prefix="btest",
$name="a_test",
$unit="1",
$help_text="A btest metric",
$labels=vector("x", "y")
]);
global btest_b_cf = Telemetry::register_counter_family([
$prefix="btest",
$name="b_test",
$unit="1",
$help_text="Another btest metric",
$labels=vector("x", "y")
]);
global btest_c_cf = Telemetry::register_counter_family([
$prefix="btest",
$name="c_test",
$unit="1",
$help_text="The last btest metric",
$labels=vector("x", "y")
]);
global system_sensor_temp_gf = Telemetry::register_gauge_family([
$prefix="system",
$name="sensor_temperature",
$unit="celsius",
$help_text="Temperatures reported by sensors in the system",
$labels=vector("name")
]);
global btest_sample_histogram_hf = Telemetry::register_histogram_family([
$prefix="btest",
$name="sample_histogram",
$unit="1",
$help_text="A sample histogram that is not returned by Telemetry::collect_metrics",
$bounds=vector(1.0, 2.0, 3.0, 4.0, 5.0),
$labels=vector("dim")
]);
function print_metrics(what: string, metrics: vector of Telemetry::Metric)
{
print fmt("### %s |%s|", what, |metrics|);
for (i in metrics)
{
local m = metrics[i];
print m$opts$metric_type, m$opts$prefix, m$opts$name, m$opts$labels, m$labels, m$value;
if (m?$count_value)
print "count_value", m$count_value;
}
}
function print_histogram_metrics(what: string, metrics: vector of Telemetry::HistogramMetric)
{
print fmt("### %s |%s|", what, |metrics|);
for (i in metrics)
{
local m = metrics[i];
print m$opts$metric_type, m$opts$prefix, m$opts$name, m$opts$bounds, m$opts$labels, m$labels, m$values, m$sum, m$observations;
}
}
event zeek_done() &priority=-100
{
Telemetry::counter_family_inc(btest_a_cf, vector("a", "b"));
Telemetry::counter_family_inc(btest_a_cf, vector("a", "c"));
Telemetry::counter_family_inc(btest_a_cf, vector("a", "c"));
Telemetry::counter_family_inc(btest_b_cf, vector("a", "b"), 10.0);
Telemetry::counter_family_inc(btest_b_cf, vector("a", "c"), 20.0);
Telemetry::counter_family_set(btest_c_cf, vector("a", "b"), 100.0);
Telemetry::counter_family_set(btest_c_cf, vector("a", "b"), 200.0);
Telemetry::gauge_family_set(system_sensor_temp_gf, vector("cpu0"), 43.0);
Telemetry::gauge_family_set(system_sensor_temp_gf, vector("cpu1"), 43.1);
Telemetry::gauge_family_inc(system_sensor_temp_gf, vector("cpu1"));
Telemetry::gauge_family_set(system_sensor_temp_gf, vector("cpu3"), 43.2);
Telemetry::gauge_family_dec(system_sensor_temp_gf, vector("cpu3"));
Telemetry::histogram_family_observe(btest_sample_histogram_hf, vector("a"), 0.5);
Telemetry::histogram_family_observe(btest_sample_histogram_hf, vector("a"), 0.9);
Telemetry::histogram_family_observe(btest_sample_histogram_hf, vector("a"), 1.1);
Telemetry::histogram_family_observe(btest_sample_histogram_hf, vector("a"), 2.0);
Telemetry::histogram_family_observe(btest_sample_histogram_hf, vector("a"), 7.0);
Telemetry::histogram_family_observe(btest_sample_histogram_hf, vector("b"), 0.5);
Telemetry::histogram_family_observe(btest_sample_histogram_hf, vector("b"), 7.0);
local zeek_session_metrics = Telemetry::collect_metrics("zeek", "*session*");
print_metrics("zeek_session_metrics", zeek_session_metrics);
local all_btest_metrics = Telemetry::collect_metrics("bt*", "*");
print_metrics("bt* metrics", all_btest_metrics);
local btest_a_metrics = Telemetry::collect_metrics("btest", "a_*");
print_metrics("btest_a_metrics", btest_a_metrics);
local btest_b_metrics = Telemetry::collect_metrics("btest", "b_*");
print_metrics("btest_b_metrics", btest_b_metrics);
local system_metrics = Telemetry::collect_metrics("system");
print_metrics("system_metrics", system_metrics);
local histogram_metrics = Telemetry::collect_histogram_metrics("btest");
print_histogram_metrics("btest_histogram_metrics", histogram_metrics);
}

View file

@ -0,0 +1,48 @@
# @TEST-EXEC: zcat <$TRACES/echo-connections.pcap.gz | zeek -b -Cr - %INPUT > out
# @TEST-EXEC: btest-diff out
# @TEST-EXEC-FAIL: test -f reporter.log
@load base/frameworks/telemetry
global connection_duration_hf = Telemetry::register_histogram_family([
$prefix="zeek",
$name="connection_duration",
$unit="seconds",
$help_text="Monitored connection durations",
$bounds=vector(2.0, 3.0, 4.0, 5.0, 6.0, 10.0)
]);
global realistic_connection_duration_hf = Telemetry::register_histogram_family([
$prefix="zeek",
$name="realistic_connection_duration",
$labels=vector("proto"),
$unit="seconds",
$help_text="Monitored connection durations by protocol",
$bounds=vector(0.1, 1.0, 10.0, 30.0, 60.0, 120.0, 300, 900.0, 1800.0)
]);
global connection_duration_h = Telemetry::histogram_with(connection_duration_hf);
event connection_state_remove(c: connection)
{
Telemetry::histogram_observe(connection_duration_h, interval_to_double(c$duration));
local proto = to_lower(cat(get_port_transport_proto(c$id$resp_p)));
Telemetry::histogram_family_observe(realistic_connection_duration_hf,
vector(proto),
interval_to_double(c$duration));
}
event zeek_done() &priority=-100
{
local histogram_metrics = Telemetry::collect_histogram_metrics("zeek", "*connection_duration");
for (i in histogram_metrics)
{
local hm = histogram_metrics[i];
print hm$opts$metric_type, hm$opts$prefix, hm$opts$name;
print hm$opts$labels;
print hm$labels;
print hm$opts$bounds;
print hm$values;
print hm$observations, hm$sum;
}
}

View file

@ -0,0 +1,43 @@
# @TEST-DOC: Query some internal broker/caf related metrics as they use the int64_t versions, too.
# @TEST-EXEC: zcat <$TRACES/echo-connections.pcap.gz | zeek -b -Cr - %INPUT > out
# @TEST-EXEC: btest-diff out
# @TEST-EXEC-FAIL: test -f reporter.log
@load base/frameworks/telemetry
function print_histogram_metrics(what: string, metrics: vector of Telemetry::HistogramMetric)
{
print fmt("### %s |%s|", what, |metrics|);
for (i in metrics)
{
local m = metrics[i];
print m$opts$metric_type, m$opts$prefix, m$opts$name, m$opts$bounds, m$opts$labels, m$labels;
# Don't output actual values as they are runtime dependent.
# print m$values, m$sum, m$observations;
if ( m$opts?$count_bounds )
print m$opts$count_bounds;
}
}
function print_metrics(what: string, metrics: vector of Telemetry::Metric)
{
print fmt("### %s |%s|", what, |metrics|);
for (i in metrics)
{
local m = metrics[i];
print m$opts$metric_type, m$opts$prefix, m$opts$name, m$opts$labels, m$labels, m$value;
if (m?$count_value)
print "count_value", m$count_value;
}
}
event zeek_done() &priority=-100
{
local broker_metrics = Telemetry::collect_metrics("broker", "*");
print_metrics("broker", broker_metrics);
local caf_metrics = Telemetry::collect_metrics("caf*", "*");
print_metrics("caf", caf_metrics);
local caf_histogram_metrics = Telemetry::collect_histogram_metrics("caf*", "*");
print_histogram_metrics("caf", caf_histogram_metrics);
}

View file

@ -0,0 +1,34 @@
# @TEST-DOC: Tests that setting log_prefixes filters out the zeek metrics normally created.
# @TEST-EXEC: zcat <$TRACES/echo-connections.pcap.gz | zeek -b -Cr - %INPUT > out
# @TEST-EXEC: btest-diff telemetry.log
# @TEST-EXEC: btest-diff telemetry_histogram.log
@load frameworks/telemetry/log
redef Telemetry::log_prefixes = {"btest"};
global connections_by_proto_cf = Telemetry::register_counter_family([
$prefix="btest",
$name="connections",
$unit="1",
$help_text="Total number of monitored connections",
$labels=vector("proto")
]);
global connection_duration_hf = Telemetry::register_histogram_family([
$prefix="btest",
$name="connection_duration",
$unit="seconds",
$help_text="Monitored connection duration",
$bounds=vector(2.0, 3.0, 4.0, 5.0, 6.0, 10.0)
]);
global connection_duration_h = Telemetry::histogram_with(connection_duration_hf);
event connection_state_remove(c: connection)
{
local proto = to_lower(cat(get_port_transport_proto(c$id$orig_p)));
Telemetry::counter_family_inc(connections_by_proto_cf, vector(proto));
Telemetry::histogram_observe(connection_duration_h, interval_to_double(c$duration));
}

View file

@ -0,0 +1,25 @@
# @TEST-DOC: Test loading of telemetry/log and smoke check the telemetry.log file
# @TEST-EXEC: zcat <$TRACES/echo-connections.pcap.gz | zeek -b -Cr - %INPUT > out
# @TEST-EXEC: grep 'zeek.*sessions' telemetry.log > telemetry.log.filtered
# @TEST-EXEC: grep 'zeek.*connection_duration' telemetry_histogram.log > telemetry_histogram.log.filtered
# @TEST-EXEC: btest-diff telemetry.log.filtered
# @TEST-EXEC: btest-diff telemetry_histogram.log.filtered
@load frameworks/telemetry/log
global connection_duration_hf = Telemetry::register_histogram_family([
$prefix="zeek",
$name="connection_duration",
$unit="seconds",
$help_text="Monitored connection duration",
$bounds=vector(2.0, 3.0, 4.0, 5.0, 6.0, 10.0)
]);
global connection_duration_h = Telemetry::histogram_with(connection_duration_hf);
event connection_state_remove(c: connection)
{
Telemetry::histogram_observe(connection_duration_h, interval_to_double(c$duration));
}

View file

@ -1 +1 @@
5b2a6d78f789f1271b68123875ef66eaaba6f3e8 c57b93b0d3aa4ee69452b039055122d4bec9058f

View file

@ -1 +1 @@
d8088ba741389aa092b5fb284d0849401234809f 5b83cfbcf5eb52b28773dded8e1d02d350737ec5

View file

@ -12,3 +12,13 @@
# (json-logs.zeek activates this). # (json-logs.zeek activates this).
redef LogAscii::use_json = F; redef LogAscii::use_json = F;
@endif @endif
# Exclude process metrics, they are non-deterministic.
redef Telemetry::log_prefixes -= { "process" };
# Prevent the version_info metric from being logged as it's not deterministic.
hook Telemetry::log_policy(rec: Telemetry::Info, id: Log::ID, filter: Log::Filter)
{
if ( rec$prefix == "zeek" && rec$name == "version_info" )
break;
}