From 6e646c4cbc56b3cf8134ca9005c1818c5947dd55 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Sun, 1 Jan 2023 18:32:14 +0000 Subject: [PATCH] Use a genserver to periodically fetch metrics Ref https://github.com/beam-telemetry/telemetry_metrics_prometheus_core/issues/52 --- lib/pleroma/prometheus_exporter.ex | 49 +++++++++++++++++++ .../controllers/metrics_controller.ex | 2 +- lib/pleroma/web/telemetry.ex | 23 +++++++-- .../akkoma_api/metrics_controller_test.exs | 2 + 4 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 lib/pleroma/prometheus_exporter.ex diff --git a/lib/pleroma/prometheus_exporter.ex b/lib/pleroma/prometheus_exporter.ex new file mode 100644 index 000000000..05170c8bb --- /dev/null +++ b/lib/pleroma/prometheus_exporter.ex @@ -0,0 +1,49 @@ +defmodule Pleroma.PrometheusExporter do + @moduledoc """ + Exports metrics in Prometheus format. + Mostly exists because of https://github.com/beam-telemetry/telemetry_metrics_prometheus_core/issues/52 + Basically we need to fetch metrics every so often, or the lib will let them pile up and eventually crash the VM. + It also sorta acts as a cache so there is that too. + """ + + use GenServer + require Logger + + def start_link(_opts) do + GenServer.start_link(__MODULE__, :ok, name: __MODULE__) + end + + def init(_opts) do + schedule_next() + {:ok, ""} + end + + defp schedule_next do + Process.send_after(self(), :gather, 60_000) + end + + # Scheduled function, gather metrics and schedule next run + def handle_info(:gather, _state) do + schedule_next() + state = TelemetryMetricsPrometheus.Core.scrape() + {:noreply, state} + end + + # Trigger the call dynamically, mostly for testing + def handle_call(:gather, _from, _state) do + state = TelemetryMetricsPrometheus.Core.scrape() + {:reply, state, state} + end + + def handle_call(:show, _from, state) do + {:reply, state, state} + end + + def show do + GenServer.call(__MODULE__, :show) + end + + def gather do + GenServer.call(__MODULE__, :gather) + end +end diff --git a/lib/pleroma/web/akkoma_api/controllers/metrics_controller.ex b/lib/pleroma/web/akkoma_api/controllers/metrics_controller.ex index cc7a616ee..ab52cb64d 100644 --- a/lib/pleroma/web/akkoma_api/controllers/metrics_controller.ex +++ b/lib/pleroma/web/akkoma_api/controllers/metrics_controller.ex @@ -15,7 +15,7 @@ defmodule Pleroma.Web.AkkomaAPI.MetricsController do def show(conn, _params) do if Config.get([:instance, :export_prometheus_metrics], true) do conn - |> text(TelemetryMetricsPrometheus.Core.scrape()) + |> text(Pleroma.PrometheusExporter.show()) else conn |> send_resp(404, "Not Found") diff --git a/lib/pleroma/web/telemetry.ex b/lib/pleroma/web/telemetry.ex index acb649421..b03850600 100644 --- a/lib/pleroma/web/telemetry.ex +++ b/lib/pleroma/web/telemetry.ex @@ -2,6 +2,7 @@ defmodule Pleroma.Web.Telemetry do use Supervisor import Telemetry.Metrics alias Pleroma.Stats + alias Pleroma.Config def start_link(arg) do Supervisor.start_link(__MODULE__, arg, name: __MODULE__) @@ -9,14 +10,28 @@ def start_link(arg) do @impl true def init(_arg) do - children = [ - {:telemetry_poller, measurements: periodic_measurements(), period: 10_000}, - {TelemetryMetricsPrometheus.Core, metrics: prometheus_metrics()} - ] + children = + [ + {:telemetry_poller, measurements: periodic_measurements(), period: 10_000} + ] ++ + prometheus_children() Supervisor.init(children, strategy: :one_for_one) end + defp prometheus_children do + config = Config.get([:instance, :export_prometheus_metrics], true) + + if config do + [ + {TelemetryMetricsPrometheus.Core, metrics: prometheus_metrics()}, + Pleroma.PrometheusExporter + ] + else + [] + end + end + # A seperate set of metrics for distributions because phoenix dashboard does NOT handle them well defp distribution_metrics do [ diff --git a/test/pleroma/web/akkoma_api/metrics_controller_test.exs b/test/pleroma/web/akkoma_api/metrics_controller_test.exs index 9482f1312..4b096237c 100644 --- a/test/pleroma/web/akkoma_api/metrics_controller_test.exs +++ b/test/pleroma/web/akkoma_api/metrics_controller_test.exs @@ -5,6 +5,8 @@ defmodule Pleroma.Web.AkkomaAPI.MetricsControllerTest do test "should return metrics when the user has admin:metrics" do %{conn: conn} = oauth_access(["admin:metrics"]) + Pleroma.PrometheusExporter.gather() + resp = conn |> get("/api/v1/akkoma/metrics")