mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-12 02:35:42 +00:00
206 lines
5.8 KiB
Elixir
206 lines
5.8 KiB
Elixir
defmodule WandererApp.Telemetry do
|
|
@moduledoc """
|
|
OpenTelemetry instrumentation for API monitoring and observability.
|
|
|
|
This module sets up comprehensive telemetry for:
|
|
- HTTP request/response metrics
|
|
- Database query performance
|
|
- Phoenix LiveView events
|
|
- Custom API metrics for performance baseline
|
|
"""
|
|
|
|
require Logger
|
|
|
|
@doc """
|
|
Sets up additional telemetry for API monitoring.
|
|
Integrates with existing PromEx and telemetry infrastructure.
|
|
"""
|
|
def setup do
|
|
Logger.info("Setting up API telemetry monitoring")
|
|
|
|
# Set up custom API metrics that integrate with existing telemetry
|
|
setup_api_metrics()
|
|
|
|
Logger.info("API telemetry setup complete")
|
|
end
|
|
|
|
# Sets up custom metrics specifically for API performance monitoring.
|
|
# These metrics will help establish baseline performance for the legacy API
|
|
# and monitor the new JSON:API endpoints.
|
|
defp setup_api_metrics do
|
|
# API request duration histogram
|
|
:telemetry.attach(
|
|
"api-request-duration",
|
|
[:phoenix, :endpoint, :stop],
|
|
&handle_api_request/4,
|
|
%{}
|
|
)
|
|
|
|
# Custom API endpoint metrics
|
|
:telemetry.attach_many(
|
|
"api-custom-metrics",
|
|
[
|
|
[:wanderer_app, :api, :request, :start],
|
|
[:wanderer_app, :api, :request, :stop],
|
|
[:wanderer_app, :api, :request, :exception]
|
|
],
|
|
&handle_custom_api_metrics/4,
|
|
%{}
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Handles Phoenix request metrics, specifically filtering for API endpoints.
|
|
"""
|
|
def handle_api_request(_event, measurements, metadata, _config) do
|
|
# Only track API endpoints
|
|
if is_api_endpoint?(metadata) do
|
|
duration_ms = System.convert_time_unit(measurements.duration, :native, :millisecond)
|
|
|
|
# Log API request metrics (integrates with existing logging infrastructure)
|
|
Logger.info("API request completed",
|
|
method: metadata.method,
|
|
route: metadata.route,
|
|
status: metadata.status,
|
|
duration_ms: duration_ms,
|
|
api_version: get_api_version(metadata.route),
|
|
endpoint: normalize_endpoint(metadata.route)
|
|
)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Handles custom API metrics for detailed performance monitoring.
|
|
"""
|
|
def handle_custom_api_metrics(event, measurements, metadata, _config) do
|
|
case event do
|
|
[:wanderer_app, :api, :request, :start] ->
|
|
Process.put(:api_request_active, true)
|
|
Process.put(:current_api_endpoint, metadata.endpoint)
|
|
|
|
[:wanderer_app, :api, :request, :stop] ->
|
|
duration_ms = System.convert_time_unit(measurements.duration, :native, :millisecond)
|
|
|
|
Logger.info("API endpoint completed",
|
|
endpoint: metadata.endpoint,
|
|
version: metadata.version,
|
|
controller: metadata.controller,
|
|
action: metadata.action,
|
|
duration_ms: duration_ms
|
|
)
|
|
|
|
Process.delete(:api_request_active)
|
|
Process.delete(:current_api_endpoint)
|
|
|
|
[:wanderer_app, :api, :request, :exception] ->
|
|
Logger.error("API endpoint error",
|
|
endpoint: metadata.endpoint,
|
|
version: metadata.version,
|
|
error_type: metadata.error_type
|
|
)
|
|
|
|
Process.delete(:api_request_active)
|
|
Process.delete(:current_api_endpoint)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Helper function to emit custom API telemetry events.
|
|
Use this in controllers to track specific API operations.
|
|
"""
|
|
def track_api_request(endpoint, version, controller, action, fun) do
|
|
start_time = System.monotonic_time()
|
|
|
|
metadata = %{
|
|
endpoint: endpoint,
|
|
version: version,
|
|
controller: controller,
|
|
action: action
|
|
}
|
|
|
|
:telemetry.execute(
|
|
[:wanderer_app, :api, :request, :start],
|
|
%{system_time: System.system_time()},
|
|
metadata
|
|
)
|
|
|
|
try do
|
|
result = fun.()
|
|
|
|
duration = System.monotonic_time() - start_time
|
|
|
|
:telemetry.execute(
|
|
[:wanderer_app, :api, :request, :stop],
|
|
%{duration: duration},
|
|
metadata
|
|
)
|
|
|
|
result
|
|
rescue
|
|
error ->
|
|
:telemetry.execute(
|
|
[:wanderer_app, :api, :request, :exception],
|
|
%{},
|
|
Map.put(metadata, :error_type, error.__struct__)
|
|
)
|
|
|
|
reraise error, __STACKTRACE__
|
|
end
|
|
end
|
|
|
|
# Private helper functions
|
|
|
|
defp is_api_endpoint?(metadata) do
|
|
route = metadata[:route] || ""
|
|
String.starts_with?(route, "/api/")
|
|
end
|
|
|
|
defp get_api_version(route) do
|
|
cond do
|
|
String.starts_with?(route, "/api/v1/") -> "v1"
|
|
String.starts_with?(route, "/api/") -> "legacy"
|
|
true -> "unknown"
|
|
end
|
|
end
|
|
|
|
defp normalize_endpoint(route) do
|
|
# Normalize route parameters for consistent grouping
|
|
route
|
|
|> String.replace(~r/\/:[^\/]+/, "/:id")
|
|
|> String.replace(~r/\/\d+/, "/:id")
|
|
end
|
|
|
|
@doc """
|
|
Performance baseline measurement functions.
|
|
These will help establish current API performance metrics.
|
|
"""
|
|
def measure_endpoint_performance(endpoint_name, iterations \\ 100) do
|
|
Logger.info("Starting performance baseline measurement for #{endpoint_name}")
|
|
|
|
results =
|
|
Enum.map(1..iterations, fn _i ->
|
|
start_time = System.monotonic_time()
|
|
# Placeholder for actual endpoint calls
|
|
# This would be implemented with actual HTTP calls to existing endpoints
|
|
duration = System.monotonic_time() - start_time
|
|
System.convert_time_unit(duration, :native, :millisecond)
|
|
end)
|
|
|
|
avg_duration = Enum.sum(results) / length(results)
|
|
max_duration = Enum.max(results)
|
|
min_duration = Enum.min(results)
|
|
|
|
baseline = %{
|
|
endpoint: endpoint_name,
|
|
iterations: iterations,
|
|
avg_duration_ms: avg_duration,
|
|
max_duration_ms: max_duration,
|
|
min_duration_ms: min_duration,
|
|
measured_at: DateTime.utc_now()
|
|
}
|
|
|
|
Logger.info("Performance baseline for #{endpoint_name}: #{inspect(baseline)}")
|
|
baseline
|
|
end
|
|
end
|