Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F115101
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Nov 27, 7:25 AM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40576
Default Alt Text
(9 KB)
Attached To
Mode
R26 prometheus.ex
Attached
Detach File
Event Timeline
Log In to Comment