Skip to content

Instantly share code, notes, and snippets.

@ivanovv
Created May 12, 2021 22:06
Show Gist options
  • Select an option

  • Save ivanovv/6290e9178e0229a689a21c3e2a62d280 to your computer and use it in GitHub Desktop.

Select an option

Save ivanovv/6290e9178e0229a689a21c3e2a62d280 to your computer and use it in GitHub Desktop.
socket_connected events
defmodule WebinarGeek.PromEx.Plugins.Phoenix do
@moduledoc false
use PromEx.Plugin
require Logger
alias Phoenix.Socket
alias Plug.Conn
@impl true
def event_metrics(opts) do
otp_app = Keyword.fetch!(opts, :otp_app)
metric_prefix = PromEx.metric_prefix(otp_app, :phoenix)
# Event metrics definitions
[
http_events(metric_prefix, opts),
channel_events(metric_prefix),
socket_events(metric_prefix),
]
end
defp http_events(metric_prefix, opts) do
# Fetch user options
phoenix_router = Keyword.fetch!(opts, :router)
event_prefix = Keyword.get(opts, :event_prefix, [:phoenix, :endpoint])
# Shared configuration
phoenix_stop_event = event_prefix ++ [:stop]
http_metrics_tags = [:status, :method, :path, :controller, :action]
Event.build(
:phoenix_http_event_metrics,
[
# Capture request duration information
distribution(
metric_prefix ++ [:http, :request, :duration, :milliseconds],
event_name: phoenix_stop_event,
measurement: :duration,
description: "The time it takes for the application to respond to HTTP requests.",
reporter_options: [
buckets: exponential!(1, 2, 12)
],
tag_values: get_conn_tags(phoenix_router),
tags: http_metrics_tags,
unit: {:native, :millisecond}
),
# Capture response payload size information
distribution(
metric_prefix ++ [:http, :response, :size, :bytes],
event_name: phoenix_stop_event,
description: "The size of the HTTP response payload.",
reporter_options: [
buckets: exponential!(1, 4, 12)
],
measurement: fn _measurements, metadata ->
case metadata.conn.resp_body do
nil -> 0
_ -> :erlang.iolist_size(metadata.conn.resp_body)
end
end,
tag_values: get_conn_tags(phoenix_router),
tags: http_metrics_tags,
unit: :byte
),
# Capture the number of requests that have been serviced
counter(
metric_prefix ++ [:http, :requests, :total],
event_name: phoenix_stop_event,
description: "The number of requests have been serviced.",
tag_values: get_conn_tags(phoenix_router),
tags: http_metrics_tags
)
]
)
end
defp channel_events(metric_prefix) do
Event.build(
:phoenix_channel_event_metrics,
[
# Capture the number of channel joins that have occured
counter(
metric_prefix ++ [:channel, :joined, :total],
event_name: [:phoenix, :channel_joined],
description: "The number of channel joins that have occured.",
tag_values: fn %{result: result, socket: %Socket{transport: transport}} ->
%{
transport: transport,
result: result
}
end,
tags: [:result, :transport]
),
# Capture channel handle_in duration
distribution(
metric_prefix ++ [:channel, :handled_in, :duration, :milliseconds],
event_name: [:phoenix, :channel_handled_in],
measurement: :duration,
description: "The time it takes for the application to respond to channel messages.",
reporter_options: [
buckets: exponential!(1, 2, 12)
],
unit: {:native, :millisecond}
)
]
)
end
defp socket_events(metric_prefix) do
Event.build(
:phoenix_socket_event_metrics,
[
# Capture the number of channel joins that have occurred
counter(
metric_prefix ++ [:socket, :connected, :total],
event_name: [:phoenix, :socket_connected],
description: "The number of socket connects that have occurred.",
tag_values: fn %{result: result, transport: transport} ->
%{
transport: transport,
result: result
}
end,
tags: [:result, :transport]
),
# Capture socket_connected duration
distribution(
metric_prefix ++ [:socket, :connected, :duration, :milliseconds],
event_name: [:phoenix, :socket_connected],
measurement: :duration,
description: "The time it takes for the application to accept websocket connection.",
reporter_options: [
buckets: exponential!(1, 2, 12)
],
unit: {:native, :millisecond}
)
]
)
end
defp get_conn_tags(router) do
fn
%{conn: %Conn{} = conn} ->
router
|> Phoenix.Router.route_info(conn.method, conn.request_path, "")
|> case do
%{route: path, plug: controller, plug_opts: action} ->
%{
path: path,
controller: normalize_module_name(controller),
action: action
}
_ ->
%{
path: "Unknown",
controller: "Unknown",
action: "Unknown"
}
end
|> Map.merge(%{
status: conn.status,
method: conn.method
})
_ ->
# TODO: Change this to warning as warn is deprecated as of Elixir 1.11
Logger.warn("Could not resolve path for request")
end
end
defp normalize_module_name(name) when is_atom(name) do
name
|> Atom.to_string()
|> String.trim_leading("Elixir.")
end
defp normalize_module_name(name), do: name
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment