defmodule Phoenix.SessionProcess.Telemetry do @moduledoc """ Telemetry events for Phoenix.SessionProcess. This module provides telemetry events that can be used to monitor session lifecycle, performance, and errors. ## Events The following telemetry events are emitted: ### Session Lifecycle - `[:phoenix, :session_process, :start]` - When a session starts - `[:phoenix, :session_process, :stop]` - When a session stops - `[:phoenix, :session_process, :start_error]` - When session start fails ### Communication - `[:phoenix, :session_process, :call]` - When a call is made to a session - `[:phoenix, :session_process, :cast]` - When a cast is made to a session - `[:phoenix, :session_process, :communication_error]` - When communication fails ### Process Management - `[:phoenix, :session_process, :worker_start]` - When a worker process starts - `[:phoenix, :session_process, :worker_terminate]` - When a worker process terminates - `[:phoenix, :session_process, :session_start]` - When a session process starts - `[:phoenix, :session_process, :session_end]` - When a session process ends ### Cleanup - `[:phoenix, :session_process, :auto_cleanup]` - When a session is auto-cleaned up - `[:phoenix, :session_process, :cleanup]` - When a session is cleaned up - `[:phoenix, :session_process, :cleanup_error]` - When cleanup fails All events include the following metadata: - `session_id` - The session ID (when applicable) - `module` - The session module - `pid` - The process PID (when applicable) - `worker_spec` - Worker specification details (for worker events) - `operation` - Operation type (for error events) - `reason` - Error reason (for error events) - `measurements` - Performance measurements """ @doc """ Emits a telemetry event for session start. """ @spec emit_session_start(String.t(), atom(), pid(), keyword()) :: :ok def emit_session_start(session_id, module, pid, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :start], Map.new(measurements), %{session_id: session_id, module: module, pid: pid} ) end @doc """ Emits a telemetry event for session stop. """ @spec emit_session_stop(String.t(), atom(), pid(), keyword()) :: :ok def emit_session_stop(session_id, module, pid, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :stop], Map.new(measurements), %{session_id: session_id, module: module, pid: pid} ) end @doc """ Emits a telemetry event for session start error. """ @spec emit_session_start_error(String.t(), atom(), any(), keyword()) :: :ok def emit_session_start_error(session_id, module, reason, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :start_error], Map.new(measurements), %{session_id: session_id, module: module, reason: reason} ) end @doc """ Emits a telemetry event for session call. """ @spec emit_session_call(String.t(), atom(), pid(), any(), keyword()) :: :ok def emit_session_call(session_id, module, pid, message, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :call], Map.new(measurements), %{session_id: session_id, module: module, pid: pid, message: message} ) end @doc """ Emits a telemetry event for session cast. """ @spec emit_session_cast(String.t(), atom(), pid(), any(), keyword()) :: :ok def emit_session_cast(session_id, module, pid, message, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :cast], Map.new(measurements), %{session_id: session_id, module: module, pid: pid, message: message} ) end @doc """ Emits a telemetry event for communication error. """ @spec emit_communication_error(String.t(), atom(), any(), any(), keyword()) :: :ok def emit_communication_error(session_id, module, operation, reason, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :communication_error], Map.new(measurements), %{session_id: session_id, module: module, operation: operation, reason: reason} ) end @doc """ Emits a telemetry event for session cleanup. """ @spec emit_session_cleanup(String.t(), atom(), pid(), keyword()) :: :ok def emit_session_cleanup(session_id, module, pid, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :cleanup], Map.new(measurements), %{session_id: session_id, module: module, pid: pid} ) end @doc """ Emits a telemetry event for session cleanup error. """ @spec emit_session_cleanup_error(String.t(), atom(), any(), keyword()) :: :ok def emit_session_cleanup_error(session_id, module, reason, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :cleanup_error], Map.new(measurements), %{session_id: session_id, module: module, reason: reason} ) end @doc """ Measures the execution time of a function and emits telemetry events. """ @spec measure(String.t(), atom(), fun()) :: {:ok, any()} | {:error, any()} def measure(session_id, operation, fun) do start_time = System.monotonic_time() try do result = fun.() end_time = System.monotonic_time() duration = end_time - start_time :telemetry.execute( [:phoenix, :session_process, operation], %{duration: duration}, %{session_id: session_id} ) result rescue error -> end_time = System.monotonic_time() duration = end_time - start_time :telemetry.execute( [:phoenix, :session_process, :error], %{duration: duration}, %{session_id: session_id, operation: operation, error: error} ) reraise error, __STACKTRACE__ catch kind, reason -> end_time = System.monotonic_time() duration = end_time - start_time :telemetry.execute( [:phoenix, :session_process, :error], %{duration: duration}, %{session_id: session_id, operation: operation, kind: kind, reason: reason} ) :erlang.raise(kind, reason, __STACKTRACE__) end end @doc """ Emits a telemetry event for worker process start. """ @spec emit_worker_start(term(), keyword()) :: :ok def emit_worker_start(worker_spec, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :worker_start], Map.new(measurements), %{worker_spec: inspect(worker_spec)} ) end @doc """ Emits a telemetry event for worker process termination. """ @spec emit_worker_terminate(pid(), keyword()) :: :ok def emit_worker_terminate(pid, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :worker_terminate], Map.new(measurements), %{pid: pid} ) end @doc """ Emits a telemetry event for session process start. """ @spec emit_session_start_event(String.t(), keyword()) :: :ok def emit_session_start_event(session_id, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :session_start], Map.new(measurements), %{session_id: session_id} ) end @doc """ Emits a telemetry event for session process end. """ @spec emit_session_end_event(String.t(), keyword()) :: :ok def emit_session_end_event(session_id, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :session_end], Map.new(measurements), %{session_id: session_id} ) end @doc """ Emits a telemetry event for automatic session cleanup. """ @spec emit_auto_cleanup_event(String.t(), atom(), pid(), keyword()) :: :ok def emit_auto_cleanup_event(session_id, module, pid, measurements \\ []) do :telemetry.execute( [:phoenix, :session_process, :auto_cleanup], Map.new(measurements), %{session_id: session_id, module: module, pid: pid} ) end end