-
Notifications
You must be signed in to change notification settings - Fork 33
/
console_reporter.ex
163 lines (129 loc) · 4.6 KB
/
console_reporter.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
defmodule Telemetry.Metrics.ConsoleReporter do
@moduledoc """
A reporter that prints events and metrics to the terminal.
This is useful for debugging and discovering all available
measurements and metadata in an event.
For example, imagine the given metrics:
metrics = [
last_value("vm.memory.binary", unit: :byte),
counter("vm.memory.total")
]
A console reporter can be started as a child of your supervision tree as:
{Telemetry.Metrics.ConsoleReporter, metrics: metrics}
Now when the "vm.memory" telemetry event is dispatched, we will see
reports like this:
[Telemetry.Metrics.ConsoleReporter] Got new event!
Event name: vm.memory
All measurements: %{binary: 100, total: 200}
All metadata: %{}
Metric measurement: :binary (last_value)
With value: 100 byte
And tag values: %{}
Metric measurement: :total (counter)
With value: 200
And tag values: %{}
In other words, every time there is an event for any of the registered
metrics, it prints the event measurement and metadata, and then it prints
information about each metric to the user.
"""
use GenServer
require Logger
def start_link(opts) do
server_opts = Keyword.take(opts, [:name])
device = opts[:device] || :stdio
metrics =
opts[:metrics] ||
raise ArgumentError, "the :metrics option is required by #{inspect(__MODULE__)}"
GenServer.start_link(__MODULE__, {metrics, device}, server_opts)
end
@impl true
def init({metrics, device}) do
Process.flag(:trap_exit, true)
groups = Enum.group_by(metrics, & &1.event_name)
for {event, metrics} <- groups do
id = {__MODULE__, event, self()}
:telemetry.attach(id, event, &__MODULE__.handle_event/4, {metrics, device})
end
{:ok, Map.keys(groups)}
end
@impl true
def terminate(_, events) do
for event <- events do
:telemetry.detach({__MODULE__, event, self()})
end
:ok
end
@doc false
def handle_event(event_name, measurements, metadata, {metrics, device}) do
prelude = """
[#{inspect(__MODULE__)}] Got new event!
Event name: #{Enum.join(event_name, ".")}
All measurements: #{inspect(measurements)}
All metadata: #{inspect(metadata)}
"""
parts =
for %struct{} = metric <- metrics do
header = """
Metric measurement: #{inspect(metric.measurement)} (#{metric(struct)})
"""
[
header
| try do
measurement = extract_measurement(metric, measurements, metadata)
tags = extract_tags(metric, metadata)
cond do
is_nil(measurement) ->
"""
Measurement value missing (metric skipped)
"""
not keep?(metric, metadata) ->
"""
Event dropped
"""
metric.__struct__ == Telemetry.Metrics.Counter ->
"""
Tag values: #{inspect(tags)}
"""
true ->
"""
With value: #{inspect(measurement)}#{unit(metric.unit)}#{info(measurement)}
Tag values: #{inspect(tags)}
"""
end
rescue
e ->
Logger.error([
"Could not format metric #{inspect(metric)}\n",
Exception.format(:error, e, __STACKTRACE__)
])
"""
Errored when processing (metric skipped - handler may detach!)
"""
end
]
end
IO.puts(device, [prelude | parts])
end
defp keep?(%{keep: nil}, _metadata), do: true
defp keep?(metric, metadata), do: metric.keep.(metadata)
defp extract_measurement(metric, measurements, metadata) do
case metric.measurement do
fun when is_function(fun, 2) -> fun.(measurements, metadata)
fun when is_function(fun, 1) -> fun.(measurements)
key -> measurements[key]
end
end
defp info(int) when is_number(int), do: ""
defp info(_), do: " (WARNING! measurement should be a number)"
defp unit(:unit), do: ""
defp unit(unit), do: " #{unit}"
defp metric(Telemetry.Metrics.Counter), do: "counter"
defp metric(Telemetry.Metrics.Distribution), do: "distribution"
defp metric(Telemetry.Metrics.LastValue), do: "last_value"
defp metric(Telemetry.Metrics.Sum), do: "sum"
defp metric(Telemetry.Metrics.Summary), do: "summary"
defp extract_tags(metric, metadata) do
tag_values = metric.tag_values.(metadata)
Map.take(tag_values, metric.tags)
end
end