-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extracted from metric_api's statman_reporter.ex (#1)
...and turned into a separate application.
- Loading branch information
Showing
10 changed files
with
310 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Used by "mix format" | ||
[ | ||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# The directory Mix will write compiled artifacts to. | ||
/_build/ | ||
|
||
# If you run "mix test --cover", coverage assets end up here. | ||
/cover/ | ||
|
||
# The directory Mix downloads your dependencies sources to. | ||
/deps/ | ||
|
||
# Where 3rd-party dependencies like ExDoc output generated docs. | ||
/doc/ | ||
|
||
# Ignore .fetch files in case you like to edit your project deps locally. | ||
/.fetch | ||
|
||
# If the VM crashes, it generates a dump, let's ignore it too. | ||
erl_crash.dump | ||
|
||
# Also ignore archive artifacts (built via "mix archive.build"). | ||
*.ez | ||
|
||
# Ignore package tarball (built via "mix hex.build"). | ||
telemetry_metrics_statman-*.tar | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
language: elixir | ||
# fossa is Ubuntu 20.04 | ||
dist: fossa | ||
elixir: '1.10.3' | ||
otp_release: '22.3.4' | ||
before_install: | ||
- git config --global '[email protected]:GameAnalytics.insteadOf' 'https://github.com/GameAnalytics' | ||
script: | ||
- mix test | ||
- travis_wait mix dialyzer | ||
- mix credo | ||
|
||
# We want to cache Dialyzer PLTs, in order not to have to rebuild them | ||
# every time. (Dialyzer will automatically rebuild them when needed.) | ||
# They are in _build/dev, but since we can only cache an entire | ||
# directory at a time, we need to remove _build/dev/{lib,rel} before | ||
# caching. | ||
before_cache: | ||
- rm -rf _build/dev/lib _build/dev/rel | ||
cache: | ||
directories: | ||
- _build/dev |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
# telemetry_metrics_statman | ||
Telemetry.Metrics reporter for Statman | ||
# TelemetryMetricsStatman | ||
|
||
`Telemetry.Metrics` reporter for Statman. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# This file is responsible for configuring your application | ||
# and its dependencies with the aid of the Mix.Config module. | ||
use Mix.Config | ||
|
||
# This configuration is loaded before any dependency and is restricted | ||
# to this project. If another project depends on this project, this | ||
# file won't be loaded nor affect the parent project. For this reason, | ||
# if you want to provide default values for your application for | ||
# 3rd-party users, it should be done in your "mix.exs" file. | ||
|
||
# You can configure your application as: | ||
# | ||
# config :telemetry_metrics_statman, key: :value | ||
# | ||
# and access this configuration in your application as: | ||
# | ||
# Application.get_env(:telemetry_metrics_statman, :key) | ||
# | ||
# You can also configure a 3rd-party app: | ||
# | ||
# config :logger, level: :info | ||
# | ||
|
||
# It is also possible to import configuration files, relative to this | ||
# directory. For example, you can emulate configuration per environment | ||
# by uncommenting the line below and defining dev.exs, test.exs and such. | ||
# Configuration from the imported file will override the ones defined | ||
# here (which is why it is important to import them last). | ||
# | ||
# import_config "#{Mix.env()}.exs" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
defmodule TelemetryMetricsStatman do | ||
@moduledoc """ | ||
`Telemetry.Metrics` reporter that uses Statman as a middleman for metrics aggregation. | ||
To start using statman reporter, start the reporter under a | ||
supervision tree with a provided list of metrics, that have to be | ||
reported: | ||
import Telemetry.Metrics | ||
TelemetryMetricsStatman.start_link( | ||
metrics: [ | ||
counter("phoenix.endpoint.count"), | ||
summary("phoenix.endpoint.duration"), | ||
sum("customers.provisioned.count") | ||
] | ||
) | ||
Telemetry metrics are mapped to the internal statman metric types as follows: | ||
- `counter` -> `counter` | ||
- `sum` -> `gauge` (metric is incremented every when it's reported) | ||
- `last_value` -> `gauge` | ||
- `summary` -> `histogram` | ||
- `distribution` -> `histogram` | ||
""" | ||
|
||
use GenServer | ||
|
||
alias Telemetry.Metrics | ||
|
||
|
||
def start_link(options) do | ||
options[:metrics] || | ||
raise ArgumentError, "the :metrics option is required by #{inspect(__MODULE__)}" | ||
|
||
GenServer.start_link(__MODULE__, options) | ||
end | ||
|
||
|
||
@impl true | ||
def init(options) do | ||
Process.flag(:trap_exit, true) | ||
handler_ids = attach(options[:metrics]) | ||
|
||
{:ok, handler_ids} | ||
end | ||
|
||
|
||
@impl true | ||
def terminate(_, handler_ids), | ||
do: detach_handlers(handler_ids) | ||
|
||
def handle_event(_event, measurements, metadata, %{metrics: metrics}) do | ||
for metric <- metrics do | ||
if value = keep?(metric, metadata) && find_measurement(metric, measurements) do | ||
key = metric_key(metric, metric.tags, metadata) | ||
report(metric, key, value) | ||
end | ||
end | ||
end | ||
|
||
|
||
defp report(metric, key, value) when is_float(value) do | ||
report(metric, key, round(value)) | ||
end | ||
|
||
defp report(%Metrics.Counter{}, key, value), | ||
do: :statman.incr(key, value) | ||
|
||
defp report(%Metrics.Sum{}, key, value), | ||
do: :statman_gauge.incr(key, value) | ||
|
||
defp report(%Metrics.LastValue{}, key, value), | ||
do: :statman.set_gauge(key, value) | ||
|
||
defp report(%Metrics.Summary{}, key, value), | ||
do: :statman_histogram.record_value(key, :statman_histogram.bin(value)) | ||
|
||
defp report(%Metrics.Distribution{}, key, value), | ||
do: :statman_histogram.record_value(key, :statman_histogram.bin(value)) | ||
|
||
|
||
defp metric_key(metric, [] = _tags, _metadata), | ||
do: metric_name(metric) | ||
|
||
defp metric_key(metric, tags, metadata) do | ||
tag_values = metric.tag_values.(metadata) | ||
|
||
categories = | ||
tags | ||
|> Enum.map(&Map.fetch!(tag_values, &1)) | ||
|> List.to_tuple | ||
|
||
{metric_name(metric), categories} | ||
end | ||
|
||
|
||
defp metric_name(metric) do | ||
case metric.reporter_options[:report_as] do | ||
nil -> | ||
List.to_tuple(metric.name) | ||
|
||
name -> | ||
name | ||
end | ||
end | ||
|
||
|
||
defp keep?(%{keep: nil}, _metadata), do: true | ||
defp keep?(%{keep: keep}, metadata), do: keep.(metadata) | ||
|
||
|
||
defp find_measurement(%Metrics.Counter{} = metric, measurements) do | ||
case extract_measurement(metric, measurements) do | ||
nil -> | ||
1 | ||
|
||
value -> | ||
value | ||
end | ||
end | ||
|
||
|
||
defp find_measurement(metric, measurements), | ||
do: extract_measurement(metric, measurements) | ||
|
||
|
||
defp extract_measurement(metric, measurements) do | ||
case metric.measurement do | ||
fun when is_function(fun, 1) -> | ||
fun.(measurements) | ||
|
||
key -> | ||
measurements[key] | ||
end | ||
end | ||
|
||
|
||
defp attach(metrics) do | ||
metrics_by_event = Enum.group_by(metrics, & &1.event_name) | ||
event_handler = &__MODULE__.handle_event/4 | ||
|
||
for {event_name, event_metrics} <- metrics_by_event do | ||
id = handler_id(event_name) | ||
:telemetry.attach(id, event_name, event_handler, %{metrics: event_metrics}) | ||
|
||
id | ||
end | ||
end | ||
|
||
|
||
defp detach_handlers(handler_ids) do | ||
for handler_id <- handler_ids, | ||
do: :telemetry.detach(handler_id) | ||
end | ||
|
||
|
||
defp handler_id(event_name), do: {__MODULE__, event_name, self()} | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
defmodule TelemetryMetricsStatman.MixProject do | ||
use Mix.Project | ||
|
||
def project do | ||
[ | ||
app: :telemetry_metrics_statman, | ||
version: "0.1.0", | ||
elixir: "~> 1.10", | ||
start_permanent: Mix.env() == :prod, | ||
deps: deps() | ||
] | ||
end | ||
|
||
# Run "mix help compile.app" to learn about applications. | ||
def application do | ||
[ | ||
extra_applications: [:logger] | ||
] | ||
end | ||
|
||
# Run "mix help deps" to learn about dependencies. | ||
defp deps do | ||
[ | ||
{:telemetry_metrics, "~> 0.4"}, | ||
{:statman, github: "GameAnalytics/statman", tag: "v0.13"}, | ||
{:dialyxir, "~> 1.0", only: [:dev], runtime: false}, | ||
{:credo, "~> 1.4", only: [:dev, :test], runtime: false}, | ||
{:mock, "~> 0.3", only: :test}, | ||
] | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
%{ | ||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, | ||
"credo": {:hex, :credo, "1.5.4", "9914180105b438e378e94a844ec3a5088ae5875626fc945b7c1462b41afc3198", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cf51af45eadc0a3f39ba13b56fdac415c91b34f7b7533a13dc13550277141bc4"}, | ||
"decorators": {:hex, :decorators, "0.1.0", "1f4fd3682de23c6bce769201613812f5eaf809310a27e35ddbfd91e53b126267", [:rebar3], [{:parse_trans, "2.9.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "6a8f521232611f44ae09d0c7b40495018e2ea16b9b19a15a499f50ba347794f4"}, | ||
"dialyxir": {:hex, :dialyxir, "1.0.0", "6a1fa629f7881a9f5aaf3a78f094b2a51a0357c843871b8bc98824e7342d00a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "aeb06588145fac14ca08d8061a142d52753dbc2cf7f0d00fc1013f53f8654654"}, | ||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, | ||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, | ||
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"}, | ||
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"}, | ||
"mock": {:hex, :mock, "0.3.6", "e810a91fabc7adf63ab5fdbec5d9d3b492413b8cda5131a2a8aa34b4185eb9b4", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "bcf1d0a6826fb5aee01bae3d74474669a3fa8b2df274d094af54a25266a1ebd2"}, | ||
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, | ||
"statman": {:git, "https://github.com/GameAnalytics/statman.git", "0efecca9adf74fb62f04e3e671bc1f2aa38118e2", [tag: "v0.13"]}, | ||
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"}, | ||
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.0", "da9d49ee7e6bb1c259d36ce6539cd45ae14d81247a2b0c90edf55e2b50507f7b", [:mix], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5cfe67ad464b243835512aa44321cee91faed6ea868d7fb761d7016e02915c3d"}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
defmodule TelemetryMetricsStatmanTest do | ||
use ExUnit.Case | ||
doctest TelemetryMetricsStatman | ||
import Mock | ||
|
||
test "test counter" do | ||
with_mock :statman, [:passthrough], [] do | ||
# Map Telemetry event [:bar, :baz] to Statman counter "foo.bar" | ||
{:ok, pid} = TelemetryMetricsStatman.start_link(metrics: [ | ||
Telemetry.Metrics.counter("foo.bar", event_name: [:bar, :baz])]) | ||
:telemetry.execute([:bar, :baz], %{}) | ||
# Assert that the Statman counter was actually incremented | ||
assert_called(:statman.incr({:foo, :bar}, 1)) | ||
|
||
Process.unlink(pid) | ||
Process.exit(pid, :kill) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ExUnit.start() |