Use a genserver to periodically fetch metrics
authorFloatingGhost <hannah@coffee-and-dreams.uk>
Sun, 1 Jan 2023 18:32:14 +0000 (18:32 +0000)
committerFloatingGhost <hannah@coffee-and-dreams.uk>
Sun, 1 Jan 2023 18:32:14 +0000 (18:32 +0000)
Ref https://github.com/beam-telemetry/telemetry_metrics_prometheus_core/issues/52

lib/pleroma/prometheus_exporter.ex [new file with mode: 0644]
lib/pleroma/web/akkoma_api/controllers/metrics_controller.ex
lib/pleroma/web/telemetry.ex
test/pleroma/web/akkoma_api/metrics_controller_test.exs

diff --git a/lib/pleroma/prometheus_exporter.ex b/lib/pleroma/prometheus_exporter.ex
new file mode 100644 (file)
index 0000000..05170c8
--- /dev/null
@@ -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
index cc7a616ee6f30fcd598acd65ba61087965d80a9c..ab52cb64de28e3d41a3c8fd7e5dfaa562e8bd666 100644 (file)
@@ -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")
index acb649421a8a8a217bd58eb2681dbd5607fc46f9..b0385060004cebed9b1083adec11e626d9feccea 100644 (file)
@@ -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 @@ defmodule Pleroma.Web.Telemetry 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
     [
index 9482f1312d250e39348a2183e81565a1bcfc6ea5..4b096237c7ef9a545a4b2c0fde931ce5f5c94af2 100644 (file)
@@ -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")