Current section
5 Versions
Jump to
Current section
5 Versions
Compare versions
6
files changed
+159
additions
-94
deletions
| @@ -1,16 +1,78 @@ | |
| 1 1 | |
| 2 2 | |
| 3 | - # opencensus_erlang_prometheus # |
| 3 | + # Opencensus Prometheus integration for Erlang/BEAM # |
| 4 4 | |
| 5 5 | Copyright (c) 2018 Ilya Khaprov <<i.khaprov@gmail.com>>. |
| 6 6 | |
| 7 | - __Version:__ 0.0.1 |
| 7 | + __Version:__ 0.3.0 |
| 8 8 | |
| 9 9 | [![Hex.pm][Hex badge]][Hex link] |
| 10 10 | [![Hex.pm Downloads][Hex downloads badge]][Hex link] |
| 11 11 | [![Build Status][Travis badge]][Travis link] |
| 12 12 | [![Coverage Status][Coveralls badge]][Coveralls link] |
| 13 13 | |
| 14 | + > [Opencensus][Opencensus Erlang link]: Erlang stats collection and distributed tracing framework. |
| 15 | + |
| 16 | + > [Prometheus.io][Prometheus Erlang link]: monitoring system and time series database client in Erlang. |
| 17 | + |
| 18 | + ## Using |
| 19 | + |
| 20 | + Opencensus has trace and stats packages, trace package if for distributed tracing and |
| 21 | + stats package is for backend-agnostic metrics collection. |
| 22 | + |
| 23 | + This library implements a trace `reporter` for exporting spans duration as prometheus metrics and |
| 24 | + a stats `exporter` for exporting metrics collected using Opencensus. |
| 25 | + |
| 26 | + Example configuration: |
| 27 | + |
| 28 | + ```erlang |
| 29 | + |
| 30 | + {opencensus, [ |
| 31 | + {reporter, {oc_reporter_sequential, [ |
| 32 | + {oc_reporter_zipkin, [ |
| 33 | + {url, "http://localhost:9411"}, |
| 34 | + {service_name, "service_name"} |
| 35 | + ]}, |
| 36 | + {oc_prometheus_reporter, [{type, histogram}, %% metric type |
| 37 | + {name, span_histogram_seconds}, %% metric name, note the time unit |
| 38 | + {buckets, [0, 1, 2]}]} %% histogram buckets with bounds in the time unit |
| 39 | + ]}}, |
| 40 | + {sampler, {oc_sampler_probability, [ |
| 41 | + {probability, 0.001} %% one in a thousand |
| 42 | + ]}} |
| 43 | + ]} |
| 44 | + |
| 45 | + ``` |
| 46 | + |
| 47 | + Since Prometheus uses push model you don't need to set up Opencensus stats exporter as usual. |
| 48 | + Instead, this library provides `oc_stat_exporter_prometheus` which implements Prometheus collector interface. |
| 49 | + |
| 50 | + ```erlang |
| 51 | + |
| 52 | + prometheus_registry:register_collector(oc_stat_exporter_prometheus) |
| 53 | + |
| 54 | + ``` |
| 55 | + |
| 56 | + ## Other Prometheus and Opencensus Links |
| 57 | + |
| 58 | + ## Opencensus |
| 59 | + |
| 60 | + - [Cowboy 2 Integration](https://github.com/deadtrickster/opencensus-cowboy) |
| 61 | + - [Google Cloud Trace](https://github.com/tsloughter/oc_google_reporter) |
| 62 | + |
| 63 | + ### Prometheus |
| 64 | + |
| 65 | + - [Cowboy1/2 Exporters and Cowboy2 instrumenter](https://hex.pm/packages/prometheus_cowboy) |
| 66 | + - [Ecto Instrumenter](https://hex.pm/packages/prometheus_ecto) |
| 67 | + - [Elixir client](https://github.com/deadtrickster/prometheus.ex) |
| 68 | + - [Elixir plugs Instrumenters and Exporter](https://hex.pm/packages/prometheus_plugs) |
| 69 | + - [Extatus - App to report metrics to Prometheus from Elixir GenServers](https://github.com/gmtprime/extatus) |
| 70 | + - [Fuse plugin](https://github.com/jlouis/fuse#fuse_stats_prometheus) |
| 71 | + - [Inets HTTPD Exporter](https://github.com/deadtrickster/prometheus_httpd) |
| 72 | + - [OS process info Collector](https://hex.pm/packages/prometheus_process_collector) (linux, freebsd, macos) |
| 73 | + - [Phoenix Instrumenter](https://hex.pm/packages/prometheus_phoenix) |
| 74 | + - [RabbitMQ Exporter](https://github.com/deadtrickster/prometheus_rabbitmq_exporter). |
| 75 | + |
| 14 76 | ## Contributing |
| 15 77 | |
| 16 78 | Section order: |
| @@ -43,12 +105,14 @@ MIT | |
| 43 105 | [Travis link]: https://travis-ci.org/deadtrickster/opencensus_erlang_prometheus |
| 44 106 | [Coveralls badge]: https://coveralls.io/repos/github/deadtrickster/opencensus_erlang_prometheus/badge.svg?branch=master |
| 45 107 | [Coveralls link]: https://coveralls.io/github/deadtrickster/opencensus_erlang_prometheus?branch=master |
| 108 | + [Opencensus Erlang link]: https://github.com/census-instrumentation/opencensus-erlang |
| 109 | + [Prometheus Erlang link]: https://github.com/deadtrickster/prometheus.erl |
| 46 110 | |
| 47 111 | |
| 48 112 | ## Modules ## |
| 49 113 | |
| 50 114 | |
| 51 115 | <table width="100%" border="0" summary="list of modules"> |
| 52 | - <tr><td><a href="https://github.com/deadtrickster/opencensus-erlang-prometheus/blob/master/doc/oc_prometheus_reporter.md" class="module">oc_prometheus_reporter</a></td></tr> |
| 116 | + <tr><td><a href="https://github.com/deadtrickster/opencensus-erlang-prometheus/blob/master/doc/oc_reporter_prometheus.md" class="module">oc_reporter_prometheus</a></td></tr> |
| 53 117 | <tr><td><a href="https://github.com/deadtrickster/opencensus-erlang-prometheus/blob/master/doc/oc_stat_exporter_prometheus.md" class="module">oc_stat_exporter_prometheus</a></td></tr></table> |
| @@ -3,7 +3,7 @@ | |
| 3 3 | {<<"description">>,<<"opencensus_erlang_prometheus">>}. |
| 4 4 | {<<"elixir">>,<<"~> 1.4">>}. |
| 5 5 | {<<"files">>, |
| 6 | - [<<"src">>,<<"src/oc_prometheus_reporter.erl">>, |
| 6 | + [<<"src">>,<<"src/oc_reporter_prometheus.erl">>, |
| 7 7 | <<"src/oc_stat_exporter_prometheus.erl">>, |
| 8 8 | <<"src/opencensus_erlang_prometheus.app.src">>,<<"README.md">>, |
| 9 9 | <<"rebar.config">>]}. |
| @@ -24,4 +24,4 @@ | |
| 24 24 | {<<"optional">>,false}, |
| 25 25 | {<<"repository">>,<<"hexpm">>}, |
| 26 26 | {<<"requirement">>,<<"~> 0.3">>}]]}. |
| 27 | - {<<"version">>,<<"0.2.0">>}. |
| 27 | + {<<"version">>,<<"0.3.0">>}. |
| @@ -1,82 +0,0 @@ | |
| 1 | - -module(oc_prometheus_reporter). |
| 2 | - |
| 3 | - -include_lib("opencensus/include/opencensus.hrl"). |
| 4 | - |
| 5 | - -behaviour(oc_reporter). |
| 6 | - |
| 7 | - -export([init/1, |
| 8 | - report/2]). |
| 9 | - |
| 10 | - %% =================================================================== |
| 11 | - %% API |
| 12 | - %% =================================================================== |
| 13 | - |
| 14 | - init(Config) -> |
| 15 | - Type = config_metric_type(Config), |
| 16 | - Name = config_metric_name(Config), |
| 17 | - Labels = config_metric_labels(Config), |
| 18 | - {LabelNames, Attributes} = lists:unzip(Labels), |
| 19 | - Type:declare([{name, Name}, |
| 20 | - {labels, [name] ++ LabelNames}, |
| 21 | - {buckets, config_metric_buckets(Config)}, |
| 22 | - {help, "Opencensus span metric"}]), |
| 23 | - {Type, Name, Attributes}. |
| 24 | - |
| 25 | - report(Spans, Opts) -> |
| 26 | - [observe_span(Span, Opts) || Span <- Spans], |
| 27 | - ok. |
| 28 | - |
| 29 | - %% =================================================================== |
| 30 | - %% Private functions |
| 31 | - %% =================================================================== |
| 32 | - |
| 33 | - config_metric_type(Config) -> |
| 34 | - type_to_module(proplists:get_value(type, Config, summary)). |
| 35 | - |
| 36 | - config_metric_name(Config) -> |
| 37 | - proplists:get_value(name, Config, span). |
| 38 | - |
| 39 | - config_metric_buckets(Config) -> |
| 40 | - proplists:get_value(buckets, Config, default). |
| 41 | - |
| 42 | - config_metric_labels(Config) -> |
| 43 | - [normalize_label(Label) |
| 44 | - || Label <- proplists:get_value(labels, Config, [])]. |
| 45 | - |
| 46 | - normalize_label(Name) when is_atom(Name) -> |
| 47 | - NameStr = atom_to_binary(Name, utf8), |
| 48 | - {NameStr, NameStr}; |
| 49 | - normalize_label(Name) when is_binary(Name) -> |
| 50 | - {Name, Name}; |
| 51 | - normalize_label(Name) when is_list(Name) -> |
| 52 | - {Name, list_to_binary(Name)}; |
| 53 | - normalize_label({Label, Attribute}) when is_binary(Attribute) -> |
| 54 | - {Label, Attribute}. |
| 55 | - |
| 56 | - compute_labels(Span, Attributes) -> |
| 57 | - Map = Span#span.attributes, |
| 58 | - [maps:get(Attribute, Map, "N/A") || Attribute <- Attributes]. |
| 59 | - |
| 60 | - observe_span(Span, {MetricType, MetricName, Attributes}) -> |
| 61 | - Labels = [Span#span.name] ++ compute_labels(Span, Attributes), |
| 62 | - observe_span(MetricType, MetricName, Labels, Span). |
| 63 | - |
| 64 | - observe_span(prometheus_summary, MetricName, Labels, Span) -> |
| 65 | - prometheus_summary:observe(MetricName, Labels, span_duration(Span)); |
| 66 | - observe_span(prometheus_histogram, MetricName, Labels, Span) -> |
| 67 | - prometheus_histogram:observe(MetricName, Labels, span_duration(Span)); |
| 68 | - observe_span(prometheus_counter, MetricName, Labels, _Span) -> |
| 69 | - prometheus_counter:inc(MetricName, Labels). |
| 70 | - |
| 71 | - type_to_module(counter) -> |
| 72 | - prometheus_counter; |
| 73 | - type_to_module(summary) -> |
| 74 | - prometheus_summary; |
| 75 | - type_to_module(histogram) -> |
| 76 | - prometheus_histogram; |
| 77 | - type_to_module(Type) -> |
| 78 | - Type. |
| 79 | - |
| 80 | - -spec span_duration(opencensus:span()) -> integer(). |
| 81 | - span_duration(#span{start_time={StartTime,_}, end_time={EndTime,_}}) -> |
| 82 | - EndTime - StartTime. |
| @@ -0,0 +1,82 @@ | |
| 1 | + -module(oc_reporter_prometheus). |
| 2 | + |
| 3 | + -include_lib("opencensus/include/opencensus.hrl"). |
| 4 | + |
| 5 | + -behaviour(oc_reporter). |
| 6 | + |
| 7 | + -export([init/1, |
| 8 | + report/2]). |
| 9 | + |
| 10 | + %% =================================================================== |
| 11 | + %% API |
| 12 | + %% =================================================================== |
| 13 | + |
| 14 | + init(Config) -> |
| 15 | + Type = config_metric_type(Config), |
| 16 | + Name = config_metric_name(Config), |
| 17 | + Labels = config_metric_labels(Config), |
| 18 | + {LabelNames, Attributes} = lists:unzip(Labels), |
| 19 | + Type:declare([{name, Name}, |
| 20 | + {labels, [name] ++ LabelNames}, |
| 21 | + {buckets, config_metric_buckets(Config)}, |
| 22 | + {help, "Opencensus span metric"}]), |
| 23 | + {Type, Name, Attributes}. |
| 24 | + |
| 25 | + report(Spans, Opts) -> |
| 26 | + [observe_span(Span, Opts) || Span <- Spans], |
| 27 | + ok. |
| 28 | + |
| 29 | + %% =================================================================== |
| 30 | + %% Private functions |
| 31 | + %% =================================================================== |
| 32 | + |
| 33 | + config_metric_type(Config) -> |
| 34 | + type_to_module(proplists:get_value(type, Config, summary)). |
| 35 | + |
| 36 | + config_metric_name(Config) -> |
| 37 | + proplists:get_value(name, Config, span). |
| 38 | + |
| 39 | + config_metric_buckets(Config) -> |
| 40 | + proplists:get_value(buckets, Config, default). |
| 41 | + |
| 42 | + config_metric_labels(Config) -> |
| 43 | + [normalize_label(Label) |
| 44 | + || Label <- proplists:get_value(labels, Config, [])]. |
| 45 | + |
| 46 | + normalize_label(Name) when is_atom(Name) -> |
| 47 | + NameStr = atom_to_binary(Name, utf8), |
| 48 | + {NameStr, NameStr}; |
| 49 | + normalize_label(Name) when is_binary(Name) -> |
| 50 | + {Name, Name}; |
| 51 | + normalize_label(Name) when is_list(Name) -> |
| 52 | + {Name, list_to_binary(Name)}; |
| 53 | + normalize_label({Label, Attribute}) when is_binary(Attribute) -> |
| 54 | + {Label, Attribute}. |
| 55 | + |
| 56 | + compute_labels(Span, Attributes) -> |
| 57 | + Map = Span#span.attributes, |
| 58 | + [maps:get(Attribute, Map, "N/A") || Attribute <- Attributes]. |
| 59 | + |
| 60 | + observe_span(Span, {MetricType, MetricName, Attributes}) -> |
| 61 | + Labels = [Span#span.name] ++ compute_labels(Span, Attributes), |
| 62 | + observe_span(MetricType, MetricName, Labels, Span). |
| 63 | + |
| 64 | + observe_span(prometheus_summary, MetricName, Labels, Span) -> |
| 65 | + prometheus_summary:observe(MetricName, Labels, span_duration(Span)); |
| 66 | + observe_span(prometheus_histogram, MetricName, Labels, Span) -> |
| 67 | + prometheus_histogram:observe(MetricName, Labels, span_duration(Span)); |
| 68 | + observe_span(prometheus_counter, MetricName, Labels, _Span) -> |
| 69 | + prometheus_counter:inc(MetricName, Labels). |
| 70 | + |
| 71 | + type_to_module(counter) -> |
| 72 | + prometheus_counter; |
| 73 | + type_to_module(summary) -> |
| 74 | + prometheus_summary; |
| 75 | + type_to_module(histogram) -> |
| 76 | + prometheus_histogram; |
| 77 | + type_to_module(Type) -> |
| 78 | + Type. |
| 79 | + |
| 80 | + -spec span_duration(opencensus:span()) -> integer(). |
| 81 | + span_duration(#span{start_time={StartTime, _}, end_time={EndTime, _}}) -> |
| 82 | + EndTime - StartTime. |
| @@ -13,7 +13,8 @@ collect_mf(_Registry, Callback) -> | |
| 13 13 | deregister_cleanup(_Registry) -> |
| 14 14 | ok. |
| 15 15 | |
| 16 | - -spec view_data_to_mf(oc_stat_view:view_data()) -> prometheus_model:'MetricFamily'(). |
| 16 | + -spec view_data_to_mf(oc_stat_view:view_data()) -> |
| 17 | + prometheus_model:'MetricFamily'(). |
| 17 18 | view_data_to_mf(#{name := Name, |
| 18 19 | description := Description, |
| 19 20 | ctags := CTags, |
| @@ -22,7 +23,7 @@ view_data_to_mf(#{name := Name, | |
| 22 23 | rows := Rows}}) -> |
| 23 24 | FullRows = augment_rows_tags(Rows, Tags, CTags), |
| 24 25 | Metrics = rows_to_metrics(Type, FullRows), |
| 25 | - prometheus_model_helpers:create_mf(Name, Description, to_prometheus_type(Type), Metrics). |
| 26 | + prometheus_model_helpers:create_mf(Name, Description, to_prom_type(Type), Metrics). |
| 26 27 | |
| 27 28 | augment_rows_tags(Rows, Tags, CTags) -> |
| 28 29 | [{maps:to_list(maps:merge(CTags, maps:from_list(lists:zip(Tags, TagsV)))), Value} |
| @@ -48,11 +49,11 @@ sum_bucket_counters([], LAcc, _CAcc) -> | |
| 48 49 | sum_bucket_counters([{Bucket, Counter} | Counters], LAcc, CAcc) -> |
| 49 50 | sum_bucket_counters(Counters, LAcc ++ [{Bucket, CAcc + Counter}], CAcc + Counter). |
| 50 51 | |
| 51 | - to_prometheus_type(latest) -> |
| 52 | + to_prom_type(latest) -> |
| 52 53 | gauge; |
| 53 | - to_prometheus_type(count) -> |
| 54 | + to_prom_type(count) -> |
| 54 55 | counter; |
| 55 | - to_prometheus_type(sum) -> |
| 56 | + to_prom_type(sum) -> |
| 56 57 | summary; |
| 57 | - to_prometheus_type(distribution) -> |
| 58 | + to_prom_type(distribution) -> |
| 58 59 | histogram. |
Loading more files…