diff --git a/lib/dotcom/application.ex b/lib/dotcom/application.ex index 622fb7b33e..07b829afcb 100644 --- a/lib/dotcom/application.ex +++ b/lib/dotcom/application.ex @@ -26,15 +26,14 @@ defmodule Dotcom.Application do children = [ - {Application.get_env(:dotcom, :cache, Dotcom.Cache.Multilevel), []}, - {Dotcom.Telemetry, []}, - {DotcomWeb.Telemetry, []}, - {Req.Telemetry, []} + {Application.get_env(:dotcom, :cache, Dotcom.Cache.Multilevel), []} ] ++ if Application.get_env(:dotcom, :env) != :test do [ - # We can't run cache telemetry in the test environment because none of the levels are running + {Dotcom.Telemetry, []}, {Dotcom.Cache.Telemetry, []}, + {DotcomWeb.Telemetry, []}, + {Req.Telemetry, []}, # We don't need to run this cache because we are using the local cache for tests {Dotcom.Cache.TripPlanFeedback.Cache, []} ] diff --git a/lib/dotcom/telemetry.ex b/lib/dotcom/telemetry.ex index 384f822d6c..456a94e914 100644 --- a/lib/dotcom/telemetry.ex +++ b/lib/dotcom/telemetry.ex @@ -1,15 +1,23 @@ defmodule Dotcom.Telemetry do @moduledoc """ + This Supervisor establishes sends vm stats to the `TelemetryMetricsSplunk` reporter. + No polling occurs as these metrics are emitted regularly anyway. """ use Supervisor alias Telemetry.Metrics + @doc """ + Starts the supervisor. + """ def start_link(arg) do Supervisor.start_link(__MODULE__, arg, name: __MODULE__) end + @doc """ + Initializes the supervisor. + """ def init(_arg) do telemetry_metrics_splunk_config = Application.get_env(:dotcom, :telemetry_metrics_splunk) diff --git a/lib/dotcom_web/telemetry.ex b/lib/dotcom_web/telemetry.ex index f5c0874b11..ff640e5af8 100644 --- a/lib/dotcom_web/telemetry.ex +++ b/lib/dotcom_web/telemetry.ex @@ -1,16 +1,23 @@ defmodule DotcomWeb.Telemetry do @moduledoc """ - This module is responsible for starting the Telemetry poller and defining the metrics to be collected for Phoenix and the Erlang VM. + This Supervisor is responsible for starting the Telemetry poller and defining the metrics to be collected for Phoenix. + We poll the metrics every 60 seconds and send them to the `TelemetryMetricsSplunk` reporter. """ use Supervisor alias Telemetry.Metrics + @doc """ + Starts the supervisor. + """ def start_link(arg) do Supervisor.start_link(__MODULE__, arg, name: __MODULE__) end + @doc """ + Initializes the supervisor. + """ def init(_arg) do telemetry_metrics_splunk_config = Application.get_env(:dotcom, :telemetry_metrics_splunk) diff --git a/lib/req/stats.ex b/lib/req/stats.ex index b04ad29c33..c7960a27c8 100644 --- a/lib/req/stats.ex +++ b/lib/req/stats.ex @@ -14,6 +14,15 @@ defmodule Req.Stats do Agent.start_link(fn -> initial_value end, name: __MODULE__) end + @doc """ + Stops the Agent and detaches from `[:finch, :recv, :stop]` telemetry events. + """ + def stop() do + :telemetry.detach("finch-recv-stop") + + Agent.stop(__MODULE__) + end + @doc """ Handles telemetry events and aggregates them by host, path, and status. """ diff --git a/lib/telemetry/helper.ex b/lib/telemetry/helper.ex index f0d4b1d503..ef82029d9a 100644 --- a/lib/telemetry/helper.ex +++ b/lib/telemetry/helper.ex @@ -37,6 +37,9 @@ defmodule Telemetry.Helper do :dbg.tp(:telemetry, :execute, 3, []) end + @doc """ + Stop the tracer and therefore stop listening for telemetry events. + """ def stop do :dbg.stop() end diff --git a/test/dotcom/cache/telemetry/reporter_test.exs b/test/dotcom/cache/telemetry/reporter_test.exs deleted file mode 100644 index 61ad3a00e4..0000000000 --- a/test/dotcom/cache/telemetry/reporter_test.exs +++ /dev/null @@ -1,37 +0,0 @@ -defmodule Dotcom.Cache.Telemetry.ReporterTest do - use ExUnit.Case, async: true - - import ExUnit.CaptureLog - - alias Dotcom.Cache.Multilevel.Local - alias Dotcom.Cache.Telemetry.Reporter - - test "the hit rate gets logged" do - Reporter.start_link( - metrics: [Telemetry.Metrics.last_value("dotcom.cache.multilevel.l1.stats.updates")] - ) - - assert capture_log(fn -> - :telemetry.execute( - [:dotcom, :cache, :multilevel, :l1, :stats], - %{hits: 99, misses: 1}, - %{cache: Local} - ) - end) =~ "hit_rate=0.99" - end - - test "cache command exceptions get logged" do - Reporter.start_link(metrics: []) - - assert capture_log(fn -> - :telemetry.execute( - [:dotcom, :cache, :multilevel, :l2, :command, :exception], - %{duration: 0}, - %{ - kind: :error, - reason: %Redix.ConnectionError{reason: :closed} - } - ) - end) =~ "dotcom.cache.multilevel.l2.command.exception kind=error reason=closed" - end -end diff --git a/test/dotcom_web/stats_test.exs b/test/dotcom_web/stats_test.exs new file mode 100644 index 0000000000..f246cb1e35 --- /dev/null +++ b/test/dotcom_web/stats_test.exs @@ -0,0 +1,49 @@ +defmodule DotcomWeb.StatsTest do + use ExUnit.Case + + import TelemetryTest + + alias DotcomWeb.Stats + + setup [:telemetry_listen] + + setup do + {:ok, _} = Stats.start_link() + + :ok + end + + @tag telemetry_listen: [:dotcom_web, :request] + test "aggregates and dispatches stats" do + # Setup + duration = :rand.uniform(1_000_000_000) + + measurement = %{ + duration: duration + } + + metadata = %{ + conn: %{ + method: Enum.random(["GET", "POST", "PUT", "DELETE"]), + status: Enum.random([200, 404, 500]) + }, + route: "/#{Faker.Internet.slug()}/" + } + + # Exercise + :telemetry.execute([:phoenix, :router_dispatch, :stop], measurement, metadata) + :telemetry.execute([:phoenix, :router_dispatch, :stop], measurement, metadata) + + Stats.dispatch_stats() + + # Verify + assert_receive { + :telemetry_event, + %{ + event: [:dotcom_web, :request], + measurements: %{avg: _duration, count: 2}, + metadata: %{method: _host, path: _path, status: _status} + } + } + end +end diff --git a/test/mbta/api/stats_test.exs b/test/req/stats_test.exs similarity index 67% rename from test/mbta/api/stats_test.exs rename to test/req/stats_test.exs index 49b1caae4c..d8cdb23730 100644 --- a/test/mbta/api/stats_test.exs +++ b/test/req/stats_test.exs @@ -1,9 +1,9 @@ -defmodule MBTA.Api.StatsTest do +defmodule Req.StatsTest do use ExUnit.Case import TelemetryTest - alias MBTA.Api.Stats + alias Req.Stats setup [:telemetry_listen] @@ -13,10 +13,9 @@ defmodule MBTA.Api.StatsTest do :ok end - @tag telemetry_listen: [:mbta_api, :request] + @tag telemetry_listen: [:req, :request] test "aggregates and dispatches stats" do # Setup - # 1 second in nanoseconds duration = :rand.uniform(1_000_000_000) measurement = %{ @@ -25,7 +24,8 @@ defmodule MBTA.Api.StatsTest do metadata = %{ request: %{ - path: "/#{Faker.Team.creature()}/" + host: Faker.Internet.domain_name(), + path: "/#{Faker.Internet.slug()}/" }, status: Enum.random([200, 404, 500]) } @@ -40,9 +40,9 @@ defmodule MBTA.Api.StatsTest do assert_receive { :telemetry_event, %{ - event: [:mbta_api, :request], - measurements: %{avg: ^duration, count: 2}, - metadata: %{path: _path, status: _status} + event: [:req, :request], + measurements: %{avg: _duration, count: 2}, + metadata: %{host: _host, path: _path, status: _status} } } end