Page MenuHomePhorge

No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/prometheus/metric.ex b/lib/prometheus/metric.ex
index 448859f..b96aa61 100644
--- a/lib/prometheus/metric.ex
+++ b/lib/prometheus/metric.ex
@@ -1,178 +1,195 @@
defmodule Prometheus.Metric do
@moduledoc """
Prometheus metrics shortcuts.
Aliases and requires respective metric modules so they are
accessible without `Prometheus.Metric` prefix.
Allows to automatically setup metrics with
`@<type>` attributes. Metrics will be declared in
the `@on_load` callback. If the module already
has `@on_load` callback, metrics will be declared
iff the callback returns `:ok`.
Example:
iex(1)> defmodule MyCoolModule do
...(1)> use Prometheus.Metric
...(1)>
...(1)> @counter name: :test_counter3, labels: [], help: "qwe"
...(1)> end
iex(2)> require Prometheus.Metric.Counter
Prometheus.Metric.Counter
iex(3)> Prometheus.Metric.Counter.value(:test_counter3)
0
"""
@metrics [:counter, :gauge, :boolean, :summary, :histogram]
defmacro __using__(_opts) do
module_name = __CALLER__.module
quote do
# credo:disable-for-next-line Credo.Check.Readability.SpaceAfterCommas
alias Prometheus.Metric.{Counter, Gauge, Histogram, Summary, Boolean}
# credo:disable-for-next-line Credo.Check.Readability.SpaceAfterCommas
require Prometheus.Metric.{Counter, Gauge, Histogram, Summary, Boolean}
require Prometheus.Error
unquote_splicing(
for metric <- @metrics do
quote do
Module.register_attribute(
unquote(module_name),
unquote(metric),
accumulate: true
)
end
end
)
@before_compile unquote(__MODULE__)
end
end
defmacro __before_compile__(env) do
+ mod = env.module
+
+ declarations =
+ for metric <- @metrics, params <- Module.delete_attribute(mod, metric) do
+ {metric, params}
+ end
+
+ declare_metrics = Enum.map(declarations, &emit_create_metric/1)
+
+ default_metrics = Enum.map(declarations, &emit_metric_tuple/1)
+
quote do
def __declare_prometheus_metrics__() do
if List.keymember?(Application.started_applications(), :prometheus, 0) do
- (unquote_splicing(
- for metric <- @metrics do
- declarations = Module.get_attribute(env.module, metric)
- Module.delete_attribute(env.module, metric)
-
- quote do
- unquote_splicing(
- for params <- declarations do
- emit_create_metric(metric, params)
- end
- )
-
- :ok
- end
- end
- ))
+ unquote_splicing(declare_metrics)
+ :ok
else
+ existing_metrics = Application.get_env(:prometheus, :default_metrics, [])
+
+ defined_metrics = unquote(default_metrics)
+
+ Application.put_env(
+ :prometheus,
+ :default_metrics,
+ defined_metrics ++ existing_metrics
+ )
+
:ok
end
end
unquote(gen_on_load(env))
end
end
defp gen_on_load(env) do
case get_on_load_attribute(env.module) do
nil ->
quote do
@on_load :__declare_prometheus_metrics__
end
on_load ->
Module.delete_attribute(env.module, :on_load)
Module.put_attribute(env.module, :on_load, :__prometheus_on_load_override__)
quote do
def __prometheus_on_load_override__() do
case unquote(on_load)() do
:ok -> __declare_prometheus_metrics__()
result -> result
end
end
end
end
end
defp get_on_load_attribute(module) do
case Module.get_attribute(module, :on_load) do
[] ->
nil
nil ->
nil
atom when is_atom(atom) ->
atom
{atom, 0} when is_atom(atom) ->
atom
[{atom, 0}] when is_atom(atom) ->
atom
other ->
raise ArgumentError,
"expected the @on_load attribute to be an atom or a " <>
"{atom, 0} tuple, got: #{inspect(other)}"
end
end
+ defp emit_metric_tuple({metric, params}) do
+ quote do
+ {unquote(metric), unquote(params)}
+ end
+ end
+
+ defp emit_create_metric({metric, params}) do
+ emit_create_metric(metric, params)
+ end
+
defp emit_create_metric(:counter, params) do
quote do
Prometheus.Metric.Counter.declare(unquote(params))
end
end
defp emit_create_metric(:gauge, params) do
quote do
Prometheus.Metric.Gauge.declare(unquote(params))
end
end
defp emit_create_metric(:boolean, params) do
quote do
Prometheus.Metric.Boolean.declare(unquote(params))
end
end
defp emit_create_metric(:summary, params) do
quote do
Prometheus.Metric.Summary.declare(unquote(params))
end
end
defp emit_create_metric(:histogram, params) do
quote do
Prometheus.Metric.Histogram.declare(unquote(params))
end
end
defmacro ct_parsable_spec?(spec) do
quote do
is_list(unquote(spec)) or is_atom(unquote(spec))
end
end
def parse_spec(spec) when is_list(spec) do
registry = Keyword.get(spec, :registry, :default)
name = Keyword.fetch!(spec, :name)
labels = Keyword.get(spec, :labels, [])
{registry, name, labels}
end
def parse_spec(spec) when is_atom(spec) do
{:default, spec, []}
end
end
diff --git a/test/metric_test.exs b/test/metric_test.exs
index ef0b410..be5fa98 100644
--- a/test/metric_test.exs
+++ b/test/metric_test.exs
@@ -1,81 +1,103 @@
defmodule Prometheus.MetricTest do
use Prometheus.Case
doctest Prometheus.Metric
defmacro __before_compile__(_env) do
quote do
def injected_fun() do
1
end
end
end
test "@metric attributes leads to metric declaration" do
defmodule ModuleWithMetrics do
use Prometheus.Metric
@before_compile Prometheus.MetricTest
@counter name: :test_counter1, labels: [], help: "qwe"
@counter name: :test_counter2, labels: [:tag], help: "qwa"
@gauge name: :test_gauge1, labels: [], help: "qwe"
@gauge name: :test_gauge2, labels: [:tag], help: "qwa"
@boolean name: :test_boolean1, labels: [], help: "qwe"
@boolean name: :test_boolean2, labels: [:tag], help: "qwa"
@summary name: :test_summary1, labels: [], help: "qwe"
@summary name: :test_summary2, labels: [:tag], help: "qwa"
@histogram name: :test_histogram1, labels: [], buckets: [1, 2], help: "qwe"
@histogram name: :test_histogram2, labels: [:tag], buckets: [1, 2], help: "qwa"
@on_load :custom_on_load_fun
def custom_on_load_fun() do
Counter.declare(name: :custom_counter, labels: [], help: "qwe")
:ok
end
end
defmodule ModuleWithoutOnLoad do
use Prometheus.Metric
@counter name: :test_counter3, labels: [], help: "qwe"
end
assert 1 == ModuleWithMetrics.injected_fun()
assert false == Counter.declare(name: :custom_counter, labels: [], help: "qwe")
assert false == Counter.declare(name: :test_counter3, labels: [], help: "qwe")
assert false == Counter.declare(name: :test_counter1, labels: [], help: "qwe")
assert false == Counter.declare(name: :test_counter2, labels: [:tag], help: "qwa")
assert false == Gauge.declare(name: :test_gauge1, labels: [], help: "qwe")
assert false == Gauge.declare(name: :test_gauge2, labels: [:tag], help: "qwa")
assert false == Boolean.declare(name: :test_boolean1, labels: [], help: "qwe")
assert false == Boolean.declare(name: :test_boolean2, labels: [:tag], help: "qwa")
assert false == Summary.declare(name: :test_summary1, labels: [], help: "qwe")
assert false == Summary.declare(name: :test_summary2, labels: [:tag], help: "qwa")
assert false ==
Histogram.declare(
name: :test_histogram1,
labels: [],
buckets: [1, 2],
help: ""
)
assert false ==
Histogram.declare(
name: :test_histogram2,
labels: [:tag],
buckets: [1, 2],
help: ""
)
end
+
+ test "@metric attributes loaded before prometheus app leads to metric declaration" do
+ _ = Application.stop(:prometheus_ex)
+ _ = Application.stop(:prometheus)
+ Application.put_env(:prometheus, :default_metrics, [])
+
+ defmodule ModuleLoadedBeforeStart do
+ use Prometheus.Metric
+
+ @counter name: :test_counter4, labels: [], help: "qwe"
+ @gauge name: :test_gauge3, help: "qwe"
+ end
+
+ assert [
+ counter: [name: :test_counter4, labels: [], help: "qwe"],
+ gauge: [name: :test_gauge3, help: "qwe"]
+ ] == Application.get_env(:prometheus, :default_metrics)
+
+ _ = Application.ensure_all_started(:prometheus_ex)
+ assert false == Counter.declare(name: :test_counter4, labels: [], help: "qwe")
+ assert false == Gauge.declare(name: :test_gauge3, help: "qwe")
+ end
end

File Metadata

Mime Type
text/x-diff
Expires
Wed, Nov 27, 7:25 AM (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40576
Default Alt Text
(9 KB)

Event Timeline