Repository: phoenixframework/phoenix
Pull request #3830 (2020-05-09 02:46:54)

Fix the other Cowboy.Drainer link
Follow-up to #3823.


Patch:
diff --git a/guides/telemetry.md b/guides/telemetry.md
index 4386868d1..1c11b9236 100644
--- a/guides/telemetry.md
+++ b/guides/telemetry.md
@@ -13,6 +13,14 @@ and we will guide you through the steps to properly
 instrument your own functions using `:telemetry`. Let's take
 a closer look at how Telemetry works in your application.
 
+## Overview
+
+The `[:telemetry]` library allows you to emit events at various stages of an application's lifecycle. You can then respond to these events by, among other things, aggregating them as metrics and sending the metrics data to a reporting destination.
+
+Telemetry stores events by their name in an ETS table, along with the handler for each event. Then, when a given event is executed, Telemetry looks up its handler and invokes it.
+
+Phoenix's Telemetry tooling provides you with a supervisor that uses `Telemetry.Metrics` to define the list of Telemetry events to handle and how to handle those events, i.e. how to structure them as a certain type of metric. This supervisor works together with Telemetry reporters to respond to the specified Telemetry events by aggregating them as the appropriate metric and sending them to the correct reporting destination.
+
 ## The Telemetry supervisor
 
 Since v1.5, new Phoenix applications are generated with a
@@ -103,7 +111,7 @@ children = [
 ## Telemetry Events
 
 Many Elixir libraries (including Phoenix) are already using
-the `:telemetry` package (http://hexdocs.pm/telemetry) as a
+the [`:telemetry`](http://hexdocs.pm/telemetry) package as a
 way to give users more insight into the behavior of their
 applications, by emitting events at key moments in the
 application lifecycle.
@@ -124,7 +132,7 @@ A Telemetry event is made up of the following:
 Here is an example of an event from your endpoint:
 
 * `[:phoenix, :endpoint, :stop]` - dispatched by
-  `Plug.Telemetry` in your endpoint whenever the response is
+  `Plug.Telemetry`, one of the default plugs in your endpoint, whenever the response is
   sent
 
   * Measurement: `%{duration: native_time}`
@@ -148,11 +156,15 @@ took to get the response:
 > &#x2015; `Telemetry.Metrics`
 
 The Telemetry.Metrics package provides a common interface
-for defining metrics. What this means is that the package
-does not perform any aggregation of the measurements itself,
-but instead relies on reporters to do so. We will discuss
+for defining metrics. It exposes a set of [five metric type functions](https://hexdocs.pm/telemetry_metrics/Telemetry.Metrics.html#module-metrics) that are responsible for structuring a given Telemetry event as a particular measurement.
+
+The package does not perform any aggregation of the measurements itself. Instead, it provides a reporter with the Telemetry event-as-measurement definition and the reporter uses that definition to perform aggregations and report them.
+
+We will discuss
 reporters in the next section.
 
+Let's take a look at some examples.
+
 Using `Telemetry.Metrics`, you can define a counter metric,
 which counts how many HTTP requests were completed:
 
@@ -182,11 +194,30 @@ documentation.
 
 ### An Ecto Example
 
-Like Phoenix, Ecto ships with built-in telemetry events.
+Like Phoenix, Ecto ships with built-in Telemetry events.
 This means that you can gain introspection into your web
 and database layers using the same tools.
 
-For instance, you might want to graph query execution time:
+Here is an example of a Telemetry event executed by Ecto when an Ecto repository starts:
+
+* `[:ecto, :repo, :init]` - dispatched by
+  `Ecto.Repo.Supervisor`
+
+  * Measurement: `%{system_time: native_time}`
+
+  * Metadata: `%{repo: Ecto.Repo, opts: Keyword.t()}`
+
+This means that whenever the `Ecto.Repo.Supervisor` starts, it will emit an event, via `:telemetry`,
+with a measurement of the time at start-up.
+
+```elixir
+:telemetry.execute([:ecto, :repo, :init], %{system_time: System.system_time()}, %{repo: repo, opts: opts})
+```
+
+Additional Telemetry events are executed by Ecto adapters.
+
+One such adapter-specific event is the `[:my_app, :repo, :query]` event.
+For instance, if you want to graph query execution time, you can use the `Telemetry.Metrics.summary/2` function to instruct your reporter to calculate statistics of the `[:my_app, :repo, :query]` event, like maximum, mean, percentiles etc.:
 
 ```elixir
 Telemetry.Metrics.summary("my_app.repo.query.query_time",
@@ -194,7 +225,7 @@ Telemetry.Metrics.summary("my_app.repo.query.query_time",
 )
 ```
 
-or view how long queries spend queued:
+Or you could use the `Telemetry.Metrics.distribution/2` function to define a histogram for another adapter-specific event: `[:my_app, :repo, :query, :queue_time]`, thus visualizing how long queries spend queued:
 
 ```elixir
 Telemetry.Metrics.distribution("my_app.repo.query.queue_time",
@@ -220,6 +251,16 @@ interface provided by `Telemetry.Metrics`. They then
 aggregate the measurements (data) into metrics to provide
 meaningful information about your application.
 
+For example, if the following `Telemetry.Metrics.summary/2` call is added to the `metrics/0` function of your Telemetry supervisor:
+
+```elixir
+summary("phoenix.endpoint.stop.duration",
+  unit: {:native, :millisecond}
+)
+```
+
+Then the reporter will attach a listener for the `"phoenix.endpoint.stop.duration"` event and will respond to this event by calculating a summary metric with the given event metadata and reporting on that metric to the appropriate source.
+
 ### Phoenix.LiveDashboard
 
 For developers interested in real-time visualizations for
diff --git a/lib/phoenix/channel/server.ex b/lib/phoenix/channel/server.ex
index cd402e234..a608f4b68 100644
--- a/lib/phoenix/channel/server.ex
+++ b/lib/phoenix/channel/server.ex
@@ -22,10 +22,11 @@ defmodule Phoenix.Channel.Server do
 
     ref = make_ref()
     from = {self(), ref}
-    child_spec = channel.child_spec({payload, from, socket})
+    child_spec = channel.child_spec({socket.endpoint, from})
 
     case PoolSupervisor.start_child(socket.endpoint, socket.handler, from, child_spec) do
       {:ok, pid} ->
+        send(pid, {Phoenix.Channel, payload, from, socket})
         mon_ref = Process.monitor(pid)
 
         receive do
@@ -254,10 +255,8 @@ defmodule Phoenix.Channel.Server do
   ## Callbacks
 
   @doc false
-  def init({auth_payload, from, socket}) do
-    # TODO: Use handle_continue when we support Erlang/OTP 21+.
-    send(self(), {:join, __MODULE__})
-    {:ok, {auth_payload, from, socket}}
+  def init({_endpoint, {pid, _}}) do
+    {:ok, Process.monitor(pid)}
   end
 
   @doc false
@@ -285,7 +284,8 @@ defmodule Phoenix.Channel.Server do
   end
 
   @doc false
-  def handle_info({:join, __MODULE__}, {auth_payload, {pid, _} = from, socket}) do
+  def handle_info({Phoenix.Channel, auth_payload, {pid, _} = from, socket}, ref) do
+    Process.demonitor(ref)
     %{channel: channel, topic: topic, private: private} = socket
     Process.put(:"$callers", [pid])
 
@@ -329,6 +329,10 @@ defmodule Phoenix.Channel.Server do
     |> handle_result(:handle_out)
   end
 
+  def handle_info({:DOWN, ref, _, _, reason}, ref) do
+    {:stop, reason, ref}
+  end
+
   def handle_info({:DOWN, _, _, transport_pid, reason}, %{transport_pid: transport_pid} = socket) do
     reason = if reason == :normal, do: {:shutdown, :closed}, else: reason
     {:stop, reason, socket}
diff --git a/lib/phoenix/endpoint/cowboy2_adapter.ex b/lib/phoenix/endpoint/cowboy2_adapter.ex
index 7047dcc0e..3ad326dfa 100644
--- a/lib/phoenix/endpoint/cowboy2_adapter.ex
+++ b/lib/phoenix/endpoint/cowboy2_adapter.ex
@@ -16,7 +16,7 @@ defmodule Phoenix.Endpoint.Cowboy2Adapter do
 
     * `:drainer` - a drainer process that triggers when your application is
       shutting to wait for any on-going request to finish. It accepts all
-      options as defined by [`Plug.Cowboy`](https://hexdocs.pm/plug_cowboy/Plug.Cowboy.Drainer.httml).
+      options as defined by [`Plug.Cowboy`](https://hexdocs.pm/plug_cowboy/Plug.Cowboy.Drainer.html).
       Defaults to `[]` and can be disabled by setting it to false.
 
   ## Custom dispatch options
diff --git a/lib/phoenix/logger.ex b/lib/phoenix/logger.ex
index 2d859d805..3460a55b1 100644
--- a/lib/phoenix/logger.ex
+++ b/lib/phoenix/logger.ex
@@ -69,7 +69,7 @@ defmodule Phoenix.Logger do
 
     * `[:phoenix, :channel_joined]` - dispatched at the end of a channel join
       * Measurement: `%{duration: native_time}`
-      * Metadata: `%{params: term, socket: Phoenix.Socket.t}`
+      * Metadata: `%{result: :ok | :error, params: term, socket: Phoenix.Socket.t}`
       * Disable logging: This event cannot be disabled
 
     * `[:phoenix, :channel_handled_in]` - dispatched at the end of a channel handle in
diff --git a/lib/phoenix/socket.ex b/lib/phoenix/socket.ex
index a5fff3d6a..74a0be896 100644
--- a/lib/phoenix/socket.ex
+++ b/lib/phoenix/socket.ex
@@ -129,9 +129,14 @@ defmodule Phoenix.Socket do
   ## Custom channels
 
   You can list any module as a channel as long as it implements
-  a `start_link/1` function that receives a tuple with three elements:
+  a `child_spec/1` function. The `child_spec/1` function receives
+  the caller as argument and it must return a child spec that
+  initializes a process.
 
-      {auth_payload, from, socket}
+  Once the process is initialized, it will receive the following
+  message:
+
+      {Phoenix.Channel, auth_payload, from, socket}
 
   A custom channel implementation MUST invoke
   `GenServer.reply(from, {:ok | :error, reply_payload})` during its
@@ -174,11 +179,6 @@ defmodule Phoenix.Socket do
   Custom channel implementations cannot be tested with `Phoenix.ChannelTest`
   and are currently considered experimental. The underlying API may be
   changed at any moment.
-
-  **Note:** in future Phoenix versions we will require custom channels
-  to provide a custom `child_spec/1` function instead of `start_link/1`.
-  Since the default behaviour of `child_spec/1` is to invoke `start_link/1`,
-  this behaviour should be backwards compatible in almost all cases.
   """
 
   require Logger
diff --git a/priv/templates/phx.gen.json/fallback_controller.ex b/priv/templates/phx.gen.json/fallback_controller.ex
index a4b88bf7a..6ae62aa42 100644
--- a/priv/templates/phx.gen.json/fallback_controller.ex
+++ b/priv/templates/phx.gen.json/fallback_controller.ex
@@ -6,14 +6,16 @@ defmodule <%= inspect context.web_module %>.FallbackController do
   """
   use <%= inspect context.web_module %>, :controller
 
-  <%= if schema.generate? do %>def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
+  <%= if schema.generate? do %># This clause handles errors returned by Ecto's insert/update/delete.
+  def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
     conn
     |> put_status(:unprocessable_entity)
     |> put_view(<%= inspect context.web_module %>.ChangesetView)
     |> render("error.json", changeset: changeset)
   end
 
-  <% end %>def call(conn, {:error, :not_found}) do
+  <% end %># This clause is an example of how to handle resources that cannot be found.
+  def call(conn, {:error, :not_found}) do
     conn
     |> put_status(:not_found)
     |> put_view(<%= inspect context.web_module %>.ErrorView)


Test patch:
diff --git a/guides/testing/testing_controllers.md b/guides/testing/testing_controllers.md
index 965093f87..72a7b6a9d 100644
--- a/guides/testing/testing_controllers.md
+++ b/guides/testing/testing_controllers.md
@@ -47,11 +47,11 @@ def index(conn, _params) do
 end
 ```
 
-It gets all posts and renders the "index.html" template. The template can be found in "lib/hello_web/templates/page/index.html.eex".
+It gets all posts and renders the "index.html" template. The template can be found in `lib/hello_web/templates/page/index.html.eex`.
 
 The test looks like this:
 
-```elixi
+```elixir
 describe "index" do
   test "lists all posts", %{conn: conn} do
     conn = get(conn, Routes.post_path(conn, :index))
diff --git a/test/phoenix/integration/websocket_channels_test.exs b/test/phoenix/integration/websocket_channels_test.exs
index 217f142f9..fdc0699a4 100644
--- a/test/phoenix/integration/websocket_channels_test.exs
+++ b/test/phoenix/integration/websocket_channels_test.exs
@@ -64,20 +64,24 @@ defmodule Phoenix.Integration.WebSocketChannelsTest do
   defmodule CustomChannel do
     use GenServer, restart: :temporary
 
-    def start_link(triplet) do
-      GenServer.start_link(__MODULE__, triplet)
+    def start_link(from) do
+      GenServer.start_link(__MODULE__, from)
     end
 
-    def init({payload, from, socket}) do
+    def init({_, _}) do
+      {:ok, :init}
+    end
+
+    def handle_info({Phoenix.Channel, payload, from, socket}, :init) do
       case payload["action"] do
         "ok" ->
           GenServer.reply(from, {:ok, %{"action" => "ok"}})
-          {:ok, socket}
+          {:noreply, socket}
 
         "ignore" ->
           GenServer.reply(from, {:error, %{"action" => "ignore"}})
           send(self(), :stop)
-          {:ok, socket}
+          {:noreply, socket}
 
         "error" ->
           raise "oops"