Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,36 @@ Common options are:

Histogram also accepts `buckets` option. Please refer to respective modules docs for the more information.

#### Default series

Metrics with no labels automatically get a zero-value series created at declaration time.
Metrics **with** labels do **not** create any series automatically — a series only appears after the
first write (e.g. `inc`, `observe`, `set`).

If you need a labeled series to be present in the output before the first write, call
`prometheus_metric:set_default(MetricModule, Registry, Name, LabelValues)` explicitly:

```erlang
%% Declare a labeled counter
prometheus_counter:declare([{name, http_requests_total}, {labels, [method]}, {help, ""}]),
%% Pre-seed specific label combinations so they appear with value 0 immediately
prometheus_metric:set_default(prometheus_counter, default, http_requests_total, [get]),
prometheus_metric:set_default(prometheus_counter, default, http_requests_total, [post]).
```

If you are using the default registry, you can use the shorter
`prometheus_metric:set_default(MetricModule, Name, LabelValues)` form:

```erlang
%% Equivalent to the previous example, but defaults Registry to `default`
prometheus_metric:set_default(prometheus_counter, http_requests_total, [get]),
prometheus_metric:set_default(prometheus_counter, http_requests_total, [post]).
```

Both `set_default/3` and `set_default/4` are idempotent: calling them again for an already-created series is a no-op.
It raises `{unknown_metric, Registry, Name}` if the metric has not been declared, and
`{invalid_metric_arity, Present, Expected}` if the label value count does not match.

### Exposition Formats

- [`prometheus_text_format`](https://github.com/deadtrickster/prometheus.erl/blob/master/doc/prometheus_text_format.md) - renders metrics for a given registry (default is `default`) in text format;
Expand Down
22 changes: 17 additions & 5 deletions src/metrics/prometheus_boolean.erl
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fuse_event(Fuse, Event) ->
declare/1,
deregister/1,
deregister/2,
set_default/2,
set_default/3,
set/2,
set/3,
set/4,
Expand Down Expand Up @@ -127,10 +127,22 @@ deregister(Registry, Name) ->
NumDeleted = ets:select_delete(?TABLE, deregister_select(Registry, Name)),
{MFR, NumDeleted > 0}.

?DOC(false).
-spec set_default(prometheus_registry:registry(), prometheus_metric:name()) -> ok.
set_default(Registry, Name) ->
set(Registry, Name, [], undefined).
?DOC("""
Pre-seeds a boolean series for `Registry`, `Name` and `LabelValues` with the value `undefined`,
if the series does not yet exist.

Raises:

* `{unknown_metric, Registry, Name}` error if boolean with name `Name` can't be found in `Registry`.
* `{invalid_metric_arity, Present, Expected}` error if labels count mismatch.
""").
-spec set_default(
Registry :: prometheus_registry:registry(),
Name :: prometheus_metric:name(),
LabelValues :: prometheus_metric:label_values()
) -> ok.
set_default(Registry, Name, LabelValues) ->
set(Registry, Name, LabelValues, undefined).

?DOC(#{equiv => set(default, Name, [], Value)}).
-spec set(prometheus_metric:name(), prometheus:prometheus_boolean()) -> ok.
Expand Down
25 changes: 20 additions & 5 deletions src/metrics/prometheus_counter.erl
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ inc(Caller) ->
declare/1,
deregister/1,
deregister/2,
set_default/2,
set_default/3,
inc/1,
inc/2,
inc/3,
Expand Down Expand Up @@ -146,10 +146,25 @@ deregister(Registry, Name) ->
NumDeleted = ets:select_delete(?TABLE, deregister_select(Registry, Name)),
{MFR, NumDeleted > 0}.

?DOC(false).
-spec set_default(prometheus_registry:registry(), prometheus_metric:name()) -> boolean().
set_default(Registry, Name) ->
ets:insert_new(?TABLE, {key(Registry, Name, []), 0, 0}).
?DOC("""
Pre-seeds a counter series for `Registry`, `Name` and `LabelValues` with an initial value of 0,
if the series does not yet exist.

Useful for ensuring a labeled series is present in output before any increments happen.

Raises:

* `{unknown_metric, Registry, Name}` error if counter with name `Name` can't be found in `Registry`.
* `{invalid_metric_arity, Present, Expected}` error if labels count mismatch.
""").
-spec set_default(
Registry :: prometheus_registry:registry(),
Name :: prometheus_metric:name(),
LabelValues :: prometheus_metric:label_values()
) -> boolean().
set_default(Registry, Name, LabelValues) ->
prometheus_metric:check_mf_exists(?TABLE, Registry, Name, LabelValues),
ets:insert_new(?TABLE, {key(Registry, Name, LabelValues), 0, 0}).

?DOC(#{equiv => inc(default, Name, [], 1)}).
-spec inc(prometheus_metric:name()) -> ok.
Expand Down
25 changes: 20 additions & 5 deletions src/metrics/prometheus_gauge.erl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ track_checked_out_sockets(CheckoutFun) ->
declare/1,
deregister/1,
deregister/2,
set_default/2,
set_default/3,
set/2,
set/3,
set/4,
Expand Down Expand Up @@ -161,10 +161,25 @@ deregister(Registry, Name) ->
NumDeleted = ets:select_delete(?TABLE, deregister_select(Registry, Name)),
{MFR, NumDeleted > 0}.

?DOC(false).
-spec set_default(prometheus_registry:registry(), prometheus_metric:name()) -> boolean().
set_default(Registry, Name) ->
ets:insert_new(?TABLE, {{Registry, Name, []}, 0, 0}).
?DOC("""
Pre-seeds a gauge series for `Registry`, `Name` and `LabelValues` with an initial value of 0,
if the series does not yet exist.

Useful for ensuring a labeled series is present in output before any updates happen.

Raises:

* `{unknown_metric, Registry, Name}` error if gauge with name `Name` can't be found in `Registry`.
* `{invalid_metric_arity, Present, Expected}` error if labels count mismatch.
""").
-spec set_default(
Registry :: prometheus_registry:registry(),
Name :: prometheus_metric:name(),
LabelValues :: prometheus_metric:label_values()
) -> boolean().
set_default(Registry, Name, LabelValues) ->
prometheus_metric:check_mf_exists(?TABLE, Registry, Name, LabelValues),
ets:insert_new(?TABLE, {{Registry, Name, LabelValues}, 0, 0}).

?DOC(#{equiv => set(default, Name, [], Value)}).
-spec set(prometheus_metric:name(), number()) -> ok.
Expand Down
24 changes: 19 additions & 5 deletions src/metrics/prometheus_histogram.erl
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ the number of time ticks when the recent value was observed).
declare/1,
deregister/1,
deregister/2,
set_default/2,
set_default/3,
observe/2,
observe/3,
observe/4,
Expand Down Expand Up @@ -178,10 +178,24 @@ deregister(Registry, Name) ->
_:_ -> {false, false}
end.

?DOC(false).
-spec set_default(prometheus_registry:registry(), prometheus_metric:name()) -> boolean().
set_default(Registry, Name) ->
insert_placeholders(Registry, Name, []).
?DOC("""
Pre-seeds a histogram series for `Registry`, `Name` and `LabelValues` with zero-count buckets
and zero sum, if the series does not yet exist.

Useful for ensuring a labeled series is present in output before any observations happen.

Raises:

* `{unknown_metric, Registry, Name}` error if histogram with name `Name` can't be found in `Registry`.
* `{invalid_metric_arity, Present, Expected}` error if labels count mismatch.
""").
-spec set_default(
Registry :: prometheus_registry:registry(),
Name :: prometheus_metric:name(),
LabelValues :: prometheus_metric:label_values()
) -> boolean().
set_default(Registry, Name, LabelValues) ->
insert_placeholders(Registry, Name, LabelValues).

?DOC(#{equiv => observe(default, Name, [], Value)}).
-spec observe(prometheus_metric:name(), number()) -> ok.
Expand Down
28 changes: 20 additions & 8 deletions src/metrics/prometheus_quantile_summary.erl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ It takes `error` and `bound` as in `t:ddskerl_ets:opts/0`.
-export([
new/1,
declare/1,
set_default/2,
set_default/3,
deregister/1,
deregister/2,
observe/2,
Expand Down Expand Up @@ -122,11 +122,23 @@ declare(Spec) ->
Spec1 = validate_summary_spec(Spec),
prometheus_metric:insert_mf(?TABLE, ?MODULE, Spec1).

?DOC(false).
-spec set_default(prometheus_registry:registry(), prometheus_metric:name()) -> boolean().
set_default(Registry, Name) ->
#{error := Error, bound := Bound} = get_configuration(Registry, Name),
Key = key(Registry, Name, []),
?DOC("""
Pre-seeds a quantile summary series for `Registry`, `Name` and `LabelValues` with an empty
initial state, if the series does not yet exist.

Raises:

* `{unknown_metric, Registry, Name}` error if summary with name `Name` can't be found in `Registry`.
* `{invalid_metric_arity, Present, Expected}` error if labels count mismatch.
""").
-spec set_default(
Registry :: prometheus_registry:registry(),
Name :: prometheus_metric:name(),
LabelValues :: prometheus_metric:label_values()
) -> boolean().
set_default(Registry, Name, LabelValues) ->
#{error := Error, bound := Bound} = get_configuration(Registry, Name, LabelValues),
Key = key(Registry, Name, LabelValues),
ddskerl_ets:new(?TABLE, Key, Error, Bound).

?DOC(#{equiv => deregister(default, Name)}).
Expand Down Expand Up @@ -472,8 +484,8 @@ insert_metric(Registry, Name, LabelValues, Key) ->
Configuration = Configuration0#{name => Key},
ddskerl_ets:new(Configuration).

get_configuration(Registry, Name) ->
MF = prometheus_metric:check_mf_exists(?TABLE, Registry, Name),
get_configuration(Registry, Name, LabelValues) ->
MF = prometheus_metric:check_mf_exists(?TABLE, Registry, Name, LabelValues),
prometheus_metric:mf_data(MF).

key(Registry, Name, LabelValues) ->
Expand Down
25 changes: 20 additions & 5 deletions src/metrics/prometheus_summary.erl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ observe_response(Size) ->
declare/1,
deregister/1,
deregister/2,
set_default/2,
set_default/3,
observe/2,
observe/3,
observe/4,
Expand Down Expand Up @@ -133,10 +133,25 @@ deregister(Registry, Name) ->
NumDeleted = ets:select_delete(?TABLE, deregister_select(Registry, Name)),
{MFR, NumDeleted > 0}.

?DOC(false).
-spec set_default(prometheus_registry:registry(), prometheus_metric:name()) -> boolean().
set_default(Registry, Name) ->
ets:insert_new(?TABLE, {key(Registry, Name, []), 0, 0, 0}).
?DOC("""
Pre-seeds a summary series for `Registry`, `Name` and `LabelValues` with zero count and zero sum,
if the series does not yet exist.

Useful for ensuring a labeled series is present in output before any observations happen.

Raises:

* `{unknown_metric, Registry, Name}` error if summary with name `Name` can't be found in `Registry`.
* `{invalid_metric_arity, Present, Expected}` error if labels count mismatch.
""").
-spec set_default(
Registry :: prometheus_registry:registry(),
Name :: prometheus_metric:name(),
LabelValues :: prometheus_metric:label_values()
) -> boolean().
set_default(Registry, Name, LabelValues) ->
prometheus_metric:check_mf_exists(?TABLE, Registry, Name, LabelValues),
ets:insert_new(?TABLE, {key(Registry, Name, LabelValues), 0, 0, 0}).

?DOC(#{equiv => observe(default, Name, [], Value)}).
-spec observe(prometheus_metric:name(), number()) -> ok.
Expand Down
27 changes: 22 additions & 5 deletions src/prometheus_metric.erl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ as well as handling metric labels and data.
-export([
insert_new_mf/3,
insert_mf/3,
set_default/3,
set_default/4,
deregister_mf/2,
deregister_mf/3,
check_mf_exists/3,
Expand Down Expand Up @@ -85,10 +87,11 @@ as well as handling metric labels and data.
?DOC("Inserts a new metric function into the table.").
-callback declare(Spec :: spec()) -> boolean().

?DOC("Sets the default metric function for the module.").
-callback set_default(Registry, Name) -> dynamic() when
Registry :: prometheus_registry:registry(),
Name :: name().
?DOC("Sets the default (zero/initial) state for the metric series identified by Registry, Name and LabelValues.").
-callback set_default(Registry :: prometheus_registry:registry(),
Name :: name(),
LabelValues :: label_values()) ->
dynamic().

?DOC("Removes a metric function by name.").
-callback remove(Name :: name()) -> boolean() | no_return().
Expand Down Expand Up @@ -159,6 +162,20 @@ insert_mf(Table, Module, Spec) ->
false
end.

?DOC("Calls Module:set_default(default, Name, LabelValues). ").
-spec set_default(Module :: module(), Name :: name(), LabelValues :: label_values()) -> dynamic().
set_default(Module, Name, LabelValues) ->
set_default(Module, default, Name, LabelValues).

?DOC("Calls Module:set_default(Registry, Name, LabelValues). ").
-spec set_default(Module :: module(),
Registry :: prometheus_registry:registry(),
Name :: name(),
LabelValues :: label_values()) ->
dynamic().
set_default(Module, Registry, Name, LabelValues) ->
Module:set_default(Registry, Name, LabelValues).

?DOC(false).
-spec deregister_mf(Table, Registry) -> boolean() | no_return() when
Table :: atom(),
Expand Down Expand Up @@ -264,7 +281,7 @@ normalize_mf_row([Name, {Labels, Help}, C, D, Data]) ->
Name :: name(),
Labels :: list().
maybe_set_default(Module, Registry, Name, []) ->
Module:set_default(Registry, Name);
set_default(Module, Registry, Name, []);
maybe_set_default(_, _, _, _) ->
ok.

Expand Down
28 changes: 27 additions & 1 deletion test/eunit/metric/prometheus_boolean_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ prometheus_format_test_() ->
fun test_collector1/1,
fun test_collector2/1,
fun test_collector3/1,
fun test_values/1
fun test_values/1,
fun test_set_default_with_labels/1
]}.

test_registration(_) ->
Expand Down Expand Up @@ -227,6 +228,31 @@ test_values(_) ->
)
].

test_set_default_with_labels(_) ->
prometheus_boolean:new([
{name, fuse_state},
{labels, [name]},
{help, "Fuse state"}
]),
%% Before seeding: labeled series is undefined
Undef = prometheus_boolean:value(fuse_state, [myapp]),
%% Seed the labeled series
prometheus_boolean:set_default(default, fuse_state, [myapp]),
%% After seeding: labeled series returns undefined (boolean default)
Seeded = prometheus_boolean:value(fuse_state, [myapp]),
[
?_assertEqual(undefined, Undef),
?_assertEqual(undefined, Seeded),
?_assertError(
{unknown_metric, default, unknown_boolean},
prometheus_boolean:set_default(default, unknown_boolean, [myapp])
),
?_assertError(
{invalid_metric_arity, 2, 1},
prometheus_boolean:set_default(default, fuse_state, [myapp, extra])
)
].

test_collector1(_) ->
prometheus_boolean:new([
{name, simple_boolean},
Expand Down
Loading