##! Functions for accessing counter metrics from script land. module Telemetry; %%{ #include #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 auto with(zeek::Val* val, const char* error_msg, Fun fun) { if ( auto ptr = dynamic_cast(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(str->Len()); return {reinterpret_cast(str->Bytes()), len}; }; std::string_view sv(const zeek::StringVal* val) noexcept { return sv(val->AsString()); }; std::vector sv_vec(zeek::VectorVal* xs) { std::vector 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 sv_tbl(zeek::TableVal* xs) { std::vector 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 labels, std::span 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 auto to_std_vec(zeek::VectorVal* xs) { std::vector result; if( xs ) for ( unsigned index = 0; index < xs->Size() ; ++index ) { if constexpr (std::is_same_v) result.emplace_back(xs->ValAt(index)->AsInt()); else result.emplace_back(xs->ValAt(index)->AsDouble()); } return result; } template auto to_std_vec(zeek::Val* xs) { return to_std_vec(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(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; if ( auto ptr = dynamic_cast(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(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(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(val) ) { return zeek::make_intrusive(ptr->GetHandle()->Value()); } else { zeek::reporter->Error("Telemetry::counter_value: invalid handle."); return zeek::make_intrusive(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(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; if ( auto ptr = dynamic_cast(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(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(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(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(val) ) { return zeek::make_intrusive(ptr->GetHandle()->Value()); } else { zeek::reporter->Error("Telemetry::gauge_value: invalid handle."); return zeek::make_intrusive(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(bounds); auto hdl = telemetry_mgr->HistogramFamily(sv(prefix), sv(name), lbl_vec, std_bounds, sv(helptext), sv(unit)); return zeek::make_intrusive(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; if ( auto ptr = dynamic_cast(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(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(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(val) ) { return zeek::make_intrusive(ptr->GetHandle()->Sum()); } else { zeek::reporter->Error("Telemetry::histogram_sum: invalid handle."); return make_intrusive(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)); %}