mirror of
https://github.com/woutdp/live_svelte
synced 2026-05-24 01:18:53 +00:00
chore: added ssr telemetry
This commit is contained in:
parent
2a9c197c53
commit
079ed517b7
3 changed files with 129 additions and 1 deletions
21
lib/ssr.ex
21
lib/ssr.ex
|
|
@ -11,6 +11,22 @@ defmodule LiveSvelte.SSR do
|
|||
To define a custom renderer, change the application config in `config.exs`:
|
||||
|
||||
config :live_svelte, ssr_module: MyCustomSSRModule
|
||||
|
||||
## Telemetry
|
||||
|
||||
Exposes a telemetry span for each render under the key `[:live_svelte, :ssr]`.
|
||||
|
||||
The following events are emitted:
|
||||
|
||||
* `[:live_svelte, :ssr, :start]` — fired when a render begins.
|
||||
Metadata: `%{component: name, props: props, slots: slots}`.
|
||||
|
||||
* `[:live_svelte, :ssr, :stop]` — fired when a render completes successfully.
|
||||
Metadata: same. Measurements include `%{duration: duration}` in native time units
|
||||
(convert with `:erlang.convert_time_unit(duration, :native, :millisecond)`).
|
||||
|
||||
* `[:live_svelte, :ssr, :exception]` — fired when the renderer raises.
|
||||
The exception is re-raised after the event is emitted.
|
||||
"""
|
||||
|
||||
@type component_name :: String.t()
|
||||
|
|
@ -41,8 +57,11 @@ defmodule LiveSvelte.SSR do
|
|||
@spec render(component_name, props, slots) :: render_response | no_return
|
||||
def render(name, props, slots) do
|
||||
mod = Application.get_env(:live_svelte, :ssr_module, LiveSvelte.SSR.NodeJS)
|
||||
meta = %{component: name, props: props, slots: slots}
|
||||
|
||||
mod.render(name, props, slots)
|
||||
:telemetry.span([:live_svelte, :ssr], meta, fn ->
|
||||
{mod.render(name, props, slots), meta}
|
||||
end)
|
||||
end
|
||||
|
||||
@deprecated "Use LiveSvelte.SSR.NodeJS.server_path/0 instead."
|
||||
|
|
|
|||
1
mix.exs
1
mix.exs
|
|
@ -67,6 +67,7 @@ defmodule LiveSvelte.MixProject do
|
|||
{:jason, "~> 1.2", optional: true},
|
||||
{:lazy_html, ">= 0.1.0", only: :test},
|
||||
{:nodejs, "~> 3.1"},
|
||||
{:telemetry, "~> 0.4 or ~> 1.0"},
|
||||
{:phoenix, ">= 1.7.0"},
|
||||
{:phoenix_html, ">= 3.3.1"},
|
||||
{:phoenix_live_view, ">= 0.18.0"}
|
||||
|
|
|
|||
108
test/ssr_telemetry_test.exs
Normal file
108
test/ssr_telemetry_test.exs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
defmodule LiveSvelte.SSRTelemetryTest do
|
||||
# must be synchronous — modifies global config and attaches telemetry handlers
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
alias LiveSvelte.SSR
|
||||
|
||||
# Named handler to avoid telemetry "local function" performance penalty log warning.
|
||||
# test_pid is threaded through the telemetry config argument.
|
||||
def handle_telemetry(event, measurements, metadata, test_pid) do
|
||||
send(test_pid, {:telemetry_event, event, measurements, metadata})
|
||||
end
|
||||
|
||||
defmodule MockSSRRenderer do
|
||||
@moduledoc false
|
||||
@behaviour SSR
|
||||
|
||||
@impl true
|
||||
def render("Success", _props, _slots) do
|
||||
%{"head" => "", "html" => "<div>Hello</div>"}
|
||||
end
|
||||
|
||||
def render("Failing", _props, _slots) do
|
||||
raise RuntimeError, "SSR render failed"
|
||||
end
|
||||
|
||||
# Catch-all: prevents unhelpful FunctionClauseError on typos in test component names
|
||||
def render(name, _props, _slots) do
|
||||
raise "MockSSRRenderer: no handler for #{inspect(name)}"
|
||||
end
|
||||
end
|
||||
|
||||
setup do
|
||||
original_module = Application.get_env(:live_svelte, :ssr_module)
|
||||
Application.put_env(:live_svelte, :ssr_module, MockSSRRenderer)
|
||||
|
||||
test_pid = self()
|
||||
handler_id = {__MODULE__, :telemetry_test, make_ref()}
|
||||
|
||||
:telemetry.attach_many(
|
||||
handler_id,
|
||||
[
|
||||
[:live_svelte, :ssr, :start],
|
||||
[:live_svelte, :ssr, :stop],
|
||||
[:live_svelte, :ssr, :exception]
|
||||
],
|
||||
&__MODULE__.handle_telemetry/4,
|
||||
test_pid
|
||||
)
|
||||
|
||||
on_exit(fn ->
|
||||
:telemetry.detach(handler_id)
|
||||
|
||||
if original_module do
|
||||
Application.put_env(:live_svelte, :ssr_module, original_module)
|
||||
else
|
||||
Application.delete_env(:live_svelte, :ssr_module)
|
||||
end
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "SSR telemetry" do
|
||||
test "emits :start event with component, props, and slots metadata" do
|
||||
props = %{"count" => 42}
|
||||
slots = %{"default" => "<span>slot content</span>"}
|
||||
|
||||
SSR.render("Success", props, slots)
|
||||
|
||||
assert_receive {:telemetry_event, [:live_svelte, :ssr, :start], _measurements, metadata}
|
||||
assert metadata.component == "Success"
|
||||
assert metadata.props == %{"count" => 42}
|
||||
assert metadata.slots == %{"default" => "<span>slot content</span>"}
|
||||
end
|
||||
|
||||
test "emits :stop event with duration measurement and passes through render result" do
|
||||
result = SSR.render("Success", %{}, %{})
|
||||
|
||||
assert result == %{"head" => "", "html" => "<div>Hello</div>"}
|
||||
assert_receive {:telemetry_event, [:live_svelte, :ssr, :stop], measurements, metadata}
|
||||
assert metadata.component == "Success"
|
||||
assert is_integer(measurements.duration)
|
||||
assert measurements.duration >= 0
|
||||
end
|
||||
|
||||
test "emits :exception event with metadata and duration when renderer raises" do
|
||||
assert_raise RuntimeError, "SSR render failed", fn ->
|
||||
SSR.render("Failing", %{}, %{})
|
||||
end
|
||||
|
||||
assert_receive {:telemetry_event, [:live_svelte, :ssr, :exception], measurements, metadata}
|
||||
assert metadata.component == "Failing"
|
||||
assert is_integer(measurements.duration)
|
||||
assert measurements.duration >= 0
|
||||
end
|
||||
|
||||
test "emits independent telemetry events for each render call" do
|
||||
SSR.render("Success", %{"call" => 1}, %{})
|
||||
SSR.render("Success", %{"call" => 2}, %{})
|
||||
|
||||
assert_receive {:telemetry_event, [:live_svelte, :ssr, :start], _,
|
||||
%{component: "Success", props: %{"call" => 1}}}
|
||||
|
||||
assert_receive {:telemetry_event, [:live_svelte, :ssr, :start], _,
|
||||
%{component: "Success", props: %{"call" => 2}}}
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in a new issue