diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 51616fc..46117d2 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -7,7 +7,7 @@ services: dockerfile: Dockerfile args: # Elixir Version: 1.9, 1.10, 1.10.4, ... - VARIANT: '1.15.4' + VARIANT: '1.15.5' # Phoenix Version: 1.4.17, 1.5.4, ... PHOENIX_VERSION: '1.7.7' # Node Version: 12, 14, ... @@ -22,7 +22,7 @@ services: command: sleep infinity db: - image: postgres:15.3-bullseye + image: postgres:15.4-bullseye restart: unless-stopped volumes: - postgres-data:/var/lib/postgresql/data diff --git a/config/config.exs b/config/config.exs index bf5e865..9e3666a 100644 --- a/config/config.exs +++ b/config/config.exs @@ -48,6 +48,31 @@ if Mix.env() == :dev do ] end +# Configures prom_ex +config :zero_phoenix, ZeroPhoenix.PromEx, + disabled: false, + manual_metrics_start_delay: :no_delay, + drop_metrics_groups: [], + grafana: [ + host: System.get_env("GRAFANA_HOST", "http://localhost:3000"), + username: System.get_env("GF_SECURITY_ADMIN_USER", "admin"), + password: System.get_env("GF_SECURITY_ADMIN_PASSWORD", "admin"), + folder_name: "zero-to-graphql-using-elixir", + upload_dashboards_on_start: true, + annotate_app_lifecycle: true + ], + metrics_server: [ + protocol: :http, + path: "/metrics", + port: 4021, + pool_size: 5, + cowboy_opts: [], + auth_strategy: :none + ] + +# config :zero_phoenix, ZeroPhoenix.PromEx, +# grafana_datasource_id: System.get_env("GRAFANA_DATASOURCE_ID", "Local Prometheus") + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{config_env()}.exs" diff --git a/config/prod.exs b/config/prod.exs index 5f0096b..3e79032 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -12,3 +12,25 @@ config :logger, level: :info # Runtime production configuration, including reading # of environment variables, is done on config/runtime.exs. + +# Configures prom_ex +config :zero_phoenix, ZeroPhoenix.PromEx, + disabled: false, + manual_metrics_start_delay: :no_delay, + drop_metrics_groups: [], + grafana: [ + host: System.get_env("GRAFANA_HOST", raise("GRAFANA_HOST is required")), + auth_token: System.get_env("GRAFANA_TOKEN", raise("GRAFANA_TOKEN is required")), + folder_name: "zero-to-graphql-using-elixir", + upload_dashboards_on_start: true, + annotate_app_lifecycle: true + ], + metrics_server: [ + protocol: :http, + path: "/metrics", + port: 4021, + pool_size: 5, + cowboy_opts: [] + auth_strategy: :bearer, + auth_token: "", + ] diff --git a/lib/zero_phoenix/application.ex b/lib/zero_phoenix/application.ex index 29904cf..86cb2f4 100644 --- a/lib/zero_phoenix/application.ex +++ b/lib/zero_phoenix/application.ex @@ -7,16 +7,18 @@ defmodule ZeroPhoenix.Application do def start(_type, _args) do children = [ - # Start the Ecto repository - ZeroPhoenix.Repo, + # Start the PromEx + ZeroPhoenix.PromEx, # Start the Telemetry supervisor ZeroPhoenixWeb.Telemetry, + # Start the Ecto repository + ZeroPhoenix.Repo, # Start the PubSub system {Phoenix.PubSub, name: ZeroPhoenix.PubSub}, - # Start the endpoint when the application starts + # Start Finch + {Finch, name: ZeroPhoenix.Finch}, + # Start the Endpoint (http/https) ZeroPhoenixWeb.Endpoint - # Start your own worker by calling: ZeroPhoenix.Worker.start_link(arg1, arg2, arg3) - # worker(ZeroPhoenix.Worker, [arg1, arg2, arg3]), ] # See https://hexdocs.pm/elixir/Supervisor.html diff --git a/lib/zero_phoenix/prom_ex.ex b/lib/zero_phoenix/prom_ex.ex new file mode 100644 index 0000000..aecf91b --- /dev/null +++ b/lib/zero_phoenix/prom_ex.ex @@ -0,0 +1,103 @@ +defmodule ZeroPhoenix.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 :zero_phoenix, ZeroPhoenix.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 = [ + ZeroPhoenix.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 ZeroPhoenixWeb.Endpoint do + use Phoenix.Endpoint, otp_app: :zero_phoenix + + ... + + plug PromEx.Plug, prom_ex_module: ZeroPhoenix.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: :zero_phoenix + + alias PromEx.Plugins + + @impl true + def plugins do + [ + # PromEx built in plugins + Plugins.Application, + Plugins.Beam, + {Plugins.Phoenix, router: ZeroPhoenixWeb.Router, endpoint: ZeroPhoenixWeb.Endpoint}, + Plugins.Ecto, + # Plugins.Oban, + Plugins.PhoenixLiveView, + Plugins.Absinthe + # Plugins.Broadway, + + # Add your own PromEx metrics plugins + # ZeroPhoenix.Users.PromExPlugin + ] + end + + @impl true + def dashboard_assigns do + [ + datasource_id: "Local Prometheus", + # datasource_id: Application.get_env(:zero_phoenix, :grafana_datasource_id), + default_selected_interval: "30s" + ] + 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"}, + {:prom_ex, "phoenix_live_view.json"}, + {:prom_ex, "absinthe.json"} + # {:prom_ex, "broadway.json"}, + + # Add your dashboard definitions here with the format: {:otp_app, "path_in_priv"} + # {:zero_phoenix, "/grafana_dashboards/user_metrics.json"} + ] + end +end diff --git a/lib/zero_phoenix_web/endpoint.ex b/lib/zero_phoenix_web/endpoint.ex index e210d31..39870dc 100644 --- a/lib/zero_phoenix_web/endpoint.ex +++ b/lib/zero_phoenix_web/endpoint.ex @@ -35,6 +35,7 @@ defmodule ZeroPhoenixWeb.Endpoint do cookie_key: "request_logger" plug Plug.RequestId + plug PromEx.Plug, prom_ex_module: ZeroPhoenix.PromEx plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] plug Plug.Parsers,