├── .DS_Store ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── assets └── alchemetrics_web.png ├── config ├── config.exs ├── dev.exs └── test.exs ├── lib ├── alchemetrics_web.ex ├── metric_collectors │ ├── ecto.ex │ └── phoenix_request.ex ├── metric_metadata │ └── request.ex └── phoenix_plug.ex ├── mix.exs ├── mix.lock └── test ├── metric_collectors ├── ecto_test.exs └── phoenix_request_test.exs ├── metric_metadata └── request_test.exs ├── support └── alchemetrics_mocker.ex └── test_helper.exs /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globocom/alchemetrics_web/c06da7b2f08026c0ba49294e59c778fbd86f0815/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Generated log folder. 23 | /log/ 24 | 25 | # ASDF files 26 | .elixir_ls/ 27 | .tool-versions 28 | .tool-versions-e -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: elixir 2 | elixir: 3 | - 1.9.0 4 | - 1.8.2 5 | otp_release: 6 | - 22.0 7 | - 21.0 8 | - 20.0 9 | 10 | matrix: 11 | include: 12 | - elixir: 1.5.2 13 | otp_release: 20.0 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Globo.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Alchemetrics Web

2 | 3 |

4 | Alchemetrics Web 5 |

6 | 7 |

8 | Metrics for phoenix and ecto. 9 |

10 | 11 |

12 | 13 | Travis 14 | 15 | 16 | Hex 17 | 18 |

19 | 20 | ## Installation 21 | 22 | If [available in Hex](https://hex.pm/docs/publish), the package can be installed 23 | by adding `alchemetrics_web` to your list of dependencies in `mix.exs`: 24 | 25 | ```elixir 26 | def deps do 27 | [ 28 | {:alchemetrics_web, "~> 0.5.4"} 29 | ] 30 | end 31 | ``` 32 | 33 | Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) 34 | and published on [HexDocs](https://hexdocs.pm). Once published, the docs can 35 | be found at [https://hexdocs.pm/alchemetrics_web](https://hexdocs.pm/alchemetrics_web). 36 | 37 | ## Usage 38 | 39 | 1) Add alchemetris_web to your application list in mix.exs file: 40 | ```elixir 41 | def application do 42 | ... 43 | applications: [:phoenix, ..., :alchemetrics_web] 44 | ... 45 | end 46 | ``` 47 | 48 | 2) Plug AlchemetricsWeb to your endpoint: 49 | ```elixir 50 | defmodule MyApp.Endpoint do 51 | use Phoenix.Endpoint, otp_app: ... 52 | use AlchemetricsWeb 53 | end 54 | ``` 55 | 56 | ```bash 57 | %{datapoint: :last_interval, request_count: %{action: :index, controller: "page_controller", type: "controller"}, value: 1} 58 | %{datapoint: :total, request_count: %{action: :index, controller: "page_controller", type: "controller"}, value: 1} 59 | 60 | %{datapoint: :avg, request_time: %{action: :index, controller: "page_controller", type: "controller"}, value: 192090} 61 | %{datapoint: :max, request_time: %{action: :index, controller: "page_controller", type: "controller"}, value: 192090} 62 | %{datapoint: :min, request_time: %{action: :index, controller: "page_controller", type: "controller"}, value: 192090} 63 | %{datapoint: :p95, request_time: %{action: :index, controller: "page_controller", type: "controller"}, value: 192090} 64 | %{datapoint: :p99, request_time: %{action: :index, controller: "page_controller", type: "controller"}, value: 192090} 65 | %{datapoint: :total, request_time: %{action: :index, controller: "page_controller", type: "controller"}, value: 192090} 66 | %{datapoint: :last_interval, request_time: %{action: :index, controller: "page_controller",type: "controller"}, value: 192090} 67 | ``` 68 | 69 | 3) To collect and report ecto query metrics, config ecto logger in config.exs: 70 | ```elixir 71 | config :my_app, MyApp.Repo, ..., 72 | loggers: [..., {AlchemetricsWeb.Collectors.Ecto, :collect, []}] 73 | ``` 74 | 75 | Ecto collector will report the follow data: 76 | ```bash 77 | %{datapoint: :total, options: [], query_count: %{type: "database.ecto"}, value: 1} 78 | %{datapoint: :total, name: "queue_time", options: [], type: "database.ecto", value: 46} 79 | ``` 80 | #### Custom reporter 81 | Alchemetrics automatically schedule collected request and query metrics, but its 82 | possible to customize the way how the metrics is reported. In order to do that 83 | you must implement modules to receive the collected metrics and set this up in 84 | your config file. The follow example shows how can you do that: 85 | 86 | ```elixir 87 | defmodule MyApp.CustomPhoenixReporter do 88 | def report(value, metadata) do 89 | # value is the reported metric value. 90 | # metadata is a keyword list. 91 | end 92 | 93 | def increment(metadata) do 94 | # metadata is a keywork list. 95 | end 96 | end 97 | ``` 98 | In config.exs 99 | ```elixir 100 | config :alchemetrics_web, :custom_request_reporter, MyApp.CustomPhoenixReporter 101 | ``` 102 | 103 | To create a custom reporter for ecto queries just replace :custom_request_reporter config key by :custom_ecto_reporter 104 | -------------------------------------------------------------------------------- /assets/alchemetrics_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/globocom/alchemetrics_web/c06da7b2f08026c0ba49294e59c778fbd86f0815/assets/alchemetrics_web.png -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure your application as: 12 | # 13 | # config :alchemetrics_web, key: :value 14 | # 15 | # and access this configuration in your application as: 16 | # 17 | # Application.get_env(:alchemetrics_web, :key) 18 | # 19 | # You can also configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | import_config "#{Mix.env}.exs" 31 | -------------------------------------------------------------------------------- /config/dev.exs: -------------------------------------------------------------------------------- 1 | 2 | use Mix.Config -------------------------------------------------------------------------------- /config/test.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config -------------------------------------------------------------------------------- /lib/alchemetrics_web.ex: -------------------------------------------------------------------------------- 1 | defmodule AlchemetricsWeb do 2 | defmacro __using__(_) do 3 | quote do 4 | plug AlchemetricsWeb.PhoenixPlug 5 | end 6 | end 7 | end -------------------------------------------------------------------------------- /lib/metric_collectors/ecto.ex: -------------------------------------------------------------------------------- 1 | defmodule AlchemetricsWeb.Collectors.Ecto do 2 | @metadata_type %{type: "database.ecto"} 3 | 4 | @alchemetrics Application.get_env :alchemetrics, :custom_ecto_reporter, Alchemetrics 5 | def collect(ecto_entry) do 6 | report "query_time", ecto_entry.query_time 7 | report "queue_time", ecto_entry.queue_time 8 | @alchemetrics.increment query_count: @metadata_type 9 | end 10 | 11 | defp report(metric_name, value) do 12 | metadata = metadata_with_name(metric_name) 13 | to_microseconds(value) 14 | |> @alchemetrics.report(metadata) 15 | end 16 | 17 | defp metadata_with_name(name) do 18 | @metadata_type 19 | |> Map.put(:name, name) 20 | |> Enum.into([]) 21 | end 22 | 23 | defp to_microseconds(nil), do: to_microseconds(0) 24 | defp to_microseconds(value) do 25 | value 26 | |> System.convert_time_unit(:native, :microsecond) 27 | end 28 | end -------------------------------------------------------------------------------- /lib/metric_collectors/phoenix_request.ex: -------------------------------------------------------------------------------- 1 | defmodule AlchemetricsWeb.Collectors.PhoenixRequest do 2 | 3 | def collect(metadata, request_started_at) do 4 | report_request_count(metadata) 5 | report_response_time metadata, fn -> 6 | request_duration(request_started_at) 7 | end 8 | end 9 | 10 | defp request_duration(request_started_at) do 11 | duration = System.monotonic_time - request_started_at 12 | duration 13 | |> System.convert_time_unit(:native, :microsecond) 14 | end 15 | 16 | @alchemetrics Application.get_env :alchemetrics, :custom_request_reporter, Alchemetrics 17 | defp report_request_count(metric_metadata) do 18 | metric_metadata 19 | |> metadata(:request_count) 20 | |> @alchemetrics.increment 21 | end 22 | 23 | defp report_response_time(metric_metadata, get_response_time_fn) do 24 | get_response_time_fn.() 25 | |> @alchemetrics.report(metadata(metric_metadata, :response_time)) 26 | end 27 | 28 | defp metadata(initial_metadata, type) do 29 | initial_metadata 30 | |> Keyword.put(:type, type) 31 | #|> Enum.into(%{}) 32 | end 33 | end -------------------------------------------------------------------------------- /lib/metric_metadata/request.ex: -------------------------------------------------------------------------------- 1 | defmodule AlchemetricsWeb.MetricMetadata.Request do 2 | def metadata(conn) do 3 | [ 4 | controller: format_controller(conn.private[:phoenix_controller]), 5 | action: format_action(conn.private[:phoenix_action]), 6 | http_status_code: conn.status, 7 | method: conn.method, 8 | host: conn.host, 9 | port: conn.port 10 | ] 11 | end 12 | 13 | defp format_controller(nil), do: :__unknown__ 14 | 15 | defp format_controller(controller) do 16 | controller 17 | |> Macro.underscore() 18 | |> String.split("/") 19 | |> List.last() 20 | end 21 | 22 | defp format_action(nil), do: :__unknown__ 23 | defp format_action(action), do: action 24 | end 25 | -------------------------------------------------------------------------------- /lib/phoenix_plug.ex: -------------------------------------------------------------------------------- 1 | defmodule AlchemetricsWeb.PhoenixPlug do 2 | @behaviour Plug 3 | import Plug.Conn 4 | alias AlchemetricsWeb.MetricMetadata.Request 5 | alias AlchemetricsWeb.Collectors.PhoenixRequest 6 | 7 | def init(opts), do: opts 8 | 9 | def call(conn, _opts) do 10 | request_started_at = System.monotonic_time 11 | 12 | register_before_send conn, fn conn -> 13 | metadata = Request.metadata(conn) 14 | PhoenixRequest.collect(metadata, request_started_at) 15 | conn 16 | end 17 | end 18 | end -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule AlchemetricsWeb.Mixfile do 2 | use Mix.Project 3 | 4 | @description """ 5 | Alchemetrics Web is a boilerplate to report basic metrics for a typical web application, such as request count, response time, ecto and erlang metrics. 6 | """ 7 | @project_url "https://github.com/globocom/alchemetrics_web" 8 | def project do 9 | [ 10 | app: :alchemetrics_web, 11 | version: "0.6.0", 12 | elixir: "~> 1.5", 13 | elixirc_paths: elixirc_paths(Mix.env), 14 | start_permanent: Mix.env == :prod, 15 | description: @description, 16 | source_url: @project_url, 17 | homepage_url: @project_url, 18 | package: package(), 19 | name: "Alchemetrics Web", 20 | docs: [ 21 | main: "AlchemetricsWeb", 22 | source_url: @project_url 23 | ], 24 | deps: deps() 25 | ] 26 | end 27 | 28 | def application do 29 | [ 30 | applications: [:alchemetrics], 31 | extra_applications: [:logger] 32 | ] 33 | end 34 | 35 | defp elixirc_paths(:test), do: ["lib", "test/support"] 36 | defp elixirc_paths(_), do: ["lib"] 37 | 38 | defp deps do 39 | [ 40 | {:plug, "~> 1.0"}, 41 | {:alchemetrics, "0.5.1"}, 42 | {:ex_doc, ">= 0.0.0", only: :dev}, 43 | {:mock, "~> 0.3.3", only: :test} 44 | ] 45 | end 46 | 47 | defp package do 48 | [files: ["config", "lib", "mix.exs", "mix.lock", "README.md", "LICENSE"], 49 | maintainers: ["Globo.com"], 50 | licenses: ["MIT"], 51 | links: %{"GitHub" => @project_url},] 52 | end 53 | 54 | end 55 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "alchemetrics": {:hex, :alchemetrics, "0.5.1", "3be808c31cbff2212766ffe2952b66ed1b44361e139b2a17858575029cf84b1b", [:mix], [{:exometer_core, "~> 1.0", [hex: :basho_exometer_core, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.12", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm"}, 3 | "bear": {:hex, :bear, "0.8.5", "e95fca1627cd9e15baf93ce0a52aff16917baf325f0ee65b88cd715376cd2344", [:rebar3], [], "hexpm"}, 4 | "earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [:mix], [], "hexpm"}, 5 | "edown": {:hex, :edown, "0.8.1", "7333b6f6b7bbc736c263e9ceb8261667d7c8f7d92e251829f1a9fe8f24e7b694", [:rebar3], [], "hexpm"}, 6 | "ex_doc": {:hex, :ex_doc, "0.16.4", "4bf6b82d4f0a643b500366ed7134896e8cccdbab4d1a7a35524951b25b1ec9f0", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, 7 | "exometer_core": {:hex, :basho_exometer_core, "1.0.2", "dc4bc9b0b47edee0c053b16ac5b9c692e662c7e1513ffc96f4d928bc3328d65f", [:rebar3], [{:folsom, "~>0.8.3", [hex: :folsom, repo: "hexpm", optional: false]}, {:lager, "~>3.2.0", [hex: :lager, repo: "hexpm", optional: false]}, {:parse_trans, "~>2.9.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:setup, "~>1.7.0", [hex: :setup, repo: "hexpm", optional: false]}], "hexpm"}, 8 | "folsom": {:hex, :folsom, "0.8.5", "94a027b56fe84feed264f9b33cb4c6ac9a801fad84b87dbda0836ce83c3b8d69", [:rebar3], [{:bear, "0.8.5", [hex: :bear, repo: "hexpm", optional: false]}], "hexpm"}, 9 | "gen_stage": {:hex, :gen_stage, "0.13.1", "edff5bca9cab22c5d03a834062515e6a1aeeb7665fb44eddae086252e39c4378", [:mix], [], "hexpm"}, 10 | "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm"}, 11 | "hut": {:hex, :hut, "1.2.0", "0089df0faa2827c605bbada88153f24fff5ea7a4be32ecf0250a7fdc2719cafb", [], [], "hexpm"}, 12 | "lager": {:hex, :lager, "3.2.4", "a6deb74dae7927f46bd13255268308ef03eb206ec784a94eaf7c1c0f3b811615", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"}, 13 | "meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm"}, 14 | "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], [], "hexpm"}, 15 | "mock": {:hex, :mock, "0.3.3", "42a433794b1291a9cf1525c6d26b38e039e0d3a360732b5e467bfc77ef26c914", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"}, 16 | "parse_trans": {:hex, :parse_trans, "2.9.0", "3f5f7b402928fb9fd200c891e635de909045d1efac40ce3f924d3892898f85eb", [:rebar], [{:edown, "> 0.0.0", [hex: :edown, repo: "hexpm", optional: false]}], "hexpm"}, 17 | "phoenix": {:hex, :phoenix, "1.3.0", "1c01124caa1b4a7af46f2050ff11b267baa3edb441b45dbf243e979cd4c5891b", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, 18 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [:mix], [], "hexpm"}, 19 | "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, 20 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, 21 | "setup": {:hex, :setup, "1.7.0", "15df8e57c6df9755e22bfb1aef0c640bd97e9889396fdfb2a85a5536a9043674", [:rebar3], [], "hexpm"}, 22 | } 23 | -------------------------------------------------------------------------------- /test/metric_collectors/ecto_test.exs: -------------------------------------------------------------------------------- 1 | defmodule AlchemetricsWeb.Test.Collectors.Ecto do 2 | use ExUnit.Case 3 | import Mock 4 | alias AlchemetricsWeb.Collectors.Ecto, as: EctoCollector 5 | 6 | @fake_ecto_entry %{query_time: 2000, queue_time: 1000} 7 | test_with_mock "It reports ecto query and queue time", Alchemetrics, [:passthrough], [] do 8 | EctoCollector.collect @fake_ecto_entry 9 | 10 | assert called Alchemetrics.report(2, [name: "query_time", type: "database.ecto"]) 11 | assert called Alchemetrics.report(1, [name: "queue_time", type: "database.ecto"]) 12 | assert called Alchemetrics.increment(query_count: %{type: "database.ecto"}) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/metric_collectors/phoenix_request_test.exs: -------------------------------------------------------------------------------- 1 | defmodule AlchemetricsWeb.Test.Collectors.PhoenixRequest do 2 | use ExUnit.Case 3 | use Test.Support.AlchemetricsMocker 4 | import Test.Support.AlchemetricsMocker 5 | alias AlchemetricsWeb.Collectors.PhoenixRequest, as: PhoenixCollector 6 | 7 | describe "Phoenix Request Collector" do 8 | @fake_metadata [controller: :fake_controller] 9 | test_with_alchemetrics "reports responsive time and request count" do 10 | PhoenixCollector.collect @fake_metadata, System.monotonic_time 11 | assert called Alchemetrics.increment(expected_metadata(:request_count)) 12 | assert called Alchemetrics.report(:_, expected_metadata(:response_time)) 13 | end 14 | end 15 | 16 | defp expected_metadata(type), do: [type: type] ++ @fake_metadata 17 | end -------------------------------------------------------------------------------- /test/metric_metadata/request_test.exs: -------------------------------------------------------------------------------- 1 | defmodule AlchemetricsWeb.Test.MetricMetadata.Request do 2 | use ExUnit.Case 3 | alias AlchemetricsWeb.MetricMetadata.Request 4 | 5 | test "It builds request metric metadata based on controller and action names" do 6 | fake_conn = %Plug.Conn{ 7 | status: 200, 8 | method: :get, 9 | host: "myapi.com", 10 | port: 80, 11 | private: %{ 12 | :phoenix_action => :fake_action, 13 | :phoenix_controller => AlchemetricsWeb.FakeController 14 | } 15 | } 16 | 17 | metadata = Request.metadata(fake_conn) 18 | 19 | assert metadata[:controller] == "fake_controller" 20 | assert metadata[:action] == :fake_action 21 | assert metadata[:method] == :get 22 | assert metadata[:host] == "myapi.com" 23 | assert metadata[:port] == 80 24 | assert metadata[:http_status_code] == 200 25 | end 26 | 27 | test "It must be able to create request metric even if there is no routing information in connection (in order to handle 404)" do 28 | fake_conn = %Plug.Conn{ 29 | status: 404, 30 | method: :get, 31 | host: "myapi.com", 32 | port: 80, 33 | path_info: ["test", "notfound"] 34 | } 35 | 36 | metadata = Request.metadata(fake_conn) 37 | 38 | assert metadata[:controller] == :__unknown__ 39 | assert metadata[:action] == :__unknown__ 40 | assert metadata[:method] == :get 41 | assert metadata[:host] == "myapi.com" 42 | assert metadata[:port] == 80 43 | assert metadata[:http_status_code] == 404 44 | end 45 | end 46 | 47 | defmodule AlchemetricsWeb.FakeController do 48 | end 49 | -------------------------------------------------------------------------------- /test/support/alchemetrics_mocker.ex: -------------------------------------------------------------------------------- 1 | defmodule Test.Support.AlchemetricsMocker do 2 | defmacro test_with_alchemetrics(test_name, test_block) do 3 | quote do 4 | test_with_mock unquote(test_name), Alchemetrics, [:passthrough], [] do 5 | unquote(test_block) 6 | end 7 | end 8 | end 9 | 10 | defmacro __using__(_) do 11 | quote do 12 | import Mock 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | --------------------------------------------------------------------------------