Skip to content

Commit 4b3a65d

Browse files
set up live view; custom LV instrumentation; notes + telemetry deep dive
1 parent f7db6f8 commit 4b3a65d

File tree

14 files changed

+156
-209
lines changed

14 files changed

+156
-209
lines changed

README.md

Lines changed: 14 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -121,215 +121,34 @@ end
121121
* Abstract away common instrumentation needs and automatically send such events to your reporter of choice.
122122
* Can still define custom handlers for events and do more stuff with them
123123

124-
### Metrics Report
125-
126-
```bash
127-
Flushing stats at Sun Mar 22 2020 13:36:41 GMT-0400 (Eastern Daylight Time)
128-
{
129-
counters: {
130-
'statsd.bad_lines_seen': 0,
131-
'statsd.packets_received': 11,
132-
'statsd.metrics_received': 40,
133-
'quantum.repo.query.count.users.select': 6,
134-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.PageController.index': 0,
135-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.UserController.show': 1,
136-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.delete': 1,
137-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.new': 2,
138-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.create': 1,
139-
# error request handling
140-
'phoenix.error_rendered.duration.404.-blah': 0,
141-
'phoenix.error_rendered.duration.404.-blah-sophie-123': 0,
142-
'phoenix.error_rendered.duration.500.-': 0
143-
},
144-
timers: {
145-
'quantum.phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.UserController.show': [],
146-
'quantum.phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.PageController.index': [],
147-
'quantum.repo.query.total_time.users.select': [ 0, 0, 0, 0, 1, 1 ],
148-
'quantum.repo.query.decode_time': [ 0, 0, 0, 0, 0, 0 ],
149-
'quantum.repo.query.query_time': [ 0, 0, 0, 0, 0, 0 ],
150-
'quantum.repo.query.queue_time': [ 0, 0, 0, 0, 0, 1 ],
151-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.UserController.show': [ 4 ],
152-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.PageController.index': [],
153-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.delete': [ 3 ],
154-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.new': [ 1, 8 ],
155-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.create': [ 245 ]
156-
},
157-
gauges: {
158-
'vm.memory.total': 50191248,
159-
'vm.total_run_queue_lengths.total': 0,
160-
'vm.total_run_queue_lengths.cpu': 0,
161-
'vm.total_run_queue_lengths.io': 0,
162-
'statsd.timestamp_lag': 0
163-
},
164-
timer_data: {
165-
'quantum.phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.UserController.show': { count_ps: 0, count: 0 },
166-
'quantum.phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.PageController.index': { count_ps: 0, count: 0 },
167-
'quantum.repo.query.total_time.users.select': {
168-
count_90: 5,
169-
mean_90: 0.2,
170-
upper_90: 1,
171-
sum_90: 1,
172-
sum_squares_90: 1,
173-
std: 0.4714045207910317,
174-
upper: 1,
175-
lower: 0,
176-
count: 6,
177-
count_ps: 0.6,
178-
sum: 2,
179-
sum_squares: 2,
180-
mean: 0.3333333333333333,
181-
median: 0
182-
},
183-
'quantum.repo.query.decode_time': {
184-
count_90: 5,
185-
mean_90: 0,
186-
upper_90: 0,
187-
sum_90: 0,
188-
sum_squares_90: 0,
189-
std: 0,
190-
upper: 0,
191-
lower: 0,
192-
count: 6,
193-
count_ps: 0.6,
194-
sum: 0,
195-
sum_squares: 0,
196-
mean: 0,
197-
median: 0
198-
},
199-
'quantum.repo.query.query_time': {
200-
count_90: 5,
201-
mean_90: 0,
202-
upper_90: 0,
203-
sum_90: 0,
204-
sum_squares_90: 0,
205-
std: 0,
206-
upper: 0,
207-
lower: 0,
208-
count: 6,
209-
count_ps: 0.6,
210-
sum: 0,
211-
sum_squares: 0,
212-
mean: 0,
213-
median: 0
214-
},
215-
'quantum.repo.query.queue_time': {
216-
count_90: 5,
217-
mean_90: 0,
218-
upper_90: 0,
219-
sum_90: 0,
220-
sum_squares_90: 0,
221-
std: 0.372677996249965,
222-
upper: 1,
223-
lower: 0,
224-
count: 6,
225-
count_ps: 0.6,
226-
sum: 1,
227-
sum_squares: 1,
228-
mean: 0.16666666666666666,
229-
median: 0
230-
},
231-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.UserController.show': {
232-
count_90: 1,
233-
mean_90: 4,
234-
upper_90: 4,
235-
sum_90: 4,
236-
sum_squares_90: 16,
237-
std: 0,
238-
upper: 4,
239-
lower: 4,
240-
count: 1,
241-
count_ps: 0.1,
242-
sum: 4,
243-
sum_squares: 16,
244-
mean: 4,
245-
median: 4
246-
},
247-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.PageController.index': { count_ps: 0, count: 0 },
248-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.delete': {
249-
count_90: 1,
250-
mean_90: 3,
251-
upper_90: 3,
252-
sum_90: 3,
253-
sum_squares_90: 9,
254-
std: 0,
255-
upper: 3,
256-
lower: 3,
257-
count: 1,
258-
count_ps: 0.1,
259-
sum: 3,
260-
sum_squares: 9,
261-
mean: 3,
262-
median: 3
263-
},
264-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.new': {
265-
count_90: 2,
266-
mean_90: 4.5,
267-
upper_90: 8,
268-
sum_90: 9,
269-
sum_squares_90: 65,
270-
std: 3.5,
271-
upper: 8,
272-
lower: 1,
273-
count: 2,
274-
count_ps: 0.2,
275-
sum: 9,
276-
sum_squares: 65,
277-
mean: 4.5,
278-
median: 4.5
279-
},
280-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.create': {
281-
count_90: 1,
282-
mean_90: 245,
283-
upper_90: 245,
284-
sum_90: 245,
285-
sum_squares_90: 60025,
286-
std: 0,
287-
upper: 245,
288-
lower: 245,
289-
count: 1,
290-
count_ps: 0.1,
291-
sum: 245,
292-
sum_squares: 60025,
293-
mean: 245,
294-
median: 245
295-
}
296-
},
297-
counter_rates: {
298-
'statsd.bad_lines_seen': 0,
299-
'statsd.packets_received': 1.1,
300-
'statsd.metrics_received': 4,
301-
'quantum.repo.query.count.users.select': 0.6,
302-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.PageController.index': 0,
303-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.UserController.show': 0.1,
304-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.delete': 0.1,
305-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.new': 0.2,
306-
'phoenix.router_dispatch.stop.duration.Elixir.QuantumWeb.SessionController.create': 0.1
307-
},
308-
sets: {},
309-
pctThreshold: [ 90 ]
310-
}
311-
```
312-
313124
## TODO
314125

315126
- [X] Success/failure web request response instrumentation
316-
* LiveView metrics with channel joined and channel handled_in
127+
* LiveView metrics with channel joined and channel handled_in -> can't be done OOTB, blog post should explain, show channel source, link to LV issue
317128
* Three custom metrics:
318129
* Worker polling
319130
* Custom event polling
320131
* Telemetry plug
321-
* Success/failure indicator
132+
- [X] LiveView handle event duration and timer
322133
- [X] VM metrics with polling
323134
* Visualize DD reporting by using DD formatter but running regular statsd, grab log statement from error message
324135

325136
## Notes
326137
* We're instrumenting for free:
327138
* Database query duration and counts
328139
* HTTP request duration and counts
140+
* VM metrics
141+
* Telemetry event handling for free with Telemetry metrics module--can emit any event with `:telemetry.execute` (is this Erlang??) and don't need to define and attach custom handle module.
329142

330143
## Blog Post
331144
* What is observability? What is instrumentation?
332145
* Common needs: web requests, database queries
146+
* Show the DIY - define an event + module, attach, custom log in handler module to report, log, etc. This might be a good place to look under the hood at ETS.
147+
* Reporter calls `telemetry.attach`
148+
* Look in `telemetry.erl`:
149+
* attach stores handler modules with associated events in ETS
150+
* execute looks up the handler for the event in ETS and invokes it
151+
* This is all abstracted away with Telemetry metrics!
333152
* OOTB instrumentation with Elixir Telemetry
334153
* We'll get web requests, database queries, VM monitoring
335154
* Implementation
@@ -341,12 +160,13 @@ Flushing stats at Sun Mar 22 2020 13:36:41 GMT-0400 (Eastern Daylight Time)
341160
* Note on Datadog formatter
342161
* Tags translate into metric tags (show the mapping)
343162
* Can leverage prefix, global tags, HTTP route tag now more usefully
344-
* Custom instrumentation
163+
* Custom instrumentation -> not necessary, any event can be handled by one Telemetry module importing `Telemetry.Metrics`
345164
* Define telemetry handler
346165
* Attach in telemetry module (?)
347166
* Good candidate--custom interaction error count - log in failure/success?
348-
* Instrumentating LiveView with Phoenix's OOTB Telemetry events
349-
* Telemetry under the hood - trace the flow of PHoenix/Ecto/app code emitting event and telemetry looking up event handle and calling it. Look at tags, etc.
167+
* Instrumentating LiveView with Phoenix's OOTB Telemetry events - CAN'T! Worth noting and comparing to Phoenix channel OOTB telemetry events, link to issue.
168+
* Custom duration and count instrumentation for
169+
* Telemetry under the hood - trace the flow of Phoenix/Ecto/app code emitting event and telemetry looking up event handle and calling it. Look at tags, etc.
350170

351171
### Questions
352172
- [X] How to instrument success/failure of web requests?

assets/js/app.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,10 @@ import "phoenix_html"
1515
//
1616
// Local files can be imported directly using relative paths, for example:
1717
// import socket from "./socket"
18+
import {Socket} from "phoenix"
19+
20+
import LiveSocket from "phoenix_live_view"
21+
22+
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");
23+
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}});
24+
liveSocket.connect()

assets/package-lock.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
},
88
"dependencies": {
99
"phoenix": "file:../deps/phoenix",
10-
"phoenix_html": "file:../deps/phoenix_html"
10+
"phoenix_html": "file:../deps/phoenix_html",
11+
"phoenix_live_view": "file:../deps/phoenix_live_view"
1112
},
1213
"devDependencies": {
1314
"@babel/core": "^7.0.0",

lib/quantum_web.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ defmodule QuantumWeb do
2020
def controller do
2121
quote do
2222
use Phoenix.Controller, namespace: QuantumWeb
23-
23+
import Phoenix.LiveView.Controller
2424
import Plug.Conn
2525
import QuantumWeb.Gettext
2626
alias QuantumWeb.Router.Helpers, as: Routes
@@ -43,6 +43,7 @@ defmodule QuantumWeb do
4343
import QuantumWeb.Gettext
4444
alias QuantumWeb.Router.Helpers, as: Routes
4545
import Quantum.Accounts.Auth, only: [signed_in?: 1]
46+
import Phoenix.LiveView.Helpers
4647
end
4748
end
4849

@@ -51,6 +52,7 @@ defmodule QuantumWeb do
5152
use Phoenix.Router
5253
import Plug.Conn
5354
import Phoenix.Controller
55+
import Phoenix.LiveView.Router
5456
end
5557
end
5658

lib/quantum_web/endpoint.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ defmodule QuantumWeb.Endpoint do
1414
websocket: true,
1515
longpoll: false
1616

17+
socket "/live", Phoenix.LiveView.Socket,
18+
websocket: [connect_info: [session: @session_options]]
19+
1720
# socket "/live", Phoenix.LiveView.Socket
1821

1922
# Serve at "/" the static files from "priv/static" directory.

lib/quantum_web/live/button_live.ex

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
defmodule QuantumWeb.ButtonLive do
2+
use Phoenix.LiveView, layout: {QuantumWeb.LayoutView, "live.html"}
3+
alias Quantum.Accounts
4+
5+
def mount(_params, %{"current_user_id" => user_id}, socket) do
6+
socket =
7+
socket
8+
|> assign(:current_user, Accounts.get_user!(user_id))
9+
|> assign(:selected_button, nil)
10+
{:ok, socket}
11+
end
12+
13+
def render(assigns) do
14+
Phoenix.View.render(QuantumWeb.ButtonView, "show.html", assigns)
15+
end
16+
17+
def handle_event("button_click", %{"button" => btn}, socket) do
18+
start = System.monotonic_time()
19+
20+
socket =
21+
socket
22+
|> assign(:selected_button, btn)
23+
24+
:telemetry.execute([:live, :handle_event, :button_click], %{duration: System.monotonic_time() - start}, socket)
25+
26+
{:noreply, socket}
27+
end
28+
end

lib/quantum_web/router.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
defmodule QuantumWeb.Router do
22
use QuantumWeb, :router
3-
43
pipeline :browser do
4+
# plug :put_root_layout, {QuantumWeb.LayoutView, :app}
55
plug :accepts, ["html"]
66
plug :fetch_session
7-
plug :fetch_flash
7+
plug :fetch_live_flash
88
plug :protect_from_forgery
99
plug :put_secure_browser_headers
1010
plug QuantumWeb.Plugs.CurrentUser
@@ -34,6 +34,7 @@ defmodule QuantumWeb.Router do
3434
delete "/logout", SessionController, :delete
3535

3636
get "/users/:id", UserController, :show
37+
live "/buttons", ButtonLive
3738
end
3839

3940
# if Mix.env() == :dev do

lib/quantum_web/telemetry.ex

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ defmodule QuantumWeb.Telemetry do
6363
"phoenix.error_rendered.duration",
6464
tag_values: &__MODULE__.error_request_metadata/1,
6565
tags: [:status, :request_path]
66+
),
67+
68+
# LiveView metrics
69+
summary(
70+
"live.handle_event.button_click.duration"
71+
),
72+
counter(
73+
"live.handle_event.button_click.count",
74+
tag_values: &__MODULE__.live_view_metadata/1,
75+
tags: [:button]
6676
)
6777
]
6878
end
@@ -79,7 +89,11 @@ defmodule QuantumWeb.Telemetry do
7989
%{status: status, plug: plug, plug_opts: plug_opts}
8090
end
8191

82-
# custom polling for worker metrics
92+
def live_view_metadata(%{assigns: %{selected_button: button}}) do
93+
%{button: button}
94+
end
95+
96+
# custom polling for worker metrics
8397
# defp periodic_measurements do
8498
# [
8599
# {:process_info,
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<h1>Buttons!</h1>
2+
<h3>Because, why not?</h3>
3+
<h4>Hi, <%= @current_user.email %>! Please select a button</h4>
4+
<button type="button" class="btn btn-primary btn-lg" phx-click="button_click" phx-value-button="blue" <%= if @selected_button == "blue", do: "disabled"%>>Blue Button</button>
5+
<button type="button" class="btn btn-secondary btn-lg" phx-click="button_click" phx-value-button="grey" <%= if @selected_button == "grey", do: "disabled"%>>Grey Button</button>
6+
<button type="button" class="btn btn-success btn-lg" phx-click="button_click" phx-value-button="green" <%= if @selected_button == "green", do: "disabled"%>>Green Button</button>
7+
<br><br>
8+
<div class="">
9+
<h4>Selected: <%= @selected_button %></h4>
10+
</div>

0 commit comments

Comments
 (0)