1defmodule Logger.App do
2  @moduledoc false
3
4  require Logger
5
6  use Application
7
8  @doc false
9  def start(_type, _args) do
10    start_options = Application.get_env(:logger, :start_options)
11    otp_reports? = Application.fetch_env!(:logger, :handle_otp_reports)
12    counter = :counters.new(1, [:atomics])
13
14    children = [
15      %{
16        id: :gen_event,
17        start: {:gen_event, :start_link, [{:local, Logger}, start_options]},
18        modules: :dynamic
19      },
20      {Logger.Watcher, {Logger.Config, counter}},
21      Logger.BackendSupervisor
22    ]
23
24    case Supervisor.start_link(children, strategy: :rest_for_one, name: Logger.Supervisor) do
25      {:ok, sup} ->
26        primary_config = add_elixir_handler(otp_reports?, counter)
27
28        default_handlers =
29          if otp_reports? do
30            delete_erlang_handler()
31          else
32            []
33          end
34
35        handlers = [{:primary, primary_config} | default_handlers]
36        {:ok, sup, handlers}
37
38      {:error, _} = error ->
39        error
40    end
41  end
42
43  @doc false
44  def start do
45    Application.start(:logger)
46  end
47
48  @doc false
49  def stop(handlers) do
50    _ = :logger.remove_handler(Logger)
51    _ = :logger.remove_primary_filter(:process_disabled)
52    add_handlers(handlers)
53
54    :logger.add_primary_filter(
55      :silence_logger_exit,
56      {&Logger.Filter.silence_logger_exit/2, []}
57    )
58  end
59
60  @doc false
61  def config_change(changed, _new, _removed) do
62    # All other config has already been persisted, we only need to
63    # update the level and reload the logger state.
64    Logger.configure(Keyword.take(changed, [:level]))
65  end
66
67  @doc """
68  Stops the application without sending messages to error logger.
69  """
70  def stop() do
71    Application.stop(:logger)
72  end
73
74  defp add_elixir_handler(otp_reports?, counter) do
75    config = %{
76      level: :all,
77      config: %{counter: counter},
78      filter_default: :log,
79      filters:
80        if not otp_reports? do
81          [filter_elixir_domain: {&Logger.Filter.filter_elixir_domain/2, []}]
82        else
83          []
84        end
85    }
86
87    %{level: erl_level} = primary_config = :logger.get_primary_config()
88
89    # Elixir's logger level is no longer set by default.
90    #
91    # If it is set, it always has higher precedence, but we warn
92    # in case of mismatches.
93    #
94    # If it is not set, we revert Erlang's kernel to debug, if it
95    # has its default value, otherwise, we keep it as is.
96    case Application.fetch_env(:logger, :level) do
97      {:ok, app_level} ->
98        level = Logger.Handler.elixir_level_to_erlang_level(app_level)
99
100        if erl_level != :notice and erl_level != level do
101          IO.warn(
102            "the level for Erlang's logger was set to #{inspect(erl_level)}, " <>
103              "but Elixir's logger was set to #{inspect(app_level)}. " <>
104              "Elixir's logger value will take higher precedence"
105          )
106        end
107
108        :ok = :logger.set_primary_config(:level, level)
109
110      :error when erl_level == :notice ->
111        :ok = :logger.set_primary_config(:level, :debug)
112
113      :error ->
114        :ok
115    end
116
117    :ok = :logger.add_primary_filter(:process_disabled, {&Logger.Filter.process_disabled/2, []})
118    :ok = :logger.add_handler(Logger, Logger.Handler, config)
119    primary_config
120  end
121
122  defp delete_erlang_handler() do
123    with {:ok, %{module: module} = config} <- :logger.get_handler_config(:default),
124         :ok <- :logger.remove_handler(:default) do
125      handler_config = {:default, module, config}
126
127      [handler_config]
128    else
129      _ -> []
130    end
131  end
132
133  defp add_handlers(handlers) do
134    for handler <- handlers do
135      case handler do
136        {handler, module, config} ->
137          :logger.add_handler(handler, module, config)
138
139        {:primary, config} ->
140          :logger.set_primary_config(config)
141      end
142    end
143
144    :ok
145  end
146end
147