Skip to content

Commit 7ad84e9

Browse files
authored
Add support for custom metrics (#1513)
* Add support for custom metrics * Fix case where no firmware metadata exist and kills health header component
1 parent 2729a26 commit 7ad84e9

File tree

6 files changed

+108
-7
lines changed

6 files changed

+108
-7
lines changed

lib/nerves_hub/devices/metrics.ex

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule NervesHub.Devices.Metrics do
44
alias NervesHub.Devices.DeviceMetric
55
alias NervesHub.Repo
66

7-
@metric_types [
7+
@default_metric_types [
88
:cpu_temp,
99
:load_15min,
1010
:load_1min,
@@ -14,7 +14,7 @@ defmodule NervesHub.Devices.Metrics do
1414
:used_percent
1515
]
1616

17-
def metric_types, do: @metric_types
17+
def default_metric_types, do: @default_metric_types
1818

1919
@doc """
2020
Get all metrics for device
@@ -60,6 +60,27 @@ defmodule NervesHub.Devices.Metrics do
6060
|> Repo.all()
6161
end
6262

63+
def get_custom_metrics_for_device(device_id) do
64+
default_metrics = Enum.map(@default_metric_types, &Atom.to_string/1)
65+
66+
DeviceMetric
67+
|> where(device_id: ^device_id)
68+
|> where([dm], dm.key not in ^default_metrics)
69+
|> order_by(asc: :inserted_at)
70+
|> Repo.all()
71+
end
72+
73+
def get_custom_metrics_for_device(device_id, {time_unit, amount}) do
74+
default_metrics = Enum.map(@default_metric_types, &Atom.to_string/1)
75+
76+
DeviceMetric
77+
|> where(device_id: ^device_id)
78+
|> where([dm], dm.key not in ^default_metrics)
79+
|> where([d], d.inserted_at > ago(^amount, ^time_unit))
80+
|> order_by(asc: :inserted_at)
81+
|> Repo.all()
82+
end
83+
6384
def get_product_metrics_by_key(product_id, key) do
6485
DeviceMetric
6586
|> join(:left, [dm], d in assoc(dm, :device))
@@ -115,7 +136,7 @@ defmodule NervesHub.Devices.Metrics do
115136
Get map with latest values for all metric types. Also includes timestamp.
116137
"""
117138
def get_latest_metric_set_for_device(device_id) do
118-
@metric_types
139+
@default_metric_types
119140
|> Enum.reduce(%{}, fn type, acc ->
120141
Map.put(acc, type, get_latest_value(device_id, Atom.to_string(type)))
121142
end)

lib/nerves_hub_web/components/device_health/health_header.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ defmodule NervesHubWeb.Components.HealthHeader do
7070
</span>
7171
</div>
7272
<p>
73-
<span :if={!@latest_health}>Never</span>
73+
<span :if={!@latest_health.timestamp}>Never</span>
7474
<time
7575
:if={@latest_health.timestamp}
7676
id="last-reported-at"
@@ -93,7 +93,7 @@ defmodule NervesHubWeb.Components.HealthHeader do
9393
</div>
9494
<div>
9595
<div class="help-text mb-1">Platform</div>
96-
<%= if is_nil(@device.firmware_metadata.platform) do %>
96+
<%= if is_nil(@device.firmware_metadata) do %>
9797
<p>Unknown</p>
9898
<% else %>
9999
<p class="badge ff-m mt-0">

lib/nerves_hub_web/live/devices/device_health.ex

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,49 @@ defmodule NervesHubWeb.Live.Devices.DeviceHealth do
133133
) do
134134
latest_metrics = Metrics.get_latest_metric_set_for_device(device.id)
135135

136-
# Create graphs for metric types and assign to socket
137-
Metrics.metric_types()
136+
# Create graphs for default metric types and assign to socket
137+
Metrics.default_metric_types()
138138
|> Enum.reduce(socket, fn type, socket ->
139139
graph =
140140
create_graph_for_type(device.id, type, chart_type, time_frame, latest_metrics.size_mb)
141141

142142
socket |> assign(type, graph)
143143
end)
144144
|> assign(:latest_metrics, latest_metrics)
145+
|> assign_custom_metrics()
146+
end
147+
148+
def assign_custom_metrics(
149+
%{
150+
assigns: %{
151+
device: device,
152+
chart_type: chart_type,
153+
time_frame: time_frame
154+
}
155+
} =
156+
socket
157+
) do
158+
custom_metrics =
159+
device.id
160+
|> Metrics.get_custom_metrics_for_device(time_frame)
161+
|> Enum.group_by(& &1.key)
162+
|> Enum.map(fn {key, metrics} ->
163+
title =
164+
key
165+
|> String.replace("_", " ")
166+
|> String.capitalize()
167+
168+
max_size = get_custom_max_value(metrics)
169+
170+
graph =
171+
metrics
172+
|> organize_metrics_for_contex()
173+
|> create_chart(chart_type, max_size, time_frame)
174+
175+
%{title: title, graph: graph}
176+
end)
177+
178+
socket |> assign(:custom_metrics, custom_metrics)
145179
end
146180

147181
def create_graph_for_type(device_id, metric_type, chart_type, time_frame, memory_size) do
@@ -182,6 +216,12 @@ defmodule NervesHubWeb.Live.Devices.DeviceHealth do
182216
|> max(1)
183217
end
184218

219+
defp get_custom_max_value(data) do
220+
data
221+
|> Enum.max_by(& &1.value)
222+
|> Map.get(:value)
223+
end
224+
185225
defp create_chart(data, _chart_type, _max_value, _time_unit)
186226
when data == [],
187227
do: raw("<p class=\"metrics-text\">No data for selected period</p>")

lib/nerves_hub_web/live/devices/device_health.html.heex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,9 @@
4646
<HealthSection.render title="Load Average - 15 min" svg={@load_15min} />
4747
<HealthSection.render title="CPU Temperature in Celcius" svg={@cpu_temp} />
4848
<HealthSection.render title="Memory Usage" svg={@used_mb} memory_size={@latest_metrics.size_mb} memory_usage={@latest_metrics.used_percent} />
49+
50+
<%= for metric <- @custom_metrics do %>
51+
<HealthSection.render title={metric.title} svg={metric.graph} />
52+
<% end %>
4953
</div>
5054
</div>

test/nerves_hub/device_metrics_test.exs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,23 @@ defmodule NervesHub.DeviceMetricsTest do
6969
assert [] = Metrics.get_device_metrics_by_key(device.id, "cpu_temp")
7070
end
7171

72+
test "get custom metrics for device", %{device: device} do
73+
assert {:ok, %DeviceMetric{}} =
74+
Metrics.save_metric(%{device_id: device.id, key: "custom_1", value: 12})
75+
76+
assert {:ok, %DeviceMetric{}} =
77+
Metrics.save_metric(%{device_id: device.id, key: "custom_2", value: 13})
78+
79+
assert {:ok, %DeviceMetric{}} =
80+
Metrics.save_metric(%{device_id: device.id, key: "cpu_temp", value: 13})
81+
82+
custom_metrics =
83+
Metrics.get_custom_metrics_for_device(device.id)
84+
85+
# Should not include metrics with default metrics key
86+
assert length(custom_metrics) == 2
87+
end
88+
7289
# test "get device metrics within time frame"
7390
end
7491

test/nerves_hub_web/live/devices/health_test.exs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,23 @@ defmodule NervesHubWeb.Devices.HealthTest do
4343
|> visit("/org/#{org.name}/#{product.name}/devices/#{device.identifier}/health")
4444
|> assert_has("svg")
4545
end
46+
47+
test "Custom metrics", %{
48+
conn: conn,
49+
org: org,
50+
product: product,
51+
device: device
52+
} do
53+
assert {:ok, _} =
54+
Metrics.save_metric(%{device_id: device.id, key: "custom_1", value: 12})
55+
56+
assert {:ok, _} =
57+
Metrics.save_metric(%{device_id: device.id, key: "custom_2", value: 13})
58+
59+
conn
60+
|> visit("/org/#{org.name}/#{product.name}/devices/#{device.identifier}/health")
61+
|> assert_has(".help-text", text: "Custom 1")
62+
|> assert_has(".help-text", text: "Custom 2")
63+
|> assert_has("svg")
64+
end
4665
end

0 commit comments

Comments
 (0)