zeek/src/telemetry/telemetry_functions.bif
2025-08-15 13:57:27 -07:00

298 lines
9.5 KiB
C++

##! Functions for accessing counter metrics from script land.
module Telemetry;
%%{
#include <span>
#include "zeek/telemetry/Counter.h"
#include "zeek/telemetry/Gauge.h"
#include "zeek/telemetry/Histogram.h"
#include "zeek/telemetry/Manager.h"
#include "zeek/telemetry/Opaques.h"
namespace {
template <class ScriptHandle, class Fun>
auto with(zeek::Val* val, const char* error_msg, Fun fun)
{
if ( auto ptr = dynamic_cast<ScriptHandle*>(val) )
{
fun(ptr->GetHandle());
return zeek::val_mgr->True();
}
else
{
zeek::reporter->Error("%s", error_msg);
return zeek::val_mgr->False();
}
}
std::string_view sv(const zeek::String* str) noexcept
{
auto len = static_cast<size_t>(str->Len());
return {reinterpret_cast<char*>(str->Bytes()), len};
};
std::string_view sv(const zeek::StringVal* val) noexcept
{
return sv(val->AsString());
};
std::vector<std::string_view> sv_vec(zeek::VectorVal* xs)
{
std::vector<std::string_view> result;
if ( xs )
for ( unsigned index = 0; index < xs->Size(); ++index )
if ( auto ptr = xs->ValAt(index) )
if ( auto* str = ptr->AsString() )
result.emplace_back(sv(str));
return result;
}
std::vector<zeek::telemetry::LabelView> sv_tbl(zeek::TableVal* xs)
{
std::vector<zeek::telemetry::LabelView> result;
if ( xs )
{
for ( auto& val : *xs->Get() )
{
auto val_ptr = val.value->GetVal();
result.emplace_back(std::string_view{val.GetKey(), val.key_size},
sv(val_ptr->AsStringVal()));
}
}
return result;
}
bool labels_valid(std::span<const zeek::telemetry::LabelView> labels,
std::span<const std::string> label_names)
{
auto key_in_label_names = [keys{label_names}](auto x)
{
return std::find(keys.begin(), keys.end(), x.first) != keys.end();
};
return labels.size() == label_names.size()
&& std::ranges::all_of(labels, key_in_label_names);
}
template <class IntOrDbl>
auto to_std_vec(zeek::VectorVal* xs)
{
std::vector<IntOrDbl> result;
if( xs )
for ( unsigned index = 0; index < xs->Size() ; ++index )
{
if constexpr (std::is_same_v<IntOrDbl, int64_t>)
result.emplace_back(xs->ValAt(index)->AsInt());
else
result.emplace_back(xs->ValAt(index)->AsDouble());
}
return result;
}
template <class IntOrDbl>
auto to_std_vec(zeek::Val* xs)
{
return to_std_vec<IntOrDbl>(xs->AsVectorVal());
}
}
%%}
# -- Counter ----------------------------------------------------------------
function Telemetry::__counter_family%(prefix: string,
name: string,
labels: string_vec,
helptext: string &default = "Zeek Script Metric",
unit: string &default = ""%): opaque of counter_metric_family
%{
auto lbl_vec = sv_vec(labels->AsVectorVal());
auto hdl = telemetry_mgr->CounterFamily(sv(prefix), sv(name), lbl_vec,
sv(helptext), sv(unit));
return zeek::make_intrusive<CounterMetricFamilyVal>(hdl);
%}
function Telemetry::__counter_metric_get_or_add%(family: opaque of counter_metric_family,
labels: table_string_of_string%): opaque of counter_metric
%{
using ResultType = zeek::IntrusivePtr<CounterMetricFamilyVal>;
if ( auto ptr = dynamic_cast<zeek::CounterMetricFamilyVal*>(family) )
{
auto hdl = ptr->GetHandle();
auto lbl_map = sv_tbl(labels->AsTableVal());
if ( labels_valid(lbl_map, hdl->LabelNames()) )
{
auto res = hdl->GetOrAdd(lbl_map);
return zeek::make_intrusive<CounterMetricVal>(res);
}
else
{
zeek::reporter->Error("Telemetry::counter_metric_get_or_add: invalid label dimensions.");
return ResultType{nullptr};
}
}
else
{
zeek::reporter->Error("Telemetry::counter_metric_get_or_add: invalid handle.");
return ResultType{nullptr};
}
%}
function Telemetry::__counter_inc%(val: opaque of counter_metric,
amount: double &default = 1.0%): bool
%{
return with<CounterMetricVal>(val, "Telemetry::counter_inc: invalid handle.", [amount](auto hdl) { hdl->Inc(amount); });
%}
function Telemetry::__counter_value%(val: opaque of counter_metric%): double
%{
if ( auto ptr = dynamic_cast<zeek::CounterMetricVal*>(val) )
{
return zeek::make_intrusive<zeek::DoubleVal>(ptr->GetHandle()->Value());
}
else
{
zeek::reporter->Error("Telemetry::counter_value: invalid handle.");
return zeek::make_intrusive<zeek::DoubleVal>(0);
}
%}
# -- Gauge ------------------------------------------------------------------
function Telemetry::__gauge_family%(prefix: string,
name: string,
labels: string_vec,
helptext: string &default = "Zeek Script Metric",
unit: string &default = ""%): opaque of gauge_metric_family
%{
auto lbl_vec = sv_vec(labels->AsVectorVal());
auto hdl = telemetry_mgr->GaugeFamily(sv(prefix), sv(name), lbl_vec,
sv(helptext), sv(unit));
return zeek::make_intrusive<GaugeMetricFamilyVal>(hdl);
%}
function Telemetry::__gauge_metric_get_or_add%(family: opaque of gauge_metric_family,
labels: table_string_of_string%): opaque of gauge_metric
%{
using ResultType = zeek::IntrusivePtr<GaugeMetricFamilyVal>;
if ( auto ptr = dynamic_cast<zeek::GaugeMetricFamilyVal*>(family) )
{
auto hdl = ptr->GetHandle();
auto lbl_map = sv_tbl(labels->AsTableVal());
if ( labels_valid(lbl_map, hdl->LabelNames()) )
{
auto res = hdl->GetOrAdd(lbl_map);
return zeek::make_intrusive<GaugeMetricVal>(res);
}
else
{
zeek::reporter->Error("Telemetry::gauge_metric_get_or_add: invalid label dimensions.");
return ResultType{nullptr};
}
}
else
{
zeek::reporter->Error("Telemetry::gauge_metric_get_or_add: invalid handle.");
return ResultType{nullptr};
}
%}
function Telemetry::__gauge_inc%(val: opaque of gauge_metric,
amount: double &default = 1.0%): bool
%{
return with<GaugeMetricVal>(val, "Telemetry::gauge_inc: invalid handle.", [amount](auto hdl) { hdl->Inc(amount); });
%}
function Telemetry::__gauge_dec%(val: opaque of gauge_metric,
amount: double &default = 1.0%): bool
%{
return with<GaugeMetricVal>(val, "Telemetry::gauge_dec: invalid handle.", [amount](auto hdl) { hdl->Dec(amount); });
%}
function Telemetry::__gauge_value%(val: opaque of gauge_metric%): double
%{
if ( auto ptr = dynamic_cast<zeek::GaugeMetricVal*>(val) )
{
return zeek::make_intrusive<zeek::DoubleVal>(ptr->GetHandle()->Value());
}
else
{
zeek::reporter->Error("Telemetry::gauge_value: invalid handle.");
return zeek::make_intrusive<zeek::DoubleVal>(0.0);
}
%}
# -- Histogram --------------------------------------------------------------
function Telemetry::__histogram_family%(prefix: string,
name: string,
labels: string_vec,
bounds: double_vec,
helptext: string &default = "Zeek Script Metric",
unit: string &default = ""%): opaque of histogram_metric_family
%{
auto lbl_vec = sv_vec(labels->AsVectorVal());
auto std_bounds = to_std_vec<double>(bounds);
auto hdl = telemetry_mgr->HistogramFamily(sv(prefix), sv(name), lbl_vec,
std_bounds, sv(helptext),
sv(unit));
return zeek::make_intrusive<HistogramMetricFamilyVal>(hdl);
%}
function Telemetry::__histogram_metric_get_or_add%(family: opaque of histogram_metric_family,
labels: table_string_of_string%): opaque of histogram_metric
%{
using ResultType = zeek::IntrusivePtr<HistogramMetricFamilyVal>;
if ( auto ptr = dynamic_cast<zeek::HistogramMetricFamilyVal*>(family) )
{
auto hdl = ptr->GetHandle();
auto lbl_map = sv_tbl(labels->AsTableVal());
if ( labels_valid(lbl_map, hdl->LabelNames()) )
{
auto res = hdl->GetOrAdd(lbl_map);
return zeek::make_intrusive<HistogramMetricVal>(res);
}
else
{
zeek::reporter->Error("Telemetry::histogram_metric_get_or_add: invalid label dimensions.");
return ResultType{nullptr};
}
}
else
{
zeek::reporter->Error("Telemetry::histogram_metric_get_or_add: invalid handle.");
return ResultType{nullptr};
}
%}
function Telemetry::__histogram_observe%(val: opaque of histogram_metric,
measurement: double%): bool
%{
return with<HistogramMetricVal>(val, "Telemetry::histogram_inc: invalid handle.",
[measurement](auto hdl) { hdl->Observe(measurement); });
%}
function Telemetry::__histogram_sum%(val: opaque of histogram_metric%): double
%{
if ( auto ptr = dynamic_cast<zeek::HistogramMetricVal*>(val) )
{
return zeek::make_intrusive<zeek::DoubleVal>(ptr->GetHandle()->Sum());
}
else
{
zeek::reporter->Error("Telemetry::histogram_sum: invalid handle.");
return make_intrusive<zeek::DoubleVal>(0.0);
}
%}
function Telemetry::__collect_metrics%(prefix: string, name: string%): any_vec
%{
return telemetry_mgr->CollectMetrics(sv(prefix), sv(name));
%}
function Telemetry::__collect_histogram_metrics%(prefix: string, name: string%): any_vec
%{
return telemetry_mgr->CollectHistogramMetrics(sv(prefix), sv(name));
%}