Skip to content

Commit f23df2d

Browse files
committed
break: Improve startup time of the prometheus application
This means that by default prometheus won't try to find all loaded modules implementing a behaviour, as this can be a very expensive operation (taking many seconds as has been seen in the field). If such behaviour was desired, it'd require explicitly setting `all_loaded` for instrumeters and collectors. The default is the most sane behaviour.
1 parent 02376b5 commit f23df2d

File tree

6 files changed

+61
-62
lines changed

6 files changed

+61
-62
lines changed

README.md

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,12 @@ Histogram also accepts `buckets` option. Please refer to respective modules docs
182182

183183
### Advanced
184184

185-
You will need these modules only if you're writing custom collector for app/lib that can't be instrumented directly.
185+
You will need these modules only if you're writing a custom collector for an app/lib that can't be instrumented directly.
186186

187187
- [`prometheus_collector`](https://github.com/deadtrickster/prometheus.erl/blob/master/doc/prometheus_collector.md) - common interface for collectors;
188188
- [`prometheus_format`](https://github.com/deadtrickster/prometheus.erl/blob/master/doc/prometheus_format.md) - common interface for exposition formats;
189189
- [`prometheus_model_helpers`](https://github.com/deadtrickster/prometheus.erl/blob/master/doc/prometheus_model_helpers.md) - provides API for working with underlying Prometheus models.
190-
You'll use that if you want to create custom collector.
190+
You'll use that if you want to create a custom collector.
191191

192192
## Build
193193

@@ -198,29 +198,27 @@ You'll use that if you want to create custom collector.
198198
## Configuration
199199

200200
Prometheus.erl supports standard Erlang app configuration.
201-
- `collectors` - List of custom collectors modules to be registered automatically. If undefined list of all modules implementing `prometheus_collector` behaviour will be used.
202-
- `default_metrics` - List of metrics to be registered during app startup. Metric format: `{Type, Spec}` where `Type` is a metric type (counter, gauge, etc), `Spec` is a list to be passed to `Metric:declare/1`. Deprecated format `{Registry, Metric, Spec}` also supported.
203-
204-
Collectors config also supports "alias" option `default`. When used these collectors will be registered:
205-
<pre>
206-
prometheus_boolean,
207-
prometheus_counter,
208-
prometheus_gauge,
209-
prometheus_histogram,
210-
prometheus_mnesia_collector,
211-
prometheus_summary,
212-
prometheus_vm_memory_collector,
213-
prometheus_vm_statistics_collector,
214-
prometheus_vm_system_info_collector
215-
</pre>
201+
- `collectors` - List of custom collectors modules to be registered automatically.
202+
Can be `all_loaded` in order to find all modules implementing the `prometheus_collector` behaviour.
203+
Supports an "alias" option `default`, which will append all default collectors implemented in this library.
204+
If undefined, the default collectors implemented in this library will be used.
205+
- `instrumenters` - List of custom instrumenter modules to be registered automatically.
206+
Can be `all_loaded` in order to find all modules implementing the `prometheus_instrumenter` behaviour.
207+
If undefined, none will be loaded.
208+
- `default_metrics` - List of metrics to be registered during app startup.
209+
Metric format: `{Type, Spec}` where `Type` is a metric type (counter, gauge, etc),
210+
`Spec` is a list to be passed to `Metric:declare/1`.
211+
Deprecated format `{Registry, Metric, Spec}` also supported.
212+
216213
## Collectors & Exporters Conventions
217214

218215
### Configuration
219216

220217
All 3d-party libraries should be configured via `prometheus` app env.
221218

222-
Exproters are responsible for maintianing scrape endpoint.
223-
Exporters usually tightly coupled with web server and are singletons. They should understand these keys:
219+
Exporters are responsible for maintaining scrape endpoint.
220+
Exporters are usually tightly coupled with the web server and are singletons.
221+
They should understand these keys:
224222
- `path` - url for scraping;
225223
- `format` - scrape format as module name i.e. `prometheus_text_format` or `prometheus_protobuf_format`.
226224
Exporter-specific options should be under `<exporter_name>_exporter` for erlang or `<Exporter_name>Exporter` for Elixir i.e. `PlugsExporter` or `elli_exporter`

src/prometheus_collector.erl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ Called when collector is deregistered. If collector is stateful you can put clea
128128
enabled_collectors() ->
129129
lists:usort(
130130
case application:get_env(prometheus, collectors) of
131-
undefined -> all_known_collectors();
131+
undefined -> ?DEFAULT_COLLECTORS;
132+
{ok, all_loaded} -> all_known_collectors();
132133
{ok, Collectors} -> catch_default_collectors(Collectors)
133134
end
134135
).

src/prometheus_instrumenter.erl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
-spec enabled_instrumenters() -> [instrumenter()].
2020
enabled_instrumenters() ->
2121
case application:get_env(prometheus, instrumenters) of
22-
undefined -> all_known_instrumenters();
22+
undefined -> [];
23+
{ok, all_loaded} -> all_known_instrumenters();
2324
{ok, Instrumenters} -> Instrumenters
2425
end.
2526

src/prometheus_misc.erl

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,41 +20,30 @@
2020
%% Retrieves a list of modules that implement a specified behaviour.
2121
-spec behaviour_modules(Behaviour :: atom()) -> [module()].
2222
behaviour_modules(Behaviour) ->
23-
[
24-
Module
25-
|| {Module, Behaviours} <-
26-
all_module_attributes(behaviour),
27-
lists:member(Behaviour, Behaviours)
28-
].
23+
Applications = application:loaded_applications(),
24+
Modules = lists:flatmap(fun get_modules_for_app/1, Applications),
25+
Targets = lists:usort(Modules),
26+
extract_behaviour_from_modules(Targets, Behaviour).
2927

30-
all_module_attributes(Name) ->
31-
Targets =
32-
lists:usort(
33-
lists:append(
34-
[
35-
[{App, Module} || Module <- Modules]
36-
|| {App, _, _} <- application:loaded_applications(),
37-
{ok, Modules} <- [application:get_key(App, modules)]
38-
]
39-
)
40-
),
41-
lists:foldl(
42-
fun({_App, Module}, Acc) ->
43-
case
44-
lists:append([
45-
Atts
46-
|| {N, Atts} <- module_attributes(Module),
47-
N =:= Name
48-
])
49-
of
50-
[] -> Acc;
51-
Atts -> [{Module, Atts} | Acc]
52-
end
53-
end,
54-
[],
55-
Targets
56-
).
28+
-spec get_modules_for_app({atom(), string(), string()}) -> [module()].
29+
get_modules_for_app({App, _, _}) ->
30+
case application:get_key(App, modules) of
31+
{ok, Modules} -> Modules;
32+
_ -> []
33+
end.
34+
35+
-spec extract_behaviour_from_modules([module()], Behaviour :: atom()) -> [module()].
36+
extract_behaviour_from_modules(Modules, Behaviour) ->
37+
Filter = fun(Module) -> does_module_implement_behaviour(Module, Behaviour) end,
38+
lists:filter(Filter, Modules).
39+
40+
-spec does_module_implement_behaviour(Module :: module(), Behaviour :: atom()) -> boolean().
41+
does_module_implement_behaviour(Module, Behaviour) ->
42+
Attributes = module_attributes(Module),
43+
Behaviours = [Atts || {N, Atts} <- Attributes, (N =:= behaviour orelse N =:= behavior)],
44+
lists:member(Behaviour, lists:flatten(Behaviours)).
5745

46+
-spec module_attributes(atom()) -> [{atom(), any()}] | [].
5847
module_attributes(Module) ->
5948
try
6049
Module:module_info(attributes)

test/eunit/prometheus_collector_tests.erl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,20 @@
1919

2020
collector_setup_test() ->
2121
prometheus:start(),
22-
application:set_env(prometheus, collectors, [qwe]),
2322
try
23+
application:set_env(prometheus, collectors, all_loaded),
24+
?assertMatch(?DEFAULT_COLLECTORS, prometheus_collector:enabled_collectors())
25+
after
26+
application:unset_env(prometheus, collectors)
27+
end,
28+
try
29+
application:set_env(prometheus, collectors, [qwe]),
2430
?assertMatch([qwe], prometheus_collector:enabled_collectors())
2531
after
2632
application:unset_env(prometheus, collectors)
2733
end,
28-
application:set_env(prometheus, collectors, [qwe, default]),
2934
try
35+
application:set_env(prometheus, collectors, [qwe, default]),
3036
C1 = ?DEFAULT_COLLECTORS ++ [qwe],
3137
?assertMatch(C1, prometheus_collector:enabled_collectors())
3238
after

test/eunit/prometheus_instrumenter_tests.erl

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@
44

55
instrumenter_setup_test() ->
66
prometheus:start(),
7-
?assertNotMatch(undefined, ets:info(prometheus_instrumenter_tests)),
8-
application:set_env(prometheus, instrumenters, [qwe]),
7+
?assertMatch(undefined, ets:info(prometheus_instrumenter_tests)),
98
try
9+
application:set_env(prometheus, instrumenters, [qwe]),
1010
?assertMatch([qwe], prometheus_instrumenter:enabled_instrumenters())
1111
after
1212
application:unset_env(prometheus, instrumenters)
1313
end,
14-
?assertMatch(
15-
[prometheus_test_instrumenter],
16-
prometheus_instrumenter:enabled_instrumenters()
17-
).
14+
try
15+
application:set_env(prometheus, instrumenters, all_loaded),
16+
Expected = [prometheus_test_instrumenter],
17+
?assertMatch(Expected, prometheus_instrumenter:enabled_instrumenters())
18+
after
19+
application:unset_env(prometheus, instrumenters)
20+
end,
21+
?assertMatch([], prometheus_instrumenter:enabled_instrumenters()).

0 commit comments

Comments
 (0)