Skip to content

Commit c9d6ea5

Browse files
author
José Valim
committed
Port Erlang/OTP 21.0 logger from master
Erlang/OTP 21.0 only forwards certain events to the error_logger, which means we need to bring the full logger implementation from master.
1 parent d0694b5 commit c9d6ea5

File tree

12 files changed

+388
-251
lines changed

12 files changed

+388
-251
lines changed

lib/ex_unit/lib/ex_unit/capture_log.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ defmodule ExUnit.CaptureLog do
7070
{:ok, string_io} = StringIO.open("")
7171

7272
try do
73-
_ = :gen_event.which_handlers(:error_logger)
73+
_ = Process.whereis(:error_logger) && :gen_event.which_handlers(:error_logger)
7474
:ok = add_capture(string_io, opts)
7575
ref = ExUnit.CaptureServer.log_capture_on(self())
7676

lib/logger/lib/logger.ex

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ defmodule Logger do
1616
performant when required but also apply backpressure
1717
when under stress.
1818
19-
* Wraps OTP's [`:error_logger`](http://erlang.org/doc/man/error_logger.html)
20-
to prevent it from overflowing.
19+
* Plugs into Erlang's [`:logger`](http://erlang.org/doc/man/logger.html)
20+
(from Erlang/OTP 21) to convert terms to Elixir syntax or wraps
21+
Erlang's [`:error_logger`](http://erlang.org/doc/man/error_logger.html)
22+
in earlier Erlang/OTP versions to prevent it from overflowing.
2123
2224
Logging is useful for tracking when an event of interest happens in your
2325
system. For example, it may be helpful to log whenever a user is deleted.
@@ -62,8 +64,8 @@ defmodule Logger do
6264
* Runtime configuration - can be set before the `:logger`
6365
application is started, but may be changed during runtime
6466
65-
* Error logger configuration - configuration for the
66-
wrapper around OTP's [`:error_logger`](http://erlang.org/doc/man/error_logger.html)
67+
* Erlang configuration - options that handle integration with
68+
Erlang's logging facilities
6769
6870
### Application configuration
6971
@@ -152,19 +154,24 @@ defmodule Logger do
152154
### Error logger configuration
153155
154156
The following configuration applies to `Logger`'s wrapper around
155-
OTP's [`:error_logger`](http://erlang.org/doc/man/error_logger.html).
156-
All the configurations below must be set before the `:logger` application starts.
157+
Erlang's logging functionalities. All the configurations below must
158+
be set before the `:logger` application starts.
157159
158160
* `:handle_otp_reports` - redirects OTP reports to `Logger` so
159-
they are formatted in Elixir terms. This uninstalls OTP's
160-
logger that prints terms to terminal. Defaults to `true`.
161+
they are formatted in Elixir terms. This effectively disables
162+
Erlang standard logger. Defaults to `true`.
161163
162164
* `:handle_sasl_reports` - redirects supervisor, crash and
163165
progress reports to `Logger` so they are formatted in Elixir
164166
terms. Your application must guarantee `:sasl` is started before
165167
`:logger`. This means you may see some initial reports written
166-
in Erlang syntax until the Logger application kicks in and
167-
uninstalls SASL's logger in favor of its own. Defaults to `false`.
168+
in Erlang syntax until the Logger application kicks in.
169+
Defaults to `false`.
170+
171+
From Erlang/OTP 21, `:handle_sasl_reports` only has an effect if
172+
`:handle_otp_reports` is true.
173+
174+
The following configurations apply only for Erlang/OTP 20 and earlier:
168175
169176
* `:discard_threshold_for_error_logger` - if `:error_logger` has more than
170177
`discard_threshold` messages in its inbox, messages will be dropped
@@ -176,19 +183,17 @@ defmodule Logger do
176183
350 (0.75 * threshold) entries and 50 (0.1 * theshold) messages will
177184
be processed before the threshold is checked once again.
178185
179-
For example, to configure `Logger` to redirect all
180-
[`:error_logger`](http://erlang.org/doc/man/error_logger.html) messages
181-
using a `config/config.exs` file:
186+
For example, to configure `Logger` to redirect all Erlang messages using a
187+
`config/config.exs` file:
182188
183189
config :logger,
184190
handle_otp_reports: true,
185191
handle_sasl_reports: true
186192
187-
Furthermore, `Logger` allows messages sent by OTP's `:error_logger`
188-
to be translated into an Elixir format via translators. Translators
189-
can be dynamically added at any time with the `add_translator/1`
190-
and `remove_translator/1` APIs. Check `Logger.Translator` for more
191-
information.
193+
Furthermore, `Logger` allows messages sent by Erlang to be translated
194+
into an Elixir format via translators. Translators can be added at any
195+
time with the `add_translator/1` and `remove_translator/1` APIs. Check
196+
`Logger.Translator` for more information.
192197
193198
## Backends
194199
@@ -523,7 +528,7 @@ defmodule Logger do
523528
"""
524529
@spec flush :: :ok
525530
def flush do
526-
_ = :gen_event.which_handlers(:error_logger)
531+
_ = Process.whereis(:error_logger) && :gen_event.which_handlers(:error_logger)
527532
:gen_event.sync_notify(Logger, :flush)
528533
end
529534

@@ -533,8 +538,7 @@ defmodule Logger do
533538
## Options
534539
535540
* `:flush` - when `true`, guarantees all messages currently sent
536-
to both Logger and OTP's [`:error_logger`](http://erlang.org/doc/man/error_logger.html)
537-
are processed before the backend is added
541+
to `Logger` are processed before the backend is added
538542
539543
"""
540544
@spec add_backend(atom, keyword) :: Supervisor.on_start_child()
@@ -560,8 +564,7 @@ defmodule Logger do
560564
## Options
561565
562566
* `:flush` - when `true`, guarantees all messages currently sent
563-
to both Logger and OTP's [`:error_logger`](http://erlang.org/doc/man/error_logger.html)
564-
are processed before the backend is removed
567+
to `Logger` are processed before the backend is removed
565568
566569
"""
567570
@spec remove_backend(atom, keyword) :: :ok | {:error, term}
@@ -610,14 +613,17 @@ defmodule Logger do
610613
when level in @levels and is_list(metadata) do
611614
case __metadata__() do
612615
{true, pdict} ->
613-
%{mode: mode, truncate: truncate, level: min_level, utc_log: utc_log?} =
614-
Logger.Config.__data__()
615-
616-
if compare_levels(level, min_level) != :lt and mode != :discard do
617-
metadata = [pid: self()] ++ Keyword.merge(pdict, metadata)
618-
{message, metadata} = normalize_message(chardata_or_fun, metadata)
616+
%{
617+
mode: mode,
618+
truncate: truncate,
619+
level: min_level,
620+
utc_log: utc_log?
621+
} = Logger.Config.__data__()
622+
623+
with true <- compare_levels(level, min_level) != :lt and mode != :discard,
624+
metadata = [pid: self()] ++ Keyword.merge(pdict, metadata),
625+
{message, metadata} <- normalize_message(chardata_or_fun, metadata) do
619626
truncated = truncate(message, truncate)
620-
621627
tuple = {Logger, truncated, Logger.Utils.timestamp(utc_log?), metadata}
622628

623629
try do
@@ -629,7 +635,7 @@ defmodule Logger do
629635
:exit, reason -> {:error, reason}
630636
end
631637
else
632-
:ok
638+
_ -> :ok
633639
end
634640

635641
{false, _} ->
@@ -755,15 +761,17 @@ defmodule Logger do
755761
end
756762

757763
defp normalize_message(fun, metadata) when is_function(fun, 0) do
758-
normalize_message(fun.(), metadata)
764+
case fun.() do
765+
{message, fun_metadata} -> {message, Keyword.merge(metadata, fun_metadata)}
766+
:skip -> :skip
767+
message -> {message, metadata}
768+
end
759769
end
760770

761-
defp normalize_message({message, fun_metadata}, metadata) when is_list(fun_metadata) do
762-
{message, Keyword.merge(metadata, fun_metadata)}
771+
defp normalize_message(message, metadata) do
772+
{message, metadata}
763773
end
764774

765-
defp normalize_message(message, metadata), do: {message, metadata}
766-
767775
defp truncate(data, n) when is_list(data) or is_binary(data), do: Logger.Utils.truncate(data, n)
768776
defp truncate(data, n), do: Logger.Utils.truncate(to_string(data), n)
769777

lib/logger/lib/logger/app.ex

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,15 @@ defmodule Logger.App do
77
def start(_type, _args) do
88
otp_reports? = Application.get_env(:logger, :handle_otp_reports)
99
sasl_reports? = Application.get_env(:logger, :handle_sasl_reports)
10-
threshold = Application.get_env(:logger, :discard_threshold_for_error_logger)
11-
error_handler = {:error_logger, Logger.ErrorHandler, {otp_reports?, sasl_reports?, threshold}}
10+
11+
otp_children =
12+
if otp_logger?() do
13+
[]
14+
else
15+
threshold = Application.get_env(:logger, :discard_threshold_for_error_logger)
16+
arg = {:error_logger, Logger.ErrorHandler, {otp_reports?, sasl_reports?, threshold}}
17+
[%{id: Logger.ErrorHandler, start: {Logger.Watcher, :start_link, [arg]}}]
18+
end
1219

1320
children = [
1421
%{
@@ -17,19 +24,21 @@ defmodule Logger.App do
1724
modules: :dynamic
1825
},
1926
{Logger.Watcher, {Logger, Logger.Config, []}},
20-
{Logger.WatcherSupervisor, {Logger.Config, :handlers, []}},
21-
%{
22-
id: Logger.ErrorHandler,
23-
start: {Logger.Watcher, :start_link, [error_handler]}
24-
}
27+
{Logger.WatcherSupervisor, {Logger.Config, :handlers, []}}
28+
| otp_children
2529
]
2630

2731
config = Logger.Config.new()
2832

2933
case Supervisor.start_link(children, strategy: :rest_for_one, name: Logger.Supervisor) do
3034
{:ok, sup} ->
31-
if otp_reports? or sasl_reports? do
32-
delete_handlers(otp_reports?, sasl_reports?)
35+
if otp_logger?() do
36+
if otp_reports? do
37+
add_elixir_handler(sasl_reports?)
38+
delete_erlang_handler()
39+
end
40+
else
41+
delete_old_handlers(otp_reports?, sasl_reports?)
3342
end
3443

3544
{:ok, sup, config}
@@ -47,6 +56,10 @@ defmodule Logger.App do
4756

4857
@doc false
4958
def stop(config) do
59+
if otp_logger?() do
60+
_ = :logger.remove_handler(Logger)
61+
end
62+
5063
add_handlers(Logger.Config.deleted_handlers())
5164
Logger.Config.delete(config)
5265
end
@@ -73,20 +86,35 @@ defmodule Logger.App do
7386
end
7487
end
7588

76-
defp delete_handlers(otp_reports?, sasl_reports?) do
89+
# TODO: Remove conditional error_logger code once we require Erlang/OTP 21+.
90+
defp otp_logger? do
91+
is_pid(Process.whereis(:logger))
92+
end
93+
94+
defp add_elixir_handler(sasl_reports?) do
95+
config = %{level: :debug, sasl_reports?: sasl_reports?}
96+
:logger.add_handler(Logger, Logger.ErlangHandler, config)
97+
end
98+
99+
defp delete_erlang_handler() do
100+
with {:ok, %{module: module} = config} <- :logger.get_handler_config(:default),
101+
:ok <- :logger.remove_handler(:default) do
102+
%{level: level} = :logger.get_primary_config()
103+
:logger.update_primary_config(%{level: :debug})
104+
primary_config = {:primary, %{level: level}}
105+
handler_config = {:default, module, config}
106+
[] = Logger.Config.deleted_handlers([primary_config, handler_config])
107+
:ok
108+
else
109+
_ -> :ok
110+
end
111+
end
112+
113+
defp delete_old_handlers(otp_reports?, sasl_reports?) do
77114
deleted =
78-
if is_pid(Process.whereis(:logger)) and Code.ensure_loaded?(:logger) do
79-
with {:ok, {module, config}} <- :logger.get_handler_config(:default),
80-
:ok <- :logger.remove_handler(:default) do
81-
[{:default, module, config}]
82-
else
83-
_ -> []
84-
end
85-
else
86-
for {tty, true} <- [error_logger_tty_h: otp_reports?, sasl_report_tty_h: sasl_reports?],
87-
:error_logger.delete_report_handler(tty) != {:error, :module_not_found},
88-
do: tty
89-
end
115+
for {tty, true} <- [error_logger_tty_h: otp_reports?, sasl_report_tty_h: sasl_reports?],
116+
:error_logger.delete_report_handler(tty) != {:error, :module_not_found},
117+
do: tty
90118

91119
[] = Logger.Config.deleted_handlers(deleted)
92120
:ok
@@ -95,8 +123,14 @@ defmodule Logger.App do
95123
defp add_handlers(handlers) do
96124
for handler <- handlers do
97125
case handler do
98-
{handler, module, config} -> :logger.add_handler(handler, module, config)
99-
handler -> :error_logger.add_report_handler(handler)
126+
{handler, module, config} ->
127+
:logger.add_handler(handler, module, config)
128+
129+
{:primary, config} ->
130+
:logger.update_primary_config(config)
131+
132+
handler ->
133+
:error_logger.add_report_handler(handler)
100134
end
101135
end
102136

lib/logger/lib/logger/error_handler.ex

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ defmodule Logger.ErrorHandler do
22
@moduledoc false
33
@behaviour :gen_event
44

5+
# TODO: Remove this when we require Erlang/OTP 21+.
6+
57
def init({otp?, sasl?, threshold}) do
68
# We store the Logger PID in the state because when we are shutting
79
# down the Logger application, the Logger process may be terminated
@@ -83,17 +85,13 @@ defmodule Logger.ErrorHandler do
8385
%{
8486
mode: mode,
8587
level: min_level,
86-
truncate: truncate,
87-
utc_log: utc_log?,
88-
translators: translators
88+
utc_log: utc_log?
8989
} = Logger.Config.__data__()
9090

9191
with true <- Logger.compare_levels(level, min_level) != :lt and mode != :discard,
92-
{:ok, message} <- translate(translators, min_level, level, kind, data, truncate) do
93-
message = Logger.Utils.truncate(message, truncate)
94-
92+
meta = [pid: ensure_pid(pid), error_logger: ensure_type(type)],
93+
{message, meta} <- Logger.ErlangHandler.translate(level, kind, data, meta, %{}) do
9594
# Mode is always async to avoid clogging the error_logger
96-
meta = [pid: ensure_pid(pid), error_logger: ensure_type(type)]
9795
event = {Logger, message, Logger.Utils.timestamp(utc_log?), meta}
9896
:gen_event.notify(state.logger, {level, gl, event})
9997
end
@@ -155,25 +153,4 @@ defmodule Logger.ErrorHandler do
155153
0 -> :ok
156154
end
157155
end
158-
159-
defp translate([{mod, fun} | t], min_level, level, kind, data, truncate) do
160-
case apply(mod, fun, [min_level, level, kind, data]) do
161-
{:ok, chardata} -> {:ok, chardata}
162-
:skip -> :skip
163-
:none -> translate(t, min_level, level, kind, data, truncate)
164-
end
165-
end
166-
167-
defp translate([], _min_level, _level, :format, {format, args}, truncate) do
168-
msg =
169-
format
170-
|> Logger.Utils.scan_inspect(args, truncate)
171-
|> :io_lib.build_text()
172-
173-
{:ok, msg}
174-
end
175-
176-
defp translate([], _min_level, _level, :report, {_type, data}, _truncate) do
177-
{:ok, Kernel.inspect(data)}
178-
end
179156
end

lib/logger/lib/logger/formatter.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ defmodule Logger.Formatter do
2020
* `$message` - the log message
2121
* `$level` - the log level
2222
* `$node` - the node that prints the message
23-
* `$metadata` - user controlled data presented in `"key=val key2=val2"` format
23+
* `$metadata` - user controlled data presented in `"key=val key2=val2 "` format
2424
* `$levelpad` - sets to a single space if level is 4 characters long,
2525
otherwise set to the empty space. Used to align the message after level.
2626

0 commit comments

Comments
 (0)