From dc8a6b26593d9210f3c9412f8d980a61ee29a581 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 17 Nov 2023 16:45:53 +0100 Subject: [PATCH] Family{C}: generalize constructor method This patch changes how new child collectors are constructed. Previously only `metric_name`, `help`, and `registry=nothing` were passed to the constructor `C`. With this patch any `args...` and `; kwargs...` not consumed by the `Family{C}` constructor are passed along as well. In order to make this efficient a constructor closure is created in the `Family{C}` constructor and stored in the `Family` struct. This change is in preparation for the Histogram collector where more arguments is needed to construct the children. It also make it easier to later add the option to explicitly pass a constructor, or to specialize the closure on the specific collector type `C`. --- src/Prometheus.jl | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/Prometheus.jl b/src/Prometheus.jl index be27bf9..e6db058 100644 --- a/src/Prometheus.jl +++ b/src/Prometheus.jl @@ -600,23 +600,35 @@ function Base.:(==)(l1::LabelValues, l2::LabelValues) return l1.label_values == l2.label_values end -struct Family{C, N} <: Collector +struct Family{C, N, F} <: Collector metric_name::String help::String label_names::LabelNames{N} children::Dict{LabelValues{N}, C} lock::ReentrantLock + constructor::F function Family{C}( - metric_name::String, help::String, label_names; - registry::Union{CollectorRegistry, Nothing}=DEFAULT_REGISTRY, + metric_name::String, help::String, args_first, args_tail...; + registry::Union{CollectorRegistry, Nothing}=DEFAULT_REGISTRY, kwargs..., ) where {C} + # Support ... on non-final argument + args_all = (args_first, args_tail...,) + label_names = last(args_all) + args = Base.front(args_all) + @assert(isempty(args)) + # TODO: Perhaps extract this into + # make_constructor(::Type{Collector}, metric_name, help, args...; kwargs...) + # so that some Collectors (like Counter) can skip the closure over args and kwargs. + function constructor() + return C(metric_name, help, args...; kwargs..., registry=nothing)::C + end labels = LabelNames(label_names) N = length(labels.label_names) children = Dict{LabelValues{N}, C}() lock = ReentrantLock() - family = new{C, N}( - verify_metric_name(metric_name), help, labels, children, lock, + family = new{C, N, typeof(constructor)}( + verify_metric_name(metric_name), help, labels, children, lock, constructor, ) if registry !== nothing register(registry, family) @@ -626,14 +638,17 @@ struct Family{C, N} <: Collector end """ - Prometheus.Family{C}(name, help, label_names; registry=DEFAULT_REGISTRY) + Prometheus.Family{C}(name, help, args..., label_names; registry=DEFAULT_REGISTRY, kwargs...) Create a labeled collector family with labels given by `label_names`. For every new set of -label values encountered a new collector of type `C <: Collector` will be created. +label values encountered a new collector of type `C <: Collector` will be created, see +[`Prometheus.labels`](@ref). **Arguments** - `name :: String`: the name of the family metric. - `help :: String`: the documentation for the family metric. + - `args...`: any extra positional arguments required for `C`s constructor, see + [`Prometheus.labels`](@ref). - `label_names`: the label names for the family. Label names can be given as either of the following (typically matching the methods label values will be given later, see [`Prometheus.labels`](@ref)): @@ -654,9 +669,11 @@ label values encountered a new collector of type `C <: Collector` will be create - `registry :: Prometheus.CollectorRegistry`: the registry in which to register the collector. If not specified the default registry is used. Pass `registry = nothing` to skip registration. + - `kwargs...`: any extra keyword arguments required for `C`s constructor, see + [`Prometheus.labels`](@ref). **Methods** - - [`Prometheus.labels`](@ref): return the collector for a specific set of labels. + - [`Prometheus.labels`](@ref): get or create the collector for a specific set of labels. - [`Prometheus.remove`](@ref): remove the collector for a specific set of labels. - [`Prometheus.clear`](@ref): remove all collectors in the family. @@ -680,8 +697,11 @@ end """ Prometheus.labels(family::Family{C}, label_values) where C -Return the collector of type `C` from the family corresponding to the labels given by -`label_values`. +Get or create the collector of type `C` from the family corresponding to the labels given by +`label_values`. If no collector exist for the input labels a new one is created by invoking +the `C` constructor as `C(name, help, args...; kwargs..., registry=nothing)`, where `name`, +`help`, `args...`, and `kwargs...` are the arguments from the family constructor, see +[`Family`](@ref). Similarly to when creating the [`Family`](@ref), `label_values` can be given as either of the following: @@ -701,8 +721,7 @@ All non-string values (e.g. `200` in the examples above) are stringified using ` function labels(family::Family{C, N}, label_values) where {C, N} labels = make_label_values(family.label_names, label_values)::LabelValues{N} collector = @lock family.lock get!(family.children, labels) do - # TODO: Avoid the re-verification of the metric name? - C(family.metric_name, family.help; registry=nothing) + family.constructor()::C end return collector end