Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add custom metrics and metric history fork to LiveDashboard (#613)
* Use live_dashboard historical_data branch * historical_data integration WIP * possible metadata storage? * Drop poller behavior, just re-emit immediately Also rename metric and simplify history to include metadata poller was based on previous custom count logic, doesn’t make sense for shadowing something that works as is without poller. * Working Custom viewing metrics/history for simple controller actions * Fix Credo, add moduledocs to all new modules
- Loading branch information
Showing
11 changed files
with
212 additions
and
5 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
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
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
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
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,16 @@ | ||
defmodule CompaniesWeb.HistoricalData do | ||
@moduledoc """ | ||
Single source of truth to gather all individual modules that provide historical | ||
data to live dashboard and merge their seperate history maps into one. If adding | ||
a new source of metric history for LiveDashboard, it only needs to implement | ||
it's own signatures/0 method as the other modules here do, and be added the the | ||
list of signatures below. | ||
""" | ||
alias CompaniesWeb.{RepoMetricsHistory, ViewingStats} | ||
|
||
def signatures do | ||
for module <- [RepoMetricsHistory, ViewingStats], reduce: %{} do | ||
acc -> Map.merge(acc, apply(module, :signatures, [])) | ||
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,68 @@ | ||
defmodule CompaniesWeb.RepoMetricsHistory do | ||
@moduledoc """ | ||
GenServer to handle historical data for Ecto queries and rebroadcast to | ||
LiveDashboard under seperate history-focused telemetry namespace. | ||
""" | ||
|
||
use GenServer | ||
|
||
@telemetry_event [:companies, :repo, :query] | ||
@historic_metric [:ecto, :dashboard, :query] | ||
@history_buffer_size 500 | ||
|
||
def signatures do | ||
%{ | ||
@historic_metric => {__MODULE__, :data, []} | ||
} | ||
end | ||
|
||
def telemetry_event, do: @telemetry_event | ||
|
||
def start_link([]) do | ||
GenServer.start_link(__MODULE__, %{}, name: __MODULE__) | ||
end | ||
|
||
def init(_state) do | ||
{:ok, %{history: CircularBuffer.new(@history_buffer_size)}} | ||
end | ||
|
||
def data(%{event_name: event_name} = metric) do | ||
if List.starts_with?(event_name, @historic_metric) do | ||
GenServer.call(__MODULE__, {:data, metric}) | ||
else | ||
[] | ||
end | ||
end | ||
|
||
def setup_handlers do | ||
:telemetry.attach( | ||
"aggregation-handler-#{__MODULE__}", | ||
@telemetry_event, | ||
&__MODULE__.handle_event/4, | ||
nil | ||
) | ||
end | ||
|
||
def handle_event(@telemetry_event, metric_map, metadata, config) do | ||
GenServer.cast(__MODULE__, {:telemetry_metric, metric_map, metadata, config}) | ||
end | ||
|
||
def handle_call({:data, _metric}, _from, %{history: history}) do | ||
{:reply, CircularBuffer.to_list(history), %{history: history}} | ||
end | ||
|
||
def handle_cast({:telemetry_metric, metric_map, metadata, _config}, %{history: history}) do | ||
time = System.system_time(:second) | ||
:telemetry.execute(@historic_metric, metric_map, metadata) | ||
|
||
new_history = CircularBuffer.insert(history, %{data: metric_map, time: time, metadata: pruned_metadata(metadata)}) | ||
|
||
{:noreply, %{history: new_history}} | ||
end | ||
|
||
defp pruned_metadata(metadata) do | ||
# for now keep it all, reminder to either keep or drop selected fields based on dashboard usage to conserve memory, | ||
# ideally via some published source of truth hook from dahsboard module to ensure correctness | ||
metadata | ||
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
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
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,88 @@ | ||
defmodule CompaniesWeb.ViewingStats do | ||
@moduledoc """ | ||
GenServer to handle aggregating simple app stats and history for telemetry_poller | ||
to pass on to LiveDashboard. | ||
""" | ||
|
||
use GenServer | ||
|
||
@telemetry_event [:page_views, :count_events] | ||
@historic_metrics [:page_views, :companies_web] | ||
@history_buffer_size 500 | ||
|
||
def signatures do | ||
%{ | ||
@historic_metrics => {__MODULE__, :data, []} | ||
} | ||
end | ||
|
||
def telemetry_event, do: @telemetry_event | ||
|
||
def start_link([]) do | ||
GenServer.start_link(__MODULE__, %{}, name: __MODULE__) | ||
end | ||
|
||
def init(_state) do | ||
{:ok, %{history: CircularBuffer.new(@history_buffer_size), current: %{}}} | ||
end | ||
|
||
def data(%{event_name: event_name} = metric) do | ||
if List.starts_with?(event_name, @historic_metrics) do | ||
GenServer.call(__MODULE__, {:data, metric}) | ||
else | ||
[] | ||
end | ||
end | ||
|
||
def setup_handlers do | ||
:telemetry.attach( | ||
"aggregation-handler-#{__MODULE__}", | ||
@telemetry_event, | ||
&__MODULE__.handle_event/4, | ||
nil | ||
) | ||
end | ||
|
||
def handle_event(@telemetry_event, map, metadata, config) do | ||
GenServer.cast(__MODULE__, {:telemetry_metric, map, metadata, config}) | ||
end | ||
|
||
def emit do | ||
GenServer.cast(__MODULE__, :emit_telemetry) | ||
end | ||
|
||
def handle_call({:raw_data, _metric}, _from, %{history: history} = state) do | ||
{:reply, history, state} | ||
end | ||
|
||
def handle_call({:data, metric}, _from, %{history: history} = state) do | ||
local_metric = List.last(metric.name) | ||
|
||
reply = | ||
for {time, time_metrics} <- history, | ||
{^local_metric, data} <- time_metrics do | ||
%{data: %{local_metric => data}, time: time} | ||
end | ||
|
||
{:reply, reply, state} | ||
end | ||
|
||
def handle_cast(:emit_telemetry, %{history: history, current: current}) do | ||
time = System.system_time(:second) | ||
|
||
for {key, value} <- current do | ||
:telemetry.execute(@historic_metrics, %{key => value}) | ||
end | ||
|
||
{:noreply, %{history: CircularBuffer.insert(history, {time, current}), current: %{}}} | ||
end | ||
|
||
def handle_cast({:telemetry_metric, metric_map, _metadata, _config}, %{current: current} = state) do | ||
updated_current = | ||
for {key, value} <- metric_map, reduce: current do | ||
acc -> Map.put_new(acc, key, 0) |> update_in([key], &(&1 + value)) | ||
end | ||
|
||
{:noreply, %{state | current: updated_current}} | ||
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
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