Skip to content

Commit

Permalink
Switching over to Phoenix init event as opposed to polling Endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
akoutmos committed Aug 8, 2024
1 parent fcfcae9 commit c358232
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 63 deletions.
99 changes: 43 additions & 56 deletions lib/prom_ex/plugins/phoenix.ex
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ if Code.ensure_loaded?(Phoenix) do
- `:phoenix_http_event_metrics`
- `:phoenix_channel_event_metrics`
- `:phoenix_socket_event_metrics`
- `:phoenix_endpoint_manual_metrics`
- `:phoenix_endpoint_metrics`
## Usage
Expand Down Expand Up @@ -167,6 +167,7 @@ if Code.ensure_loaded?(Phoenix) do
alias PromEx.Utils

@stop_event [:prom_ex, :plugin, :phoenix, :stop]
@init_event [:phoenix, :endpoint, :init]

@impl true
def event_metrics(opts) do
Expand All @@ -180,87 +181,73 @@ if Code.ensure_loaded?(Phoenix) do

# Event metrics definitions
[
endpoint_info(metric_prefix, opts)
http_events(metric_prefix, opts),
channel_events(metric_prefix, duration_unit, normalize_event_name),
socket_events(metric_prefix, duration_unit)
]
end

@impl true
def manual_metrics(opts) do
otp_app = Keyword.fetch!(opts, :otp_app)
metric_prefix = Keyword.get(opts, :metric_prefix, PromEx.metric_prefix(otp_app, :phoenix))

[
endpoint_info(metric_prefix, opts)
socket_events(metric_prefix, duration_unit),
]
end

defp endpoint_info(metric_prefix, opts) do
# Fetch user options
phoenix_endpoint = Keyword.get(opts, :endpoint) || Keyword.get(opts, :endpoints)
keep_function_filter = keep_endpoint_metrics(phoenix_endpoint)

Manual.build(
:phoenix_endpoint_manual_metrics,
{__MODULE__, :execute_phoenix_endpoint_info, [phoenix_endpoint]},
Event.build(
:phoenix_endpoint_metrics,
[
last_value(
metric_prefix ++ [:endpoint, :url, :info],
event_name: [:prom_ex, :plugin, :phoenix, :endpoint_url],
event_name: @init_event,
description: "The configured URL of the Endpoint module.",
measurement: :status,
tags: [:url, :endpoint]
measurement: fn _measurements -> 1 end,
tag_values: &phoenix_init_tag_values/1,
tags: [:url, :endpoint],
keep: keep_function_filter
),
last_value(
metric_prefix ++ [:endpoint, :port, :info],
event_name: [:prom_ex, :plugin, :phoenix, :endpoint_port],
event_name: @init_event,
description: "The configured port of the Endpoint module.",
measurement: :status,
tags: [:port, :endpoint]
measurement: fn _measurements -> 1 end,
tag_values: &phoenix_init_tag_values/1,
tags: [:port, :endpoint],
keep: keep_function_filter
)
]
)
end

@doc false
def execute_phoenix_endpoint_info(endpoint) do
# TODO: This is a bit of a hack until Phoenix supports an init telemetry event to
# reliably get the configuration.
endpoint_init_checker = fn
count, endpoint_module, endpoint_init_checker_function when count < 10 ->
case Process.whereis(endpoint_module) do
pid when is_pid(pid) ->
measurements = %{status: 1}
url_metadata = %{url: endpoint_module.url(), endpoint: normalize_module_name(endpoint_module)}
:telemetry.execute([:prom_ex, :plugin, :phoenix, :endpoint_url], measurements, url_metadata)

%URI{port: port} = endpoint_module.struct_url()
port_metadata = %{port: port, endpoint: normalize_module_name(endpoint_module)}
:telemetry.execute([:prom_ex, :plugin, :phoenix, :endpoint_port], measurements, port_metadata)

_ ->
Process.sleep(1_000)
endpoint_init_checker_function.(count + 1, endpoint_module, endpoint_init_checker_function)
end

_, _, _ ->
:noop
end
defp keep_endpoint_metrics(phoenix_endpoint) when is_atom(phoenix_endpoint) do
keep_endpoint_metrics([phoenix_endpoint])
end

if is_list(endpoint) do
endpoint
|> Enum.each(fn {endpoint_module, _} ->
Task.start(fn ->
endpoint_init_checker.(0, endpoint_module, endpoint_init_checker)
end)
end)
else
Task.start(fn ->
endpoint_init_checker.(0, endpoint, endpoint_init_checker)
end)
defp keep_endpoint_metrics(phoenix_endpoints) do
fn %{module: module} ->
module in phoenix_endpoints
end
end

defp phoenix_init_tag_values(%{config: config, module: module}) do
port =
cond do
Keyword.has_key?(config, :http) and config[:http][:port] ->
config[:http][:port]

Keyword.has_key?(config, :https) and config[:https][:port] ->
config[:https][:port]

true ->
"Unknown"
end

%{
endpoint: module,
url: module.url(),
port: port
}
end

defp http_events(metric_prefix, opts) do
routers = fetch_routers!(opts)
additional_routes = fetch_additional_routes!(opts)
Expand Down
8 changes: 1 addition & 7 deletions test/prom_ex/plugins/phoenix_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ defmodule PromEx.Plugins.PhoenixTest do

describe "event_metrics/1" do
test "should return the correct number of metrics" do
assert length(Phoenix.event_metrics(otp_app: :prom_ex, router: Some.Module)) == 3
assert length(Phoenix.event_metrics(otp_app: :prom_ex, router: Some.Module)) == 4
end
end

Expand All @@ -93,10 +93,4 @@ defmodule PromEx.Plugins.PhoenixTest do
assert Phoenix.polling_metrics([]) == []
end
end

describe "manual_metrics/1" do
test "should return the correct number of metrics" do
assert length(Phoenix.manual_metrics(otp_app: :prom_ex, router: Some.Module, endpoint: Some.Endpoint)) == 1
end
end
end

0 comments on commit c358232

Please sign in to comment.