From ec6ee11ea7e23ddcfeedc365a33962001456d32f Mon Sep 17 00:00:00 2001 From: Josh Adams Date: Thu, 27 Oct 2022 19:47:37 -0500 Subject: [PATCH] Add most (all?) the promex setup --- config/dev.exs | 7 ++ config/prod.exs | 14 ++++ lib/pleroma/application.ex | 1 + lib/pleroma/prom_ex.ex | 90 ++++++++++++++++++++++ lib/pleroma/web/endpoint.ex | 6 ++ lib/pleroma/web/plugs/metrics_predicate.ex | 29 +++++++ mix.exs | 1 + mix.lock | 1 + 8 files changed, 149 insertions(+) create mode 100644 lib/pleroma/prom_ex.ex create mode 100644 lib/pleroma/web/plugs/metrics_predicate.ex diff --git a/config/dev.exs b/config/dev.exs index ab3e83c120..c34d4c5c53 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -58,6 +58,13 @@ # https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects config :phoenix, :plug_init_mode, :runtime +config :pleroma, Pleroma.PromEx, + disabled: false, + manual_metrics_start_delay: :no_delay, + drop_metrics_groups: [], + grafana: :disabled, + metrics_server: :disabled + if File.exists?("./config/dev.secret.exs") do import_config "dev.secret.exs" else diff --git a/config/prod.exs b/config/prod.exs index e35c26373c..67fdab05a9 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -23,6 +23,20 @@ config :logger, :console, level: :info config :logger, :ex_syslogger, level: :info +# PromEx set up +config :pleroma, Pleroma.Web.Plugs.MetricsPredicate, + auth_token: System.fetch_env!("PROMETHEUS_AUTH_TOKEN") + +config :pleroma, Pleroma.PromEx, + prometheus_data_source_id: System.fetch_env!("PROMETHEUS_DATASOURCE_ID"), + grafana: [ + host: System.fetch_env!("GRAFANA_HOST"), + auth_token: System.fetch_env!("GRAFANA_AUTH_TOKEN"), + upload_dashboards_on_start: true, + folder_name: "Pleroma - PromEx", + annotate_app_lifecycle: true + ] + # ## SSL Support # # To get SSL working, you will need to add the `https` key diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 57bc9618ee..5e0b1ebfa9 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -94,6 +94,7 @@ def start(_type, _args) do # Define workers and child supervisors to be supervised children = [ + Pleroma.PromEx, Pleroma.Repo, Config.TransferTask, Pleroma.Emoji, diff --git a/lib/pleroma/prom_ex.ex b/lib/pleroma/prom_ex.ex new file mode 100644 index 0000000000..09e290f20b --- /dev/null +++ b/lib/pleroma/prom_ex.ex @@ -0,0 +1,90 @@ +defmodule Pleroma.PromEx do + @moduledoc """ + Be sure to add the following to finish setting up PromEx: + + 1. Update your configuration (config.exs, dev.exs, prod.exs, releases.exs, etc) to + configure the necessary bit of PromEx. Be sure to check out `PromEx.Config` for + more details regarding configuring PromEx: + ``` + config :pleroma, Pleroma.PromEx, + disabled: false, + manual_metrics_start_delay: :no_delay, + drop_metrics_groups: [], + grafana: :disabled, + metrics_server: :disabled + ``` + + 2. Add this module to your application supervision tree. It should be one of the first + things that is started so that no Telemetry events are missed. For example, if PromEx + is started after your Repo module, you will miss Ecto's init events and the dashboards + will be missing some data points: + ``` + def start(_type, _args) do + children = [ + Pleroma.PromEx, + + ... + ] + + ... + end + ``` + + 3. Update your `endpoint.ex` file to expose your metrics (or configure a standalone + server using the `:metrics_server` config options). Be sure to put this plug before + your `Plug.Telemetry` entry so that you can avoid having calls to your `/metrics` + endpoint create their own metrics and logs which can pollute your logs/metrics given + that Prometheus will scrape at a regular interval and that can get noisy: + ``` + defmodule PleromaWeb.Endpoint do + use Phoenix.Endpoint, otp_app: :pleroma + + ... + + plug PromEx.Plug, prom_ex_module: Pleroma.PromEx + + ... + end + ``` + + 4. Update the list of plugins in the `plugins/0` function return list to reflect your + application's dependencies. Also update the list of dashboards that are to be uploaded + to Grafana in the `dashboards/0` function. + """ + + use PromEx, otp_app: :pleroma + + alias PromEx.Plugins + + @impl true + def plugins do + [ + # PromEx built in plugins + Plugins.Application, + Plugins.Beam, + {Plugins.Phoenix, router: Pleroma.Web.Router, endpoint: Pleroma.Web.Endpoint}, + Plugins.Ecto, + Plugins.Oban + ] + end + + @impl true + def dashboard_assigns do + [ + datasource_id: "Prometheus", + default_selected_interval: "1m" + ] + end + + @impl true + def dashboards do + [ + # PromEx built in Grafana dashboards + {:prom_ex, "application.json"}, + {:prom_ex, "beam.json"}, + {:prom_ex, "phoenix.json"}, + {:prom_ex, "ecto.json"}, + {:prom_ex, "oban.json"} + ] + end +end diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index d8d40cceb6..870fe6e80f 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -12,6 +12,12 @@ defmodule Pleroma.Web.Endpoint do socket("/socket", Pleroma.Web.UserSocket) socket("/live", Phoenix.LiveView.Socket) + plug(Unplug, + if: Pleroma.Web.Plugs.MetricsPredicate, + do: {PromEx.Plug, path: "/api/metrics", prom_ex_module: Pleroma.PromEx} + ) + + plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint]) plug(Pleroma.Web.Plugs.SetLocalePlug) diff --git a/lib/pleroma/web/plugs/metrics_predicate.ex b/lib/pleroma/web/plugs/metrics_predicate.ex new file mode 100644 index 0000000000..70dbd21fe6 --- /dev/null +++ b/lib/pleroma/web/plugs/metrics_predicate.ex @@ -0,0 +1,29 @@ +defmodule Pleroma.Web.Plugs.MetricsPredicate do + @moduledoc """ + This Unplug predicate is used to authorize requests to the PromEx metrics + """ + + @behaviour Unplug.Predicate + + @impl true + def call(conn, _) do + conn + |> Plug.Conn.get_req_header("authorization") + |> case do + ["Bearer " <> token] -> + token == get_configured_auth_token() + + [] -> + get_configured_auth_token() == :disabled + + _ -> + false + end + end + + defp get_configured_auth_token do + :pleroma + |> Application.get_env(__MODULE__, auth_token: "super_secret") + |> Keyword.get(:auth_token) + end +end diff --git a/mix.exs b/mix.exs index e12b52ebf9..43af0c33e3 100644 --- a/mix.exs +++ b/mix.exs @@ -205,6 +205,7 @@ defp deps do {:phoenix_live_dashboard, "~> 0.6.2"}, {:ecto_psql_extras, "~> 0.6"}, {:prom_ex, "~> 1.7.1"}, + {:unplug, "~> 1.0"}, # indirect dependency version override {:plug, "~> 1.10.4", override: true}, diff --git a/mix.lock b/mix.lock index 6930b5c12a..32ed479e9d 100644 --- a/mix.lock +++ b/mix.lock @@ -151,6 +151,7 @@ "ueberauth_slack": {:hex, :ueberauth_slack, "0.3.0", "ec8f6c96e1d41a458a00b5d8cd918c1f387998884fe4636e67dd88b1bd06f928", [:mix], [{:oauth2, "~> 0.5", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.2", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "b4012b2cf1664dc80eda5762c5d3a495a819456186ab789da8ce4e7b9c0fa22f"}, "ueberauth_twitter": {:hex, :ueberauth_twitter, "0.4.0", "4b98620341bc91bac90459093bba093c650823b6e2df35b70255c493c17e9227", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:oauther, "~> 1.1", [hex: :oauther, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.6", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "fb29c9047ca263038c0c61f5a0ec8597e8564aba3f2b4cb02704b60205fd4468"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, + "unplug": {:hex, :unplug, "1.0.0", "8ec2479de0baa9a6283c04a1cc616c5ca6c5b80b8ff1d857481bb2943368dbbc", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d171a85758aa412d4e85b809c203e1b1c4c76a4d6ab58e68dc9a8a8acd9b7c3a"}, "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"}, "web_push_encryption": {:hex, :web_push_encryption, "0.3.1", "76d0e7375142dfee67391e7690e89f92578889cbcf2879377900b5620ee4708d", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.1", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "4f82b2e57622fb9337559058e8797cb0df7e7c9790793bdc4e40bc895f70e2a2"}, "websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []},