Parcourir la source

ft:修改

master
SisMaker il y a 4 ans
Parent
révision
910a543b27
43 fichiers modifiés avec 9234 ajouts et 9326 suppressions
  1. +351
    -442
      README.md
  2. +23
    -23
      rebar.config
  3. +2
    -2
      src/eRum.app.src
  4. +430
    -430
      src/eRum.erl
  5. +303
    -303
      src/eRum_app.erl
  6. +451
    -451
      src/formater/lager_default_formatter.erl
  7. +322
    -322
      src/formater/lager_format.erl
  8. +643
    -643
      src/misc/error_logger_lager_h.erl
  9. +38
    -38
      src/misc/lager_backend_throttle.erl
  10. +56
    -56
      src/misc/lager_config.erl
  11. +499
    -499
      src/misc/lager_console_backend.erl
  12. +254
    -254
      src/misc/lager_crash_log.erl
  13. +853
    -853
      src/misc/lager_file_backend.erl
  14. +157
    -157
      src/misc/lager_handler_watcher.erl
  15. +6
    -6
      src/misc/lager_handler_watcher_sup.erl
  16. +24
    -24
      src/misc/lager_manager_killer.erl
  17. +20
    -20
      src/misc/lager_msg.erl
  18. +337
    -337
      src/misc/lager_stdlib.erl
  19. +640
    -640
      src/misc/lager_trunc_io.erl
  20. +752
    -752
      src/misc/rumUtil.erl
  21. +9
    -9
      src/rotator/lager_rotator_behaviour.erl
  22. +152
    -152
      src/rotator/lager_rotator_default.erl
  23. +62
    -62
      src/test/lager_common_test_backend.erl
  24. +251
    -251
      src/transform/lager_transform.erl
  25. +11
    -11
      test/compress_pr_record_test.erl
  26. +60
    -60
      test/crash.erl
  27. +10
    -10
      test/crash_fsm.erl
  28. +19
    -19
      test/crash_statem.erl
  29. +4
    -4
      test/lager_app_tests.erl
  30. +28
    -28
      test/lager_crash_backend.erl
  31. +100
    -100
      test/lager_manager_killer_test.erl
  32. +38
    -39
      test/lager_metadata_whitelist_test.erl
  33. +130
    -130
      test/lager_rotate.erl
  34. +10
    -10
      test/lager_slow_backend.erl
  35. +1689
    -1689
      test/lager_test_backend.erl
  36. +145
    -145
      test/lager_test_function_transform.erl
  37. +68
    -68
      test/lager_trace_test.erl
  38. +31
    -31
      test/pr_composite_test.erl
  39. +36
    -36
      test/pr_stacktrace_test.erl
  40. +25
    -25
      test/special_process.erl
  41. +33
    -33
      test/sync_error_logger.erl
  42. +141
    -141
      test/trunc_io_eqc.erl
  43. +21
    -21
      test/zzzz_gh280_crash.erl

+ 351
- 442
README.md
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 23
- 23
rebar.config Voir le fichier

@ -1,40 +1,40 @@
{erl_opts, [
{lager_extra_sinks, ['__lager_test_sink']},
{platform_define, "^(19|20|21|22)", test_statem},
{platform_define, "^18", 'FUNCTION_NAME', unavailable},
{platform_define, "^18", 'FUNCTION_ARITY', 0},
debug_info,
report,
verbose,
warn_deprecated_function,
warn_deprecated_type,
warn_export_all,
warn_export_vars,
warn_obsolete_guard,
warn_untyped_record,
warn_unused_import
% do NOT include warnings_as_errors, as rebar includes these options
% when compiling for eunit, and at least one test module has code that
% is deliberatly broken and will generate an un-maskable warning
{lager_extra_sinks, ['__lager_test_sink']},
{platform_define, "^(19|20|21|22)", test_statem},
{platform_define, "^18", 'FUNCTION_NAME', unavailable},
{platform_define, "^18", 'FUNCTION_ARITY', 0},
debug_info,
report,
verbose,
warn_deprecated_function,
warn_deprecated_type,
warn_export_all,
warn_export_vars,
warn_obsolete_guard,
warn_untyped_record,
warn_unused_import
% do NOT include warnings_as_errors, as rebar includes these options
% when compiling for eunit, and at least one test module has code that
% is deliberatly broken and will generate an un-maskable warning
]}.
{erl_first_files, ["src/lager_util.erl"]}.
{eunit_opts, [verbose]}.
{eunit_compile_opts, [
export_all,
export_all,
nowarn_untyped_record,
nowarn_export_all
nowarn_untyped_record,
nowarn_export_all
]}.
{deps, [
{goldrush, "0.1.9"}
{goldrush, "0.1.9"}
]}.
{shell, [
% {config, "config/sys.config"},
{apps, [eRum]}
% {config, "config/sys.config"},
{apps, [eRum]}
]}.
{xref_checks, []}.

+ 2
- 2
src/eRum.app.src Voir le fichier

@ -7,9 +7,9 @@
{registered, [eRum_sup, lager_event, lager_crash_log, lager_handler_watcher_sup]},
{mod, {eRum_app, []}},
{env, [
%% Note: application:start(lager) overwrites previously defined environment variables thus declaration of default handlers is done at lager_app.erl
%% Note: application:start(lager) overwrites previously defined environment variables thus declaration of default handlers is done at lager_app.erl
%% What colors to use with what log levels
%% What colors to use with what log levels
{colored, false},
{colors, [
{debug, "\e[0;38m"},

+ 430
- 430
src/eRum.erl
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 303
- 303
src/eRum_app.erl Voir le fichier

@ -6,11 +6,11 @@
-include_lib("eunit/include/eunit.hrl").
-endif.
-export([start/0,
start/2,
start_handler/3,
configure_sink/2,
stop/1,
boot/1]).
start/2,
start_handler/3,
configure_sink/2,
stop/1,
boot/1]).
%% The `application:get_env/3` compatibility wrapper was useful
%% for other modules in r15 and before
@ -19,388 +19,388 @@
-define(FILENAMES, '__lager_file_backend_filenames').
-define(THROTTLE, lager_backend_throttle).
-define(DEFAULT_HANDLER_CONF,
[{lager_console_backend, [{level, info}]},
{lager_file_backend,
[{file, "log/error.log"}, {level, error},
{size, 10485760}, {date, "$D0"}, {count, 5}]
},
{lager_file_backend,
[{file, "log/console.log"}, {level, info},
{size, 10485760}, {date, "$D0"}, {count, 5}]
}
]).
[{lager_console_backend, [{level, info}]},
{lager_file_backend,
[{file, "log/error.log"}, {level, error},
{size, 10485760}, {date, "$D0"}, {count, 5}]
},
{lager_file_backend,
[{file, "log/console.log"}, {level, info},
{size, 10485760}, {date, "$D0"}, {count, 5}]
}
]).
start() ->
application:start(lager).
application:start(lager).
start_throttle(Sink, Threshold, Window) ->
_ = supervisor:start_child(lager_handler_watcher_sup,
[Sink, ?THROTTLE, [Threshold, Window]]),
ok.
_ = supervisor:start_child(lager_handler_watcher_sup,
[Sink, ?THROTTLE, [Threshold, Window]]),
ok.
determine_async_behavior(_Sink, undefined, _Window) ->
ok;
ok;
determine_async_behavior(_Sink, Threshold, _Window) when not is_integer(Threshold) orelse Threshold < 0 ->
error_logger:error_msg("Invalid value for 'async_threshold': ~p~n",
[Threshold]),
throw({error, bad_config});
error_logger:error_msg("Invalid value for 'async_threshold': ~p~n",
[Threshold]),
throw({error, bad_config});
determine_async_behavior(Sink, Threshold, undefined) ->
start_throttle(Sink, Threshold, erlang:trunc(Threshold * 0.2));
start_throttle(Sink, Threshold, erlang:trunc(Threshold * 0.2));
determine_async_behavior(_Sink, Threshold, Window) when not is_integer(Window) orelse Window > Threshold orelse Window < 0 ->
error_logger:error_msg(
"Invalid value for 'async_threshold_window': ~p~n", [Window]),
throw({error, bad_config});
error_logger:error_msg(
"Invalid value for 'async_threshold_window': ~p~n", [Window]),
throw({error, bad_config});
determine_async_behavior(Sink, Threshold, Window) ->
start_throttle(Sink, Threshold, Window).
start_throttle(Sink, Threshold, Window).
start_handlers(_Sink, undefined) ->
ok;
ok;
start_handlers(_Sink, Handlers) when not is_list(Handlers) ->
error_logger:error_msg(
"Invalid value for 'handlers' (must be list): ~p~n", [Handlers]),
throw({error, bad_config});
error_logger:error_msg(
"Invalid value for 'handlers' (must be list): ~p~n", [Handlers]),
throw({error, bad_config});
start_handlers(Sink, Handlers) ->
%% handlers failing to start are handled in the handler_watcher
lager_config:global_set(handlers,
lager_config:global_get(handlers, []) ++
lists:map(fun({Module, Config}) ->
check_handler_config(Module, Config),
start_handler(Sink, Module, Config);
(_) ->
throw({error, bad_config})
end,
expand_handlers(Handlers))),
ok.
%% handlers failing to start are handled in the handler_watcher
lager_config:global_set(handlers,
lager_config:global_get(handlers, []) ++
lists:map(fun({Module, Config}) ->
check_handler_config(Module, Config),
start_handler(Sink, Module, Config);
(_) ->
throw({error, bad_config})
end,
expand_handlers(Handlers))),
ok.
start_handler(Sink, Module, Config) ->
{ok, Watcher} = supervisor:start_child(lager_handler_watcher_sup,
[Sink, Module, Config]),
{Module, Watcher, Sink}.
{ok, Watcher} = supervisor:start_child(lager_handler_watcher_sup,
[Sink, Module, Config]),
{Module, Watcher, Sink}.
check_handler_config({lager_file_backend, F}, Config) when is_list(Config); is_tuple(Config) ->
Fs = case get(?FILENAMES) of
undefined -> ordsets:new();
X -> X
end,
case ordsets:is_element(F, Fs) of
true ->
error_logger:error_msg(
"Cannot have same file (~p) in multiple file backends~n", [F]),
throw({error, bad_config});
false ->
put(?FILENAMES,
ordsets:add_element(F, Fs))
end,
ok;
Fs = case get(?FILENAMES) of
undefined -> ordsets:new();
X -> X
end,
case ordsets:is_element(F, Fs) of
true ->
error_logger:error_msg(
"Cannot have same file (~p) in multiple file backends~n", [F]),
throw({error, bad_config});
false ->
put(?FILENAMES,
ordsets:add_element(F, Fs))
end,
ok;
check_handler_config(_Handler, Config) when is_list(Config) orelse is_atom(Config) ->
ok;
ok;
check_handler_config(Handler, _BadConfig) ->
throw({error, {bad_config, Handler}}).
throw({error, {bad_config, Handler}}).
clean_up_config_checks() ->
erase(?FILENAMES).
erase(?FILENAMES).
interpret_hwm(undefined) ->
undefined;
undefined;
interpret_hwm(HWM) when not is_integer(HWM) orelse HWM < 0 ->
_ = eRum:log(warning, self(), "Invalid error_logger high water mark: ~p, disabling", [HWM]),
undefined;
_ = eRum:log(warning, self(), "Invalid error_logger high water mark: ~p, disabling", [HWM]),
undefined;
interpret_hwm(HWM) ->
HWM.
HWM.
maybe_install_sink_killer(_Sink, undefined, _ReinstallTimer) -> ok;
maybe_install_sink_killer(Sink, HWM, undefined) -> maybe_install_sink_killer(Sink, HWM, 5000);
maybe_install_sink_killer(Sink, HWM, ReinstallTimer) when is_integer(HWM) andalso is_integer(ReinstallTimer)
andalso HWM >= 0 andalso ReinstallTimer >= 0 ->
_ = supervisor:start_child(lager_handler_watcher_sup, [Sink, lager_manager_killer,
[HWM, ReinstallTimer]]);
andalso HWM >= 0 andalso ReinstallTimer >= 0 ->
_ = supervisor:start_child(lager_handler_watcher_sup, [Sink, lager_manager_killer,
[HWM, ReinstallTimer]]);
maybe_install_sink_killer(_Sink, HWM, ReinstallTimer) ->
error_logger:error_msg("Invalid value for 'killer_hwm': ~p or 'killer_reinstall_after': ~p", [HWM, ReinstallTimer]),
throw({error, bad_config}).
error_logger:error_msg("Invalid value for 'killer_hwm': ~p or 'killer_reinstall_after': ~p", [HWM, ReinstallTimer]),
throw({error, bad_config}).
-spec start_error_logger_handler(boolean(), pos_integer(), list()) -> list().
start_error_logger_handler(false, _HWM, _Whitelist) ->
[];
[];
start_error_logger_handler(true, HWM, WhiteList) ->
GlStrategy = case application:get_env(lager, error_logger_groupleader_strategy) of
undefined ->
handle;
{ok, GlStrategy0} when
GlStrategy0 =:= handle;
GlStrategy0 =:= ignore;
GlStrategy0 =:= mirror ->
GlStrategy0;
{ok, BadGlStrategy} ->
error_logger:error_msg(
"Invalid value for 'error_logger_groupleader_strategy': ~p~n",
[BadGlStrategy]),
throw({error, bad_config})
GlStrategy = case application:get_env(lager, error_logger_groupleader_strategy) of
undefined ->
handle;
{ok, GlStrategy0} when
GlStrategy0 =:= handle;
GlStrategy0 =:= ignore;
GlStrategy0 =:= mirror ->
GlStrategy0;
{ok, BadGlStrategy} ->
error_logger:error_msg(
"Invalid value for 'error_logger_groupleader_strategy': ~p~n",
[BadGlStrategy]),
throw({error, bad_config})
end,
case whereis(error_logger) of
undefined ->
%% On OTP 21 and above, error_logger is deprecated in favor of 'logger'
%% As a band-aid, boot up error_logger anyway and install it as a logger handler
%% we can't use error_logger:add_report_handler because we want supervision of the handler
%% so we have to manually add the logger handler
%%
%% Longer term we should be installing a logger handler instead, but this will bridge the gap
%% for now.
_ = error_logger:start(),
_ = logger:add_handler(error_logger, error_logger, #{level => info, filter_default => log}),
ok = maybe_remove_logger_handler();
_ ->
ok
end,
%% capture which handlers we removed from error_logger so we can restore them when lager stops
OldHandlers = case supervisor:start_child(lager_handler_watcher_sup, [error_logger, error_logger_lager_h, [HWM, GlStrategy]]) of
{ok, _} ->
[begin error_logger:delete_report_handler(X), X end ||
X <- gen_event:which_handlers(error_logger) -- [error_logger_lager_h | WhiteList]];
{error, _} ->
[]
end,
case whereis(error_logger) of
undefined ->
%% On OTP 21 and above, error_logger is deprecated in favor of 'logger'
%% As a band-aid, boot up error_logger anyway and install it as a logger handler
%% we can't use error_logger:add_report_handler because we want supervision of the handler
%% so we have to manually add the logger handler
%%
%% Longer term we should be installing a logger handler instead, but this will bridge the gap
%% for now.
_ = error_logger:start(),
_ = logger:add_handler(error_logger,error_logger,#{level=>info,filter_default=>log}),
ok = maybe_remove_logger_handler();
_ ->
ok
end,
%% capture which handlers we removed from error_logger so we can restore them when lager stops
OldHandlers = case supervisor:start_child(lager_handler_watcher_sup, [error_logger, error_logger_lager_h, [HWM, GlStrategy]]) of
{ok, _} ->
[begin error_logger:delete_report_handler(X), X end ||
X <- gen_event:which_handlers(error_logger) -- [error_logger_lager_h | WhiteList]];
{error, _} ->
[]
end,
OldHandlers.
OldHandlers.
%% On OTP 21.1 and higher we need to remove the `default' handler.
%% But it might not exist, so we will wrap this in a try-catch
%% block
maybe_remove_logger_handler() ->
try
ok = logger:remove_handler(default)
catch
error:undef -> ok;
Err:Reason ->
error_logger:error_msg("calling logger:remove_handler(default) failed: ~p ~p",
[Err, Reason])
end.
try
ok = logger:remove_handler(default)
catch
error:undef -> ok;
Err:Reason ->
error_logger:error_msg("calling logger:remove_handler(default) failed: ~p ~p",
[Err, Reason])
end.
configure_sink(Sink, SinkDef) ->
lager_config:new_sink(Sink),
ChildId = rumUtil:make_internal_sink_name(Sink),
_ = supervisor:start_child(eRum_sup,
{ChildId,
{gen_event, start_link,
[{local, Sink}]},
permanent, 5000, worker, dynamic}),
determine_async_behavior(Sink, proplists:get_value(async_threshold, SinkDef),
proplists:get_value(async_threshold_window, SinkDef)
),
_ = maybe_install_sink_killer(Sink, proplists:get_value(killer_hwm, SinkDef),
proplists:get_value(killer_reinstall_after, SinkDef)),
start_handlers(Sink,
proplists:get_value(handlers, SinkDef, [])),
eRum:update_loglevel_config(Sink).
lager_config:new_sink(Sink),
ChildId = rumUtil:make_internal_sink_name(Sink),
_ = supervisor:start_child(eRum_sup,
{ChildId,
{gen_event, start_link,
[{local, Sink}]},
permanent, 5000, worker, dynamic}),
determine_async_behavior(Sink, proplists:get_value(async_threshold, SinkDef),
proplists:get_value(async_threshold_window, SinkDef)
),
_ = maybe_install_sink_killer(Sink, proplists:get_value(killer_hwm, SinkDef),
proplists:get_value(killer_reinstall_after, SinkDef)),
start_handlers(Sink,
proplists:get_value(handlers, SinkDef, [])),
eRum:update_loglevel_config(Sink).
configure_extra_sinks(Sinks) ->
lists:foreach(fun({Sink, Proplist}) -> configure_sink(Sink, Proplist) end,
Sinks).
lists:foreach(fun({Sink, Proplist}) -> configure_sink(Sink, Proplist) end,
Sinks).
-spec get_env(atom(), atom(), term()) -> term().
get_env(Application, Key, Default) ->
application:get_env(Application, Key, Default).
application:get_env(Application, Key, Default).
start(_StartType, _StartArgs) ->
{ok, Pid} = eRum_sup:start_link(),
SavedHandlers = boot(),
_ = boot('__all_extra'),
_ = boot('__traces'),
clean_up_config_checks(),
{ok, Pid, SavedHandlers}.
{ok, Pid} = eRum_sup:start_link(),
SavedHandlers = boot(),
_ = boot('__all_extra'),
_ = boot('__traces'),
clean_up_config_checks(),
{ok, Pid, SavedHandlers}.
boot() ->
%% Handle the default sink.
determine_async_behavior(?RumDefSink,
application:get_env(lager, async_threshold, undefined),
application:get_env(lager, async_threshold_window, undefined)),
%% Handle the default sink.
determine_async_behavior(?RumDefSink,
application:get_env(lager, async_threshold, undefined),
application:get_env(lager, async_threshold_window, undefined)),
_ = maybe_install_sink_killer(?RumDefSink, application:get_env(lager, killer_hwm, undefined),
application:get_env(lager, killer_reinstall_after, undefined)),
_ = maybe_install_sink_killer(?RumDefSink, application:get_env(lager, killer_hwm, undefined),
application:get_env(lager, killer_reinstall_after, undefined)),
start_handlers(?RumDefSink,
application:get_env(lager, handlers, ?DEFAULT_HANDLER_CONF)),
start_handlers(?RumDefSink,
application:get_env(lager, handlers, ?DEFAULT_HANDLER_CONF)),
eRum:update_loglevel_config(?RumDefSink),
eRum:update_loglevel_config(?RumDefSink),
SavedHandlers = start_error_logger_handler(
application:get_env(lager, error_logger_redirect, true),
interpret_hwm(application:get_env(lager, error_logger_hwm, 0)),
application:get_env(lager, error_logger_whitelist, [])
),
SavedHandlers = start_error_logger_handler(
application:get_env(lager, error_logger_redirect, true),
interpret_hwm(application:get_env(lager, error_logger_hwm, 0)),
application:get_env(lager, error_logger_whitelist, [])
),
SavedHandlers.
SavedHandlers.
boot('__traces') ->
_ = rumUtil:trace_filter(none),
ok = add_configured_traces();
_ = rumUtil:trace_filter(none),
ok = add_configured_traces();
boot('__all_extra') ->
configure_extra_sinks(application:get_env(lager, extra_sinks, []));
configure_extra_sinks(application:get_env(lager, extra_sinks, []));
boot(?RumDefSink) -> boot();
boot(Sink) ->
AllSinksDef = application:get_env(lager, extra_sinks, []),
boot_sink(Sink, lists:keyfind(Sink, 1, AllSinksDef)).
AllSinksDef = application:get_env(lager, extra_sinks, []),
boot_sink(Sink, lists:keyfind(Sink, 1, AllSinksDef)).
boot_sink(Sink, {Sink, Def}) ->
configure_sink(Sink, Def);
configure_sink(Sink, Def);
boot_sink(Sink, false) ->
configure_sink(Sink, []).
configure_sink(Sink, []).
stop(Handlers) ->
lists:foreach(fun(Handler) ->
error_logger:add_report_handler(Handler)
end, Handlers),
lager_config:cleanup().
lists:foreach(fun(Handler) ->
error_logger:add_report_handler(Handler)
end, Handlers),
lager_config:cleanup().
expand_handlers([]) ->
[];
expand_handlers([{lager_file_backend, [{Key, _Value}|_]=Config}|T]) when is_atom(Key) ->
%% this is definitely a new-style config, no expansion needed
[maybe_make_handler_id(lager_file_backend, Config) | expand_handlers(T)];
expand_handlers([{lager_file_backend, Configs}|T]) ->
?INT_LOG(notice, "Deprecated lager_file_backend config detected, please consider updating it", []),
[ {lager_file_backend:config_to_id(Config), Config} || Config <- Configs] ++
expand_handlers(T);
expand_handlers([{Mod, Config}|T]) when is_atom(Mod) ->
[maybe_make_handler_id(Mod, Config) | expand_handlers(T)];
expand_handlers([H|T]) ->
[H | expand_handlers(T)].
[];
expand_handlers([{lager_file_backend, [{Key, _Value} | _] = Config} | T]) when is_atom(Key) ->
%% this is definitely a new-style config, no expansion needed
[maybe_make_handler_id(lager_file_backend, Config) | expand_handlers(T)];
expand_handlers([{lager_file_backend, Configs} | T]) ->
?INT_LOG(notice, "Deprecated lager_file_backend config detected, please consider updating it", []),
[{lager_file_backend:config_to_id(Config), Config} || Config <- Configs] ++
expand_handlers(T);
expand_handlers([{Mod, Config} | T]) when is_atom(Mod) ->
[maybe_make_handler_id(Mod, Config) | expand_handlers(T)];
expand_handlers([H | T]) ->
[H | expand_handlers(T)].
add_configured_traces() ->
Traces = case application:get_env(lager, traces) of
undefined ->
[];
{ok, TraceVal} ->
TraceVal
end,
Traces = case application:get_env(lager, traces) of
undefined ->
[];
{ok, TraceVal} ->
TraceVal
end,
lists:foreach(fun start_configured_trace/1, Traces),
ok.
lists:foreach(fun start_configured_trace/1, Traces),
ok.
start_configured_trace({Handler, Filter}) ->
{ok, _} = eRum:trace(Handler, Filter);
{ok, _} = eRum:trace(Handler, Filter);
start_configured_trace({Handler, Filter, Level}) when is_atom(Level) ->
{ok, _} = eRum:trace(Handler, Filter, Level).
{ok, _} = eRum:trace(Handler, Filter, Level).
maybe_make_handler_id(Mod, Config) ->
%% Allow the backend to generate a gen_event handler id, if it wants to.
%% We don't use erlang:function_exported here because that requires the module
%% already be loaded, which is unlikely at this phase of startup. Using code:load
%% caused undesirable side-effects with generating code-coverage reports.
try Mod:config_to_id(Config) of
Id ->
{Id, Config}
catch
error:undef ->
{Mod, Config}
end.
%% Allow the backend to generate a gen_event handler id, if it wants to.
%% We don't use erlang:function_exported here because that requires the module
%% already be loaded, which is unlikely at this phase of startup. Using code:load
%% caused undesirable side-effects with generating code-coverage reports.
try Mod:config_to_id(Config) of
Id ->
{Id, Config}
catch
error:undef ->
{Mod, Config}
end.
-ifdef(TEST).
application_config_mangling_test_() ->
[
{"Explode the file backend handlers",
?_assertMatch(
[{lager_console_backend, [{level, info}]},
{{lager_file_backend,"error.log"},{"error.log",error,10485760, "$D0",5}},
{{lager_file_backend,"console.log"},{"console.log",info,10485760, "$D0",5}}
],
expand_handlers([{lager_console_backend, [{level, info}]},
{lager_file_backend, [
{"error.log", error, 10485760, "$D0", 5},
{"console.log", info, 10485760, "$D0", 5}
]}]
))
},
{"Explode the short form of backend file handlers",
?_assertMatch(
[{lager_console_backend, [{level, info}]},
{{lager_file_backend,"error.log"},{"error.log",error}},
{{lager_file_backend,"console.log"},{"console.log",info}}
],
expand_handlers([{lager_console_backend, [{level, info}]},
{lager_file_backend, [
{"error.log", error},
{"console.log", info}
]}]
))
},
{"Explode with formatter info",
?_assertMatch(
[{{lager_file_backend,"test.log"}, [{"test.log", debug, 10485760, "$D0", 5},{lager_default_formatter,["[",severity,"] ", message, "\n"]}]},
{{lager_file_backend,"test2.log"}, [{"test2.log",debug, 10485760, "$D0", 5},{lager_default_formatter,["2>[",severity,"] ", message, "\n"]}]}],
expand_handlers([{lager_file_backend, [
[{"test.log", debug, 10485760, "$D0", 5},{lager_default_formatter,["[",severity,"] ", message, "\n"]}],
[{"test2.log",debug, 10485760, "$D0", 5},{lager_default_formatter,["2>[",severity,"] ",message, "\n"]}]
]
}])
)
},
{"Explode short form with short formatter info",
?_assertMatch(
[{{lager_file_backend,"test.log"}, [{"test.log", debug},{lager_default_formatter,["[",severity,"] ", message, "\n"]}]},
{{lager_file_backend,"test2.log"}, [{"test2.log",debug},{lager_default_formatter}]}],
expand_handlers([{lager_file_backend, [
[{"test.log", debug},{lager_default_formatter,["[",severity,"] ", message, "\n"]}],
[{"test2.log",debug},{lager_default_formatter}]
]
}])
)
},
{"New form needs no expansion",
?_assertMatch([
{{lager_file_backend,"test.log"}, [{file, "test.log"}]},
{{lager_file_backend,"test2.log"}, [{file, "test2.log"}, {level, info}, {sync_on, none}]},
{{lager_file_backend,"test3.log"}, [{formatter, lager_default_formatter}, {file, "test3.log"}]}
[
{"Explode the file backend handlers",
?_assertMatch(
[{lager_console_backend, [{level, info}]},
{{lager_file_backend, "error.log"}, {"error.log", error, 10485760, "$D0", 5}},
{{lager_file_backend, "console.log"}, {"console.log", info, 10485760, "$D0", 5}}
],
expand_handlers([{lager_console_backend, [{level, info}]},
{lager_file_backend, [
{"error.log", error, 10485760, "$D0", 5},
{"console.log", info, 10485760, "$D0", 5}
]}]
))
},
{"Explode the short form of backend file handlers",
?_assertMatch(
[{lager_console_backend, [{level, info}]},
{{lager_file_backend, "error.log"}, {"error.log", error}},
{{lager_file_backend, "console.log"}, {"console.log", info}}
],
expand_handlers([
{lager_file_backend, [{file, "test.log"}]},
{lager_file_backend, [{file, "test2.log"}, {level, info}, {sync_on, none}]},
{lager_file_backend, [{formatter, lager_default_formatter},{file, "test3.log"}]}
])
)
}
].
expand_handlers([{lager_console_backend, [{level, info}]},
{lager_file_backend, [
{"error.log", error},
{"console.log", info}
]}]
))
},
{"Explode with formatter info",
?_assertMatch(
[{{lager_file_backend, "test.log"}, [{"test.log", debug, 10485760, "$D0", 5}, {lager_default_formatter, ["[", severity, "] ", message, "\n"]}]},
{{lager_file_backend, "test2.log"}, [{"test2.log", debug, 10485760, "$D0", 5}, {lager_default_formatter, ["2>[", severity, "] ", message, "\n"]}]}],
expand_handlers([{lager_file_backend, [
[{"test.log", debug, 10485760, "$D0", 5}, {lager_default_formatter, ["[", severity, "] ", message, "\n"]}],
[{"test2.log", debug, 10485760, "$D0", 5}, {lager_default_formatter, ["2>[", severity, "] ", message, "\n"]}]
]
}])
)
},
{"Explode short form with short formatter info",
?_assertMatch(
[{{lager_file_backend, "test.log"}, [{"test.log", debug}, {lager_default_formatter, ["[", severity, "] ", message, "\n"]}]},
{{lager_file_backend, "test2.log"}, [{"test2.log", debug}, {lager_default_formatter}]}],
expand_handlers([{lager_file_backend, [
[{"test.log", debug}, {lager_default_formatter, ["[", severity, "] ", message, "\n"]}],
[{"test2.log", debug}, {lager_default_formatter}]
]
}])
)
},
{"New form needs no expansion",
?_assertMatch([
{{lager_file_backend, "test.log"}, [{file, "test.log"}]},
{{lager_file_backend, "test2.log"}, [{file, "test2.log"}, {level, info}, {sync_on, none}]},
{{lager_file_backend, "test3.log"}, [{formatter, lager_default_formatter}, {file, "test3.log"}]}
],
expand_handlers([
{lager_file_backend, [{file, "test.log"}]},
{lager_file_backend, [{file, "test2.log"}, {level, info}, {sync_on, none}]},
{lager_file_backend, [{formatter, lager_default_formatter}, {file, "test3.log"}]}
])
)
}
].
check_handler_config_test_() ->
Good = expand_handlers(?DEFAULT_HANDLER_CONF),
Bad = expand_handlers([{lager_console_backend, [{level, info}]},
{lager_file_backend, [{file, "same_file.log"}]},
{lager_file_backend, [{file, "same_file.log"}, {level, info}]}]),
AlsoBad = [{lager_logstash_backend,
{level, info},
{output, {udp, "localhost", 5000}},
{format, json},
{json_encoder, jiffy}}],
BadToo = [{fail, {fail}}],
OldSchoolLagerGood = expand_handlers([{lager_console_backend, [{level, info}]},
{lager_file_backend, [
{"./log/error.log",error,10485760,"$D0",5},
{"./log/console.log",info,10485760,"$D0",5},
{"./log/debug.log",debug,10485760,"$D0",5}
]}]),
NewConfigMissingList = expand_handlers([{foo_backend, {file, "same_file.log"}}]),
[
{"lager_file_backend_good",
?_assertEqual([ok, ok, ok], [ check_handler_config(M,C) || {M,C} <- Good ])
},
{"lager_file_backend_bad",
?_assertThrow({error, bad_config}, [ check_handler_config(M,C) || {M,C} <- Bad ])
},
{"Invalid config dies",
?_assertThrow({error, bad_config}, start_handlers(foo, AlsoBad))
},
{"Invalid config dies",
?_assertThrow({error, {bad_config, _}}, start_handlers(foo, BadToo))
},
{"Old Lager config works",
?_assertEqual([ok, ok, ok, ok], [ check_handler_config(M, C) || {M, C} <- OldSchoolLagerGood])
},
{"New Config missing its list should fail",
?_assertThrow({error, {bad_config, foo_backend}}, [ check_handler_config(M, C) || {M, C} <- NewConfigMissingList])
}
].
Good = expand_handlers(?DEFAULT_HANDLER_CONF),
Bad = expand_handlers([{lager_console_backend, [{level, info}]},
{lager_file_backend, [{file, "same_file.log"}]},
{lager_file_backend, [{file, "same_file.log"}, {level, info}]}]),
AlsoBad = [{lager_logstash_backend,
{level, info},
{output, {udp, "localhost", 5000}},
{format, json},
{json_encoder, jiffy}}],
BadToo = [{fail, {fail}}],
OldSchoolLagerGood = expand_handlers([{lager_console_backend, [{level, info}]},
{lager_file_backend, [
{"./log/error.log", error, 10485760, "$D0", 5},
{"./log/console.log", info, 10485760, "$D0", 5},
{"./log/debug.log", debug, 10485760, "$D0", 5}
]}]),
NewConfigMissingList = expand_handlers([{foo_backend, {file, "same_file.log"}}]),
[
{"lager_file_backend_good",
?_assertEqual([ok, ok, ok], [check_handler_config(M, C) || {M, C} <- Good])
},
{"lager_file_backend_bad",
?_assertThrow({error, bad_config}, [check_handler_config(M, C) || {M, C} <- Bad])
},
{"Invalid config dies",
?_assertThrow({error, bad_config}, start_handlers(foo, AlsoBad))
},
{"Invalid config dies",
?_assertThrow({error, {bad_config, _}}, start_handlers(foo, BadToo))
},
{"Old Lager config works",
?_assertEqual([ok, ok, ok, ok], [check_handler_config(M, C) || {M, C} <- OldSchoolLagerGood])
},
{"New Config missing its list should fail",
?_assertThrow({error, {bad_config, foo_backend}}, [check_handler_config(M, C) || {M, C} <- NewConfigMissingList])
}
].
-endif.

+ 451
- 451
src/formater/lager_default_formatter.erl Voir le fichier

@ -53,206 +53,206 @@
%%
%% `[{pid, ["My pid is ", pid], ["Unknown Pid"]}]' -> if pid is in the metada print "My pid is ?.?.?", otherwise print "Unknown Pid"
%% @end
-spec format(lager_msg:lager_msg(),list(),list()) -> any().
format(Msg,[], Colors) ->
format(Msg, [{eol, "\n"}], Colors);
format(Msg,[{eol, EOL}], Colors) ->
Config = case application:get_env(lager, metadata_whitelist) of
undefined -> config(EOL, []);
{ok, Whitelist} -> config(EOL, Whitelist)
end,
format(Msg, Config, Colors);
format(Message,Config,Colors) ->
[ case V of
color -> output_color(Message,Colors);
_ -> output(V,Message)
end || V <- Config ].
-spec format(lager_msg:lager_msg(), list(), list()) -> any().
format(Msg, [], Colors) ->
format(Msg, [{eol, "\n"}], Colors);
format(Msg, [{eol, EOL}], Colors) ->
Config = case application:get_env(lager, metadata_whitelist) of
undefined -> config(EOL, []);
{ok, Whitelist} -> config(EOL, Whitelist)
end,
format(Msg, Config, Colors);
format(Message, Config, Colors) ->
[case V of
color -> output_color(Message, Colors);
_ -> output(V, Message)
end || V <- Config].
-spec format(lager_msg:lager_msg(),list()) -> any().
-spec format(lager_msg:lager_msg(), list()) -> any().
format(Msg, Config) ->
format(Msg, Config, []).
format(Msg, Config, []).
-spec output(term(),lager_msg:lager_msg()) -> iolist().
output(message,Msg) -> lager_msg:message(Msg);
output(date,Msg) ->
{D, _T} = lager_msg:datetime(Msg),
D;
output(time,Msg) ->
{_D, T} = lager_msg:datetime(Msg),
T;
output(severity,Msg) ->
atom_to_list(lager_msg:severity(Msg));
-spec output(term(), lager_msg:lager_msg()) -> iolist().
output(message, Msg) -> lager_msg:message(Msg);
output(date, Msg) ->
{D, _T} = lager_msg:datetime(Msg),
D;
output(time, Msg) ->
{_D, T} = lager_msg:datetime(Msg),
T;
output(severity, Msg) ->
atom_to_list(lager_msg:severity(Msg));
output(severity_upper, Msg) ->
uppercase_severity(lager_msg:severity(Msg));
output(blank,_Msg) ->
output({blank," "},_Msg);
uppercase_severity(lager_msg:severity(Msg));
output(blank, _Msg) ->
output({blank, " "}, _Msg);
output(node, _Msg) ->
output({node, atom_to_list(node())},_Msg);
output({blank,Fill},_Msg) ->
Fill;
output(sev,Msg) ->
%% Write brief acronym for the severity level (e.g. debug -> $D)
[rumUtil:level_to_chr(lager_msg:severity(Msg))];
output({node, atom_to_list(node())}, _Msg);
output({blank, Fill}, _Msg) ->
Fill;
output(sev, Msg) ->
%% Write brief acronym for the severity level (e.g. debug -> $D)
[rumUtil:level_to_chr(lager_msg:severity(Msg))];
output(metadata, Msg) ->
output({metadata, "=", " "}, Msg);
output({metadata, "=", " "}, Msg);
output({metadata, IntSep, FieldSep}, Msg) ->
MD = lists:keysort(1, lager_msg:metadata(Msg)),
string:join([io_lib:format("~s~s~p", [K, IntSep, V]) || {K, V} <- MD], FieldSep);
MD = lists:keysort(1, lager_msg:metadata(Msg)),
string:join([io_lib:format("~s~s~p", [K, IntSep, V]) || {K, V} <- MD], FieldSep);
output({pterm, Key}, Msg) ->
output({pterm, Key, ""}, Msg);
output({pterm, Key, ""}, Msg);
output({pterm, Key, Default}, _Msg) ->
make_printable(maybe_get_persistent_term(Key, Default));
output(Prop,Msg) when is_atom(Prop) ->
Metadata = lager_msg:metadata(Msg),
make_printable(get_metadata(Prop,Metadata,<<"Undefined">>));
output({Prop,Default},Msg) when is_atom(Prop) ->
Metadata = lager_msg:metadata(Msg),
make_printable(get_metadata(Prop,Metadata,output(Default,Msg)));
make_printable(maybe_get_persistent_term(Key, Default));
output(Prop, Msg) when is_atom(Prop) ->
Metadata = lager_msg:metadata(Msg),
make_printable(get_metadata(Prop, Metadata, <<"Undefined">>));
output({Prop, Default}, Msg) when is_atom(Prop) ->
Metadata = lager_msg:metadata(Msg),
make_printable(get_metadata(Prop, Metadata, output(Default, Msg)));
output({Prop, Present, Absent}, Msg) when is_atom(Prop) ->
%% sort of like a poor man's ternary operator
Metadata = lager_msg:metadata(Msg),
case get_metadata(Prop, Metadata) of
undefined ->
[ output(V, Msg) || V <- Absent];
_ ->
[ output(V, Msg) || V <- Present]
end;
%% sort of like a poor man's ternary operator
Metadata = lager_msg:metadata(Msg),
case get_metadata(Prop, Metadata) of
undefined ->
[output(V, Msg) || V <- Absent];
_ ->
[output(V, Msg) || V <- Present]
end;
output({Prop, Present, Absent, Width}, Msg) when is_atom(Prop) ->
%% sort of like a poor man's ternary operator
Metadata = lager_msg:metadata(Msg),
case get_metadata(Prop, Metadata) of
undefined ->
[ output(V, Msg, Width) || V <- Absent];
_ ->
[ output(V, Msg, Width) || V <- Present]
end;
output(Other,_) -> make_printable(Other).
%% sort of like a poor man's ternary operator
Metadata = lager_msg:metadata(Msg),
case get_metadata(Prop, Metadata) of
undefined ->
[output(V, Msg, Width) || V <- Absent];
_ ->
[output(V, Msg, Width) || V <- Present]
end;
output(Other, _) -> make_printable(Other).
output(message, Msg, _Width) -> lager_msg:message(Msg);
output(date,Msg, _Width) ->
{D, _T} = lager_msg:datetime(Msg),
D;
output(date, Msg, _Width) ->
{D, _T} = lager_msg:datetime(Msg),
D;
output(time, Msg, _Width) ->
{_D, T} = lager_msg:datetime(Msg),
T;
{_D, T} = lager_msg:datetime(Msg),
T;
output(severity, Msg, Width) ->
make_printable(atom_to_list(lager_msg:severity(Msg)), Width);
output(sev,Msg, _Width) ->
%% Write brief acronym for the severity level (e.g. debug -> $D)
[rumUtil:level_to_chr(lager_msg:severity(Msg))];
make_printable(atom_to_list(lager_msg:severity(Msg)), Width);
output(sev, Msg, _Width) ->
%% Write brief acronym for the severity level (e.g. debug -> $D)
[rumUtil:level_to_chr(lager_msg:severity(Msg))];
output(node, Msg, _Width) ->
output({node, atom_to_list(node())}, Msg, _Width);
output(blank,_Msg, _Width) ->
output({blank, " "},_Msg, _Width);
output({blank, Fill},_Msg, _Width) ->
Fill;
output({node, atom_to_list(node())}, Msg, _Width);
output(blank, _Msg, _Width) ->
output({blank, " "}, _Msg, _Width);
output({blank, Fill}, _Msg, _Width) ->
Fill;
output(metadata, Msg, _Width) ->
output({metadata, "=", " "}, Msg, _Width);
output({metadata, "=", " "}, Msg, _Width);
output({metadata, IntSep, FieldSep}, Msg, _Width) ->
MD = lists:keysort(1, lager_msg:metadata(Msg)),
[string:join([io_lib:format("~s~s~p", [K, IntSep, V]) || {K, V} <- MD], FieldSep)];
MD = lists:keysort(1, lager_msg:metadata(Msg)),
[string:join([io_lib:format("~s~s~p", [K, IntSep, V]) || {K, V} <- MD], FieldSep)];
output({pterm, Key}, Msg, Width) ->
output({pterm, Key, ""}, Msg, Width);
output({pterm, Key, ""}, Msg, Width);
output({pterm, Key, Default}, _Msg, _Width) ->
make_printable(maybe_get_persistent_term(Key, Default));
make_printable(maybe_get_persistent_term(Key, Default));
output(Prop, Msg, Width) when is_atom(Prop) ->
Metadata = lager_msg:metadata(Msg),
make_printable(get_metadata(Prop,Metadata,<<"Undefined">>), Width);
output({Prop,Default},Msg, Width) when is_atom(Prop) ->
Metadata = lager_msg:metadata(Msg),
make_printable(get_metadata(Prop,Metadata,output(Default,Msg)), Width);
output(Other,_, Width) -> make_printable(Other, Width).
Metadata = lager_msg:metadata(Msg),
make_printable(get_metadata(Prop, Metadata, <<"Undefined">>), Width);
output({Prop, Default}, Msg, Width) when is_atom(Prop) ->
Metadata = lager_msg:metadata(Msg),
make_printable(get_metadata(Prop, Metadata, output(Default, Msg)), Width);
output(Other, _, Width) -> make_printable(Other, Width).
output_color(_Msg,[]) -> [];
output_color(Msg,Colors) ->
Level = lager_msg:severity(Msg),
case lists:keyfind(Level, 1, Colors) of
{_, Color} -> Color;
_ -> []
end.
output_color(_Msg, []) -> [];
output_color(Msg, Colors) ->
Level = lager_msg:severity(Msg),
case lists:keyfind(Level, 1, Colors) of
{_, Color} -> Color;
_ -> []
end.
-spec make_printable(any()) -> iolist().
make_printable(A) when is_atom(A) -> atom_to_list(A);
make_printable(P) when is_pid(P) -> pid_to_list(P);
make_printable(L) when is_list(L) orelse is_binary(L) -> L;
make_printable(Other) -> io_lib:format("~p",[Other]).
make_printable(L) when is_list(L) orelse is_binary(L) -> L;
make_printable(Other) -> io_lib:format("~p", [Other]).
make_printable(A,W) when is_integer(W)-> string:left(make_printable(A),W);
make_printable(A,{Align,W}) when is_integer(W) ->
case Align of
left ->
string:left(make_printable(A),W);
centre ->
string:centre(make_printable(A),W);
right ->
string:right(make_printable(A),W);
_ ->
string:left(make_printable(A),W)
end;
make_printable(A, W) when is_integer(W) -> string:left(make_printable(A), W);
make_printable(A, {Align, W}) when is_integer(W) ->
case Align of
left ->
string:left(make_printable(A), W);
centre ->
string:centre(make_printable(A), W);
right ->
string:right(make_printable(A), W);
_ ->
string:left(make_printable(A), W)
end;
make_printable(A,_W) -> make_printable(A).
make_printable(A, _W) -> make_printable(A).
%% persistent term was introduced in OTP 21.2, so
%% if we're running on an older OTP, just return the
%% default value.
-ifdef(OTP_RELEASE).
maybe_get_persistent_term(Key, Default) ->
try
persistent_term:get(Key, Default)
catch
_:undef -> Default
end.
try
persistent_term:get(Key, Default)
catch
_:undef -> Default
end.
-else.
maybe_get_persistent_term(_Key, Default) -> Default.
-endif.
run_function(Function, Default) ->
try Function() of
Result ->
Result
catch
_:_ ->
Default
end.
try Function() of
Result ->
Result
catch
_:_ ->
Default
end.
get_metadata(Key, Metadata) ->
get_metadata(Key, Metadata, undefined).
get_metadata(Key, Metadata, undefined).
get_metadata(Key, Metadata, Default) ->
case lists:keyfind(Key, 1, Metadata) of
false ->
Default;
{Key, Value} when is_function(Value) ->
run_function(Value, Default);
{Key, Value} ->
Value
end.
case lists:keyfind(Key, 1, Metadata) of
false ->
Default;
{Key, Value} when is_function(Value) ->
run_function(Value, Default);
{Key, Value} ->
Value
end.
config(EOL, []) ->
[
date, " ", time, " ", color, "[", severity, "] ",
{pid, ""},
{module, [
{pid, ["@"], ""},
module,
{function, [":", function], ""},
{line, [":",line], ""}], ""},
" ", message, EOL
];
[
date, " ", time, " ", color, "[", severity, "] ",
{pid, ""},
{module, [
{pid, ["@"], ""},
module,
{function, [":", function], ""},
{line, [":", line], ""}], ""},
" ", message, EOL
];
config(EOL, MetaWhitelist) ->
[
date, " ", time, " ", color, "[", severity, "] ",
{pid, ""},
{module, [
{pid, ["@"], ""},
module,
{function, [":", function], ""},
{line, [":",line], ""}], ""},
" "
] ++
[{M, [atom_to_list(M), "=", M, " "], ""}|| M <- MetaWhitelist] ++
[message, EOL].
[
date, " ", time, " ", color, "[", severity, "] ",
{pid, ""},
{module, [
{pid, ["@"], ""},
module,
{function, [":", function], ""},
{line, [":", line], ""}], ""},
" "
] ++
[{M, [atom_to_list(M), "=", M, " "], ""} || M <- MetaWhitelist] ++
[message, EOL].
@ -267,305 +267,305 @@ uppercase_severity(emergency) -> "EMERGENCY".
-ifdef(TEST).
date_time_now() ->
Now = os:timestamp(),
{Date, Time} = rumUtil:format_time(rumUtil:maybe_utc(rumUtil:localtime_ms(Now))),
{Date, Time, Now}.
Now = os:timestamp(),
{Date, Time} = rumUtil:format_time(rumUtil:maybe_utc(rumUtil:localtime_ms(Now))),
{Date, Time, Now}.
basic_test_() ->
{Date, Time, Now} = date_time_now(),
[{"Default formatting test",
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] ", pid_to_list(self()), " Message\n"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, self()}],
[]),
[])))
},
{"Basic Formatting",
?_assertEqual(<<"Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, self()}],
[]),
["Simplist Format"])))
},
{"Default equivalent formatting test",
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] ", pid_to_list(self()), " Message\n"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, self()}],
[]),
[date, " ", time," [",severity,"] ",pid, " ", message, "\n"]
)))
},
{"Non existent metadata can default to string",
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] Fallback Message\n"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, self()}],
[]),
[date, " ", time," [",severity,"] ",{does_not_exist,"Fallback"}, " ", message, "\n"]
)))
},
{"Non existent metadata can default to other metadata",
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] Fallback Message\n"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, "Fallback"}],
[]),
[date, " ", time," [",severity,"] ",{does_not_exist,pid}, " ", message, "\n"]
)))
},
{"Non existent metadata can default to a string2",
?_assertEqual(iolist_to_binary(["Unknown Pid"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[],
[]),
[{pid, ["My pid is ", pid], ["Unknown Pid"]}]
)))
},
{"Metadata can have extra formatting",
?_assertEqual(iolist_to_binary(["My pid is hello"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
[{pid, ["My pid is ", pid], ["Unknown Pid"]}]
)))
},
{"Metadata can have extra formatting1",
?_assertEqual(iolist_to_binary(["servername"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}, {server, servername}],
[]),
[{server,{pid, ["(", pid, ")"], ["(Unknown Server)"]}}]
)))
},
{"Metadata can have extra formatting2",
?_assertEqual(iolist_to_binary(["(hello)"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
[{server,{pid, ["(", pid, ")"], ["(Unknown Server)"]}}]
)))
},
{"Metadata can have extra formatting3",
?_assertEqual(iolist_to_binary(["(Unknown Server)"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[],
[]),
[{server,{pid, ["(", pid, ")"], ["(Unknown Server)"]}}]
)))
},
{"Metadata can be printed in its enterity",
?_assertEqual(iolist_to_binary(["bar=2 baz=3 foo=1"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{foo, 1}, {bar, 2}, {baz, 3}],
[]),
[metadata]
)))
},
{"Metadata can be printed in its enterity with custom seperators",
?_assertEqual(iolist_to_binary(["bar->2, baz->3, foo->1"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{foo, 1}, {bar, 2}, {baz, 3}],
[]),
[{metadata, "->", ", "}]
)))
},
{"Metadata can have extra formatting with width 1",
?_assertEqual(iolist_to_binary(["(hello )(hello )(hello)(hello)(hello)"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
["(",{pid, [pid], "", 10},")",
"(",{pid, [pid], "", {bad_align,10}},")",
"(",{pid, [pid], "", bad10},")",
"(",{pid, [pid], "", {right,bad20}},")",
"(",{pid, [pid], "", {bad_align,bad20}},")"]
)))
},
{"Metadata can have extra formatting with width 2",
?_assertEqual(iolist_to_binary(["(hello )"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
["(",{pid, [pid], "", {left,10}},")"]
)))
},
{"Metadata can have extra formatting with width 3",
?_assertEqual(iolist_to_binary(["( hello)"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
["(",{pid, [pid], "", {right,10}},")"]
)))
},
{"Metadata can have extra formatting with width 4",
?_assertEqual(iolist_to_binary(["( hello )"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
["(",{pid, [pid], "", {centre,10}},")"]
)))
},
{"Metadata can have extra formatting with width 5",
?_assertEqual(iolist_to_binary(["error |hello ! ( hello )"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
[{x,"",[severity,{blank,"|"},pid], 10},"!",blank,"(",{pid, [pid], "", {centre,10}},")"]
)))
},
{"Metadata can have extra formatting with width 6",
?_assertEqual(iolist_to_binary([Time,Date," bar=2 baz=3 foo=1 pid=hello EMessage"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello},{foo, 1}, {bar, 2}, {baz, 3}],
[]),
[{x,"",[time]}, {x,"",[date],20},blank,{x,"",[metadata],30},blank,{x,"",[sev],10},message, {message,message,"", {right,20}}]
)))
},
{"Uppercase Severity Formatting - DEBUG",
?_assertEqual(<<"DEBUG Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
debug,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - INFO",
?_assertEqual(<<"INFO Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
info,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - NOTICE",
?_assertEqual(<<"NOTICE Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
notice,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - WARNING",
?_assertEqual(<<"WARNING Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
warning,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - ERROR",
?_assertEqual(<<"ERROR Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - CRITICAL",
?_assertEqual(<<"CRITICAL Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
critical,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - ALERT",
?_assertEqual(<<"ALERT Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
alert,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - EMERGENCY",
?_assertEqual(<<"EMERGENCY Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
emergency,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"pterm presence test",
%% skip test on OTP < 21
case list_to_integer(erlang:system_info(otp_release)) >= 21 of
true ->
?_assertEqual(<<"Pterm is: something">>,
begin
persistent_term:put(thing, something),
Ret = iolist_to_binary(format(lager_msg:new("Message",
Now,
emergency,
[{pid, self()}],
[]),
["Pterm is: ", {pterm, thing}])),
persistent_term:erase(thing),
Ret
end);
false -> ?_assert(true)
end
},
{"pterm absence test",
?_assertEqual(<<"Pterm is: nothing">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
emergency,
[{pid, self()}],
[]),
["Pterm is: ", {pterm, thing, "nothing"}])))
},
{"node formatting basic",
begin
[N, "foo"] = format(lager_msg:new("Message",
Now,
info,
[{pid, self()}],
[]),
[node, "foo"]),
?_assertNotMatch(nomatch, re:run(N, <<"@">>))
end
}
].
{Date, Time, Now} = date_time_now(),
[{"Default formatting test",
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] ", pid_to_list(self()), " Message\n"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, self()}],
[]),
[])))
},
{"Basic Formatting",
?_assertEqual(<<"Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, self()}],
[]),
["Simplist Format"])))
},
{"Default equivalent formatting test",
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] ", pid_to_list(self()), " Message\n"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, self()}],
[]),
[date, " ", time, " [", severity, "] ", pid, " ", message, "\n"]
)))
},
{"Non existent metadata can default to string",
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] Fallback Message\n"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, self()}],
[]),
[date, " ", time, " [", severity, "] ", {does_not_exist, "Fallback"}, " ", message, "\n"]
)))
},
{"Non existent metadata can default to other metadata",
?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] Fallback Message\n"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, "Fallback"}],
[]),
[date, " ", time, " [", severity, "] ", {does_not_exist, pid}, " ", message, "\n"]
)))
},
{"Non existent metadata can default to a string2",
?_assertEqual(iolist_to_binary(["Unknown Pid"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[],
[]),
[{pid, ["My pid is ", pid], ["Unknown Pid"]}]
)))
},
{"Metadata can have extra formatting",
?_assertEqual(iolist_to_binary(["My pid is hello"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
[{pid, ["My pid is ", pid], ["Unknown Pid"]}]
)))
},
{"Metadata can have extra formatting1",
?_assertEqual(iolist_to_binary(["servername"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}, {server, servername}],
[]),
[{server, {pid, ["(", pid, ")"], ["(Unknown Server)"]}}]
)))
},
{"Metadata can have extra formatting2",
?_assertEqual(iolist_to_binary(["(hello)"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
[{server, {pid, ["(", pid, ")"], ["(Unknown Server)"]}}]
)))
},
{"Metadata can have extra formatting3",
?_assertEqual(iolist_to_binary(["(Unknown Server)"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[],
[]),
[{server, {pid, ["(", pid, ")"], ["(Unknown Server)"]}}]
)))
},
{"Metadata can be printed in its enterity",
?_assertEqual(iolist_to_binary(["bar=2 baz=3 foo=1"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{foo, 1}, {bar, 2}, {baz, 3}],
[]),
[metadata]
)))
},
{"Metadata can be printed in its enterity with custom seperators",
?_assertEqual(iolist_to_binary(["bar->2, baz->3, foo->1"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{foo, 1}, {bar, 2}, {baz, 3}],
[]),
[{metadata, "->", ", "}]
)))
},
{"Metadata can have extra formatting with width 1",
?_assertEqual(iolist_to_binary(["(hello )(hello )(hello)(hello)(hello)"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
["(", {pid, [pid], "", 10}, ")",
"(", {pid, [pid], "", {bad_align, 10}}, ")",
"(", {pid, [pid], "", bad10}, ")",
"(", {pid, [pid], "", {right, bad20}}, ")",
"(", {pid, [pid], "", {bad_align, bad20}}, ")"]
)))
},
{"Metadata can have extra formatting with width 2",
?_assertEqual(iolist_to_binary(["(hello )"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
["(", {pid, [pid], "", {left, 10}}, ")"]
)))
},
{"Metadata can have extra formatting with width 3",
?_assertEqual(iolist_to_binary(["( hello)"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
["(", {pid, [pid], "", {right, 10}}, ")"]
)))
},
{"Metadata can have extra formatting with width 4",
?_assertEqual(iolist_to_binary(["( hello )"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
["(", {pid, [pid], "", {centre, 10}}, ")"]
)))
},
{"Metadata can have extra formatting with width 5",
?_assertEqual(iolist_to_binary(["error |hello ! ( hello )"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}],
[]),
[{x, "", [severity, {blank, "|"}, pid], 10}, "!", blank, "(", {pid, [pid], "", {centre, 10}}, ")"]
)))
},
{"Metadata can have extra formatting with width 6",
?_assertEqual(iolist_to_binary([Time, Date, " bar=2 baz=3 foo=1 pid=hello EMessage"]),
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, hello}, {foo, 1}, {bar, 2}, {baz, 3}],
[]),
[{x, "", [time]}, {x, "", [date], 20}, blank, {x, "", [metadata], 30}, blank, {x, "", [sev], 10}, message, {message, message, "", {right, 20}}]
)))
},
{"Uppercase Severity Formatting - DEBUG",
?_assertEqual(<<"DEBUG Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
debug,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - INFO",
?_assertEqual(<<"INFO Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
info,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - NOTICE",
?_assertEqual(<<"NOTICE Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
notice,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - WARNING",
?_assertEqual(<<"WARNING Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
warning,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - ERROR",
?_assertEqual(<<"ERROR Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
error,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - CRITICAL",
?_assertEqual(<<"CRITICAL Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
critical,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - ALERT",
?_assertEqual(<<"ALERT Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
alert,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"Uppercase Severity Formatting - EMERGENCY",
?_assertEqual(<<"EMERGENCY Simplist Format">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
emergency,
[{pid, self()}],
[]),
[severity_upper, " Simplist Format"])))
},
{"pterm presence test",
%% skip test on OTP < 21
case list_to_integer(erlang:system_info(otp_release)) >= 21 of
true ->
?_assertEqual(<<"Pterm is: something">>,
begin
persistent_term:put(thing, something),
Ret = iolist_to_binary(format(lager_msg:new("Message",
Now,
emergency,
[{pid, self()}],
[]),
["Pterm is: ", {pterm, thing}])),
persistent_term:erase(thing),
Ret
end);
false -> ?_assert(true)
end
},
{"pterm absence test",
?_assertEqual(<<"Pterm is: nothing">>,
iolist_to_binary(format(lager_msg:new("Message",
Now,
emergency,
[{pid, self()}],
[]),
["Pterm is: ", {pterm, thing, "nothing"}])))
},
{"node formatting basic",
begin
[N, "foo"] = format(lager_msg:new("Message",
Now,
info,
[{pid, self()}],
[]),
[node, "foo"]),
?_assertNotMatch(nomatch, re:run(N, <<"@">>))
end
}
].
-endif.

+ 322
- 322
src/formater/lager_format.erl Voir le fichier

@ -23,114 +23,114 @@
-export([format/3, format/4]).
-record(options, {
chomp = false :: boolean()
}).
chomp = false :: boolean()
}).
format(FmtStr, Args, MaxLen) ->
format(FmtStr, Args, MaxLen, []).
format(FmtStr, Args, MaxLen, []).
format([], [], _, _) ->
"";
"";
format(FmtStr, Args, MaxLen, Opts) when is_atom(FmtStr) ->
format(atom_to_list(FmtStr), Args, MaxLen, Opts);
format(atom_to_list(FmtStr), Args, MaxLen, Opts);
format(FmtStr, Args, MaxLen, Opts) when is_binary(FmtStr) ->
format(binary_to_list(FmtStr), Args, MaxLen, Opts);
format(binary_to_list(FmtStr), Args, MaxLen, Opts);
format(FmtStr, Args, MaxLen, Opts) when is_list(FmtStr) ->
case io_lib:deep_char_list(FmtStr) of
true ->
Options = make_options(Opts, #options{}),
Cs = collect(FmtStr, Args),
{Cs2, MaxLen2} = build(Cs, [], MaxLen, Options),
%% count how many terms remain
{Count, StrLen} = lists:foldl(
fun({_C, _As, _F, _Adj, _P, _Pad, _Enc}, {Terms, Chars}) ->
{Terms + 1, Chars};
(_, {Terms, Chars}) ->
{Terms, Chars + 1}
end, {0, 0}, Cs2),
build2(Cs2, Count, MaxLen2 - StrLen);
false ->
erlang:error(badarg)
end;
case io_lib:deep_char_list(FmtStr) of
true ->
Options = make_options(Opts, #options{}),
Cs = collect(FmtStr, Args),
{Cs2, MaxLen2} = build(Cs, [], MaxLen, Options),
%% count how many terms remain
{Count, StrLen} = lists:foldl(
fun({_C, _As, _F, _Adj, _P, _Pad, _Enc}, {Terms, Chars}) ->
{Terms + 1, Chars};
(_, {Terms, Chars}) ->
{Terms, Chars + 1}
end, {0, 0}, Cs2),
build2(Cs2, Count, MaxLen2 - StrLen);
false ->
erlang:error(badarg)
end;
format(_FmtStr, _Args, _MaxLen, _Opts) ->
erlang:error(badarg).
erlang:error(badarg).
collect([$~|Fmt0], Args0) ->
{C,Fmt1,Args1} = collect_cseq(Fmt0, Args0),
[C|collect(Fmt1, Args1)];
collect([C|Fmt], Args) ->
[C|collect(Fmt, Args)];
collect([$~ | Fmt0], Args0) ->
{C, Fmt1, Args1} = collect_cseq(Fmt0, Args0),
[C | collect(Fmt1, Args1)];
collect([C | Fmt], Args) ->
[C | collect(Fmt, Args)];
collect([], []) -> [].
collect_cseq(Fmt0, Args0) ->
{F,Ad,Fmt1,Args1} = field_width(Fmt0, Args0),
{P,Fmt2,Args2} = precision(Fmt1, Args1),
{Pad,Fmt3,Args3} = pad_char(Fmt2, Args2),
{Encoding,Fmt4,Args4} = encoding(Fmt3, Args3),
{C,As,Fmt5,Args5} = collect_cc(Fmt4, Args4),
{{C,As,F,Ad,P,Pad,Encoding},Fmt5,Args5}.
encoding([$t|Fmt],Args) ->
{unicode,Fmt,Args};
encoding(Fmt,Args) ->
{latin1,Fmt,Args}.
field_width([$-|Fmt0], Args0) ->
{F,Fmt,Args} = field_value(Fmt0, Args0),
field_width(-F, Fmt, Args);
{F, Ad, Fmt1, Args1} = field_width(Fmt0, Args0),
{P, Fmt2, Args2} = precision(Fmt1, Args1),
{Pad, Fmt3, Args3} = pad_char(Fmt2, Args2),
{Encoding, Fmt4, Args4} = encoding(Fmt3, Args3),
{C, As, Fmt5, Args5} = collect_cc(Fmt4, Args4),
{{C, As, F, Ad, P, Pad, Encoding}, Fmt5, Args5}.
encoding([$t | Fmt], Args) ->
{unicode, Fmt, Args};
encoding(Fmt, Args) ->
{latin1, Fmt, Args}.
field_width([$- | Fmt0], Args0) ->
{F, Fmt, Args} = field_value(Fmt0, Args0),
field_width(-F, Fmt, Args);
field_width(Fmt0, Args0) ->
{F,Fmt,Args} = field_value(Fmt0, Args0),
field_width(F, Fmt, Args).
{F, Fmt, Args} = field_value(Fmt0, Args0),
field_width(F, Fmt, Args).
field_width(F, Fmt, Args) when F < 0 ->
{-F,left,Fmt,Args};
{-F, left, Fmt, Args};
field_width(F, Fmt, Args) when F >= 0 ->
{F,right,Fmt,Args}.
{F, right, Fmt, Args}.
precision([$.|Fmt], Args) ->
field_value(Fmt, Args);
precision([$. | Fmt], Args) ->
field_value(Fmt, Args);
precision(Fmt, Args) ->
{none,Fmt,Args}.
{none, Fmt, Args}.
field_value([$*|Fmt], [A|Args]) when is_integer(A) ->
{A,Fmt,Args};
field_value([C|Fmt], Args) when is_integer(C), C >= $0, C =< $9 ->
field_value([C|Fmt], Args, 0);
field_value([$* | Fmt], [A | Args]) when is_integer(A) ->
{A, Fmt, Args};
field_value([C | Fmt], Args) when is_integer(C), C >= $0, C =< $9 ->
field_value([C | Fmt], Args, 0);
field_value(Fmt, Args) ->
{none,Fmt,Args}.
{none, Fmt, Args}.
field_value([C|Fmt], Args, F) when is_integer(C), C >= $0, C =< $9 ->
field_value(Fmt, Args, 10*F + (C - $0));
field_value([C | Fmt], Args, F) when is_integer(C), C >= $0, C =< $9 ->
field_value(Fmt, Args, 10 * F + (C - $0));
field_value(Fmt, Args, F) -> %Default case
{F,Fmt,Args}.
{F, Fmt, Args}.
pad_char([$.,$*|Fmt], [Pad|Args]) -> {Pad,Fmt,Args};
pad_char([$.,Pad|Fmt], Args) -> {Pad,Fmt,Args};
pad_char(Fmt, Args) -> {$\s,Fmt,Args}.
pad_char([$., $* | Fmt], [Pad | Args]) -> {Pad, Fmt, Args};
pad_char([$., Pad | Fmt], Args) -> {Pad, Fmt, Args};
pad_char(Fmt, Args) -> {$\s, Fmt, Args}.
%% collect_cc([FormatChar], [Argument]) ->
%% {Control,[ControlArg],[FormatChar],[Arg]}.
%% Here we collect the argments for each control character.
%% Be explicit to cause failure early.
collect_cc([$w|Fmt], [A|Args]) -> {$w,[A],Fmt,Args};
collect_cc([$p|Fmt], [A|Args]) -> {$p,[A],Fmt,Args};
collect_cc([$W|Fmt], [A,Depth|Args]) -> {$W,[A,Depth],Fmt,Args};
collect_cc([$P|Fmt], [A,Depth|Args]) -> {$P,[A,Depth],Fmt,Args};
collect_cc([$s|Fmt], [A|Args]) -> {$s,[A],Fmt,Args};
collect_cc([$e|Fmt], [A|Args]) -> {$e,[A],Fmt,Args};
collect_cc([$f|Fmt], [A|Args]) -> {$f,[A],Fmt,Args};
collect_cc([$g|Fmt], [A|Args]) -> {$g,[A],Fmt,Args};
collect_cc([$b|Fmt], [A|Args]) -> {$b,[A],Fmt,Args};
collect_cc([$B|Fmt], [A|Args]) -> {$B,[A],Fmt,Args};
collect_cc([$x|Fmt], [A,Prefix|Args]) -> {$x,[A,Prefix],Fmt,Args};
collect_cc([$X|Fmt], [A,Prefix|Args]) -> {$X,[A,Prefix],Fmt,Args};
collect_cc([$+|Fmt], [A|Args]) -> {$+,[A],Fmt,Args};
collect_cc([$#|Fmt], [A|Args]) -> {$#,[A],Fmt,Args};
collect_cc([$c|Fmt], [A|Args]) -> {$c,[A],Fmt,Args};
collect_cc([$~|Fmt], Args) when is_list(Args) -> {$~,[],Fmt,Args};
collect_cc([$n|Fmt], Args) when is_list(Args) -> {$n,[],Fmt,Args};
collect_cc([$i|Fmt], [A|Args]) -> {$i,[A],Fmt,Args}.
collect_cc([$w | Fmt], [A | Args]) -> {$w, [A], Fmt, Args};
collect_cc([$p | Fmt], [A | Args]) -> {$p, [A], Fmt, Args};
collect_cc([$W | Fmt], [A, Depth | Args]) -> {$W, [A, Depth], Fmt, Args};
collect_cc([$P | Fmt], [A, Depth | Args]) -> {$P, [A, Depth], Fmt, Args};
collect_cc([$s | Fmt], [A | Args]) -> {$s, [A], Fmt, Args};
collect_cc([$e | Fmt], [A | Args]) -> {$e, [A], Fmt, Args};
collect_cc([$f | Fmt], [A | Args]) -> {$f, [A], Fmt, Args};
collect_cc([$g | Fmt], [A | Args]) -> {$g, [A], Fmt, Args};
collect_cc([$b | Fmt], [A | Args]) -> {$b, [A], Fmt, Args};
collect_cc([$B | Fmt], [A | Args]) -> {$B, [A], Fmt, Args};
collect_cc([$x | Fmt], [A, Prefix | Args]) -> {$x, [A, Prefix], Fmt, Args};
collect_cc([$X | Fmt], [A, Prefix | Args]) -> {$X, [A, Prefix], Fmt, Args};
collect_cc([$+ | Fmt], [A | Args]) -> {$+, [A], Fmt, Args};
collect_cc([$# | Fmt], [A | Args]) -> {$#, [A], Fmt, Args};
collect_cc([$c | Fmt], [A | Args]) -> {$c, [A], Fmt, Args};
collect_cc([$~ | Fmt], Args) when is_list(Args) -> {$~, [], Fmt, Args};
collect_cc([$n | Fmt], Args) when is_list(Args) -> {$n, [], Fmt, Args};
collect_cc([$i | Fmt], [A | Args]) -> {$i, [A], Fmt, Args}.
%% build([Control], Pc, Indentation) -> [Char].
@ -138,29 +138,29 @@ collect_cc([$i|Fmt], [A|Args]) -> {$i,[A],Fmt,Args}.
%% remaining and only calculate indentation when necessary. Must also
%% be smart when calculating indentation for characters in format.
build([{$n, _, _, _, _, _, _}], Acc, MaxLen, #options{chomp=true}) ->
%% trailing ~n, ignore
{lists:reverse(Acc), MaxLen};
build([{C,As,F,Ad,P,Pad,Enc}|Cs], Acc, MaxLen, O) ->
{S, MaxLen2} = control(C, As, F, Ad, P, Pad, Enc, MaxLen),
build(Cs, [S|Acc], MaxLen2, O);
build([$\n], Acc, MaxLen, #options{chomp=true}) ->
%% trailing \n, ignore
{lists:reverse(Acc), MaxLen};
build([$\n|Cs], Acc, MaxLen, O) ->
build(Cs, [$\n|Acc], MaxLen - 1, O);
build([$\t|Cs], Acc, MaxLen, O) ->
build(Cs, [$\t|Acc], MaxLen - 1, O);
build([C|Cs], Acc, MaxLen, O) ->
build(Cs, [C|Acc], MaxLen - 1, O);
build([{$n, _, _, _, _, _, _}], Acc, MaxLen, #options{chomp = true}) ->
%% trailing ~n, ignore
{lists:reverse(Acc), MaxLen};
build([{C, As, F, Ad, P, Pad, Enc} | Cs], Acc, MaxLen, O) ->
{S, MaxLen2} = control(C, As, F, Ad, P, Pad, Enc, MaxLen),
build(Cs, [S | Acc], MaxLen2, O);
build([$\n], Acc, MaxLen, #options{chomp = true}) ->
%% trailing \n, ignore
{lists:reverse(Acc), MaxLen};
build([$\n | Cs], Acc, MaxLen, O) ->
build(Cs, [$\n | Acc], MaxLen - 1, O);
build([$\t | Cs], Acc, MaxLen, O) ->
build(Cs, [$\t | Acc], MaxLen - 1, O);
build([C | Cs], Acc, MaxLen, O) ->
build(Cs, [C | Acc], MaxLen - 1, O);
build([], Acc, MaxLen, _O) ->
{lists:reverse(Acc), MaxLen}.
{lists:reverse(Acc), MaxLen}.
build2([{C,As,F,Ad,P,Pad,Enc}|Cs], Count, MaxLen) ->
{S, Len} = control2(C, As, F, Ad, P, Pad, Enc, MaxLen div Count),
[S|build2(Cs, Count - 1, MaxLen - Len)];
build2([C|Cs], Count, MaxLen) ->
[C|build2(Cs, Count, MaxLen)];
build2([{C, As, F, Ad, P, Pad, Enc} | Cs], Count, MaxLen) ->
{S, Len} = control2(C, As, F, Ad, P, Pad, Enc, MaxLen div Count),
[S | build2(Cs, Count - 1, MaxLen - Len)];
build2([C | Cs], Count, MaxLen) ->
[C | build2(Cs, Count, MaxLen)];
build2([], _, _) -> [].
%% control(FormatChar, [Argument], FieldWidth, Adjust, Precision, PadChar,
@ -169,128 +169,128 @@ build2([], _, _) -> [].
%% Field widths and precisions have already been calculated.
control($e, [A], F, Adj, P, Pad, _Enc, L) when is_float(A) ->
Res = fwrite_e(A, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
Res = fwrite_e(A, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
control($f, [A], F, Adj, P, Pad, _Enc, L) when is_float(A) ->
Res = fwrite_f(A, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
Res = fwrite_f(A, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
control($g, [A], F, Adj, P, Pad, _Enc, L) when is_float(A) ->
Res = fwrite_g(A, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
Res = fwrite_g(A, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
control($b, [A], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
Res = unprefixed_integer(A, F, Adj, base(P), Pad, true),
{Res, L - lists:flatlength(Res)};
Res = unprefixed_integer(A, F, Adj, base(P), Pad, true),
{Res, L - lists:flatlength(Res)};
control($B, [A], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
Res = unprefixed_integer(A, F, Adj, base(P), Pad, false),
{Res, L - lists:flatlength(Res)};
control($x, [A,Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A),
is_atom(Prefix) ->
Res = prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), true),
{Res, L - lists:flatlength(Res)};
control($x, [A,Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
Res = prefixed_integer(A, F, Adj, base(P), Pad, Prefix, true),
{Res, L - lists:flatlength(Res)};
control($X, [A,Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A),
is_atom(Prefix) ->
Res = prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), false),
{Res, L - lists:flatlength(Res)};
control($X, [A,Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
Res = prefixed_integer(A, F, Adj, base(P), Pad, Prefix, false),
{Res, L - lists:flatlength(Res)};
Res = unprefixed_integer(A, F, Adj, base(P), Pad, false),
{Res, L - lists:flatlength(Res)};
control($x, [A, Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A),
is_atom(Prefix) ->
Res = prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), true),
{Res, L - lists:flatlength(Res)};
control($x, [A, Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
Res = prefixed_integer(A, F, Adj, base(P), Pad, Prefix, true),
{Res, L - lists:flatlength(Res)};
control($X, [A, Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A),
is_atom(Prefix) ->
Res = prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), false),
{Res, L - lists:flatlength(Res)};
control($X, [A, Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
Res = prefixed_integer(A, F, Adj, base(P), Pad, Prefix, false),
{Res, L - lists:flatlength(Res)};
control($+, [A], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
Base = base(P),
Prefix = [integer_to_list(Base), $#],
Res = prefixed_integer(A, F, Adj, Base, Pad, Prefix, true),
{Res, L - lists:flatlength(Res)};
Base = base(P),
Prefix = [integer_to_list(Base), $#],
Res = prefixed_integer(A, F, Adj, Base, Pad, Prefix, true),
{Res, L - lists:flatlength(Res)};
control($#, [A], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
Base = base(P),
Prefix = [integer_to_list(Base), $#],
Res = prefixed_integer(A, F, Adj, Base, Pad, Prefix, false),
{Res, L - lists:flatlength(Res)};
Base = base(P),
Prefix = [integer_to_list(Base), $#],
Res = prefixed_integer(A, F, Adj, Base, Pad, Prefix, false),
{Res, L - lists:flatlength(Res)};
control($c, [A], F, Adj, P, Pad, unicode, L) when is_integer(A) ->
Res = char(A, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
Res = char(A, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
control($c, [A], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
Res = char(A band 255, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
Res = char(A band 255, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
control($~, [], F, Adj, P, Pad, _Enc, L) ->
Res = char($~, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
Res = char($~, F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
control($n, [], F, Adj, P, Pad, _Enc, L) ->
Res = newline(F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
Res = newline(F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
control($i, [_A], _F, _Adj, _P, _Pad, _Enc, L) ->
{[], L};
{[], L};
control($s, [A], F, Adj, P, Pad, _Enc, L) when is_atom(A) ->
Res = string(atom_to_list(A), F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
Res = string(atom_to_list(A), F, Adj, P, Pad),
{Res, L - lists:flatlength(Res)};
control(C, A, F, Adj, P, Pad, Enc, L) ->
%% save this for later - these are all the 'large' terms
{{C, A, F, Adj, P, Pad, Enc}, L}.
%% save this for later - these are all the 'large' terms
{{C, A, F, Adj, P, Pad, Enc}, L}.
control2($w, [A], F, Adj, P, Pad, _Enc, L) ->
Term = lager_trunc_io:fprint(A, L, [{lists_as_strings, false}]),
Res = term(Term, F, Adj, P, Pad),
{Res, lists:flatlength(Res)};
Term = lager_trunc_io:fprint(A, L, [{lists_as_strings, false}]),
Res = term(Term, F, Adj, P, Pad),
{Res, lists:flatlength(Res)};
control2($p, [A], _F, _Adj, _P, _Pad, _Enc, L) ->
Term = lager_trunc_io:fprint(A, L, [{lists_as_strings, true}]),
{Term, lists:flatlength(Term)};
control2($W, [A,Depth], F, Adj, P, Pad, _Enc, L) when is_integer(Depth) ->
Term = lager_trunc_io:fprint(A, L, [{depth, Depth}, {lists_as_strings, false}]),
Res = term(Term, F, Adj, P, Pad),
{Res, lists:flatlength(Res)};
control2($P, [A,Depth], _F, _Adj, _P, _Pad, _Enc, L) when is_integer(Depth) ->
Term = lager_trunc_io:fprint(A, L, [{depth, Depth}, {lists_as_strings, true}]),
{Term, lists:flatlength(Term)};
Term = lager_trunc_io:fprint(A, L, [{lists_as_strings, true}]),
{Term, lists:flatlength(Term)};
control2($W, [A, Depth], F, Adj, P, Pad, _Enc, L) when is_integer(Depth) ->
Term = lager_trunc_io:fprint(A, L, [{depth, Depth}, {lists_as_strings, false}]),
Res = term(Term, F, Adj, P, Pad),
{Res, lists:flatlength(Res)};
control2($P, [A, Depth], _F, _Adj, _P, _Pad, _Enc, L) when is_integer(Depth) ->
Term = lager_trunc_io:fprint(A, L, [{depth, Depth}, {lists_as_strings, true}]),
{Term, lists:flatlength(Term)};
control2($s, [L0], F, Adj, P, Pad, latin1, L) ->
List = lager_trunc_io:fprint(iolist_to_chars(L0), L, [{force_strings, true}]),
Res = string(List, F, Adj, P, Pad),
{Res, lists:flatlength(Res)};
List = lager_trunc_io:fprint(iolist_to_chars(L0), L, [{force_strings, true}]),
Res = string(List, F, Adj, P, Pad),
{Res, lists:flatlength(Res)};
control2($s, [L0], F, Adj, P, Pad, unicode, L) ->
List = lager_trunc_io:fprint(cdata_to_chars(L0), L, [{force_strings, true}]),
Res = uniconv(string(List, F, Adj, P, Pad)),
{Res, lists:flatlength(Res)}.
iolist_to_chars([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 ->
[C | iolist_to_chars(Cs)];
iolist_to_chars([I|Cs]) ->
[iolist_to_chars(I) | iolist_to_chars(Cs)];
List = lager_trunc_io:fprint(cdata_to_chars(L0), L, [{force_strings, true}]),
Res = uniconv(string(List, F, Adj, P, Pad)),
{Res, lists:flatlength(Res)}.
iolist_to_chars([C | Cs]) when is_integer(C), C >= $\000, C =< $\377 ->
[C | iolist_to_chars(Cs)];
iolist_to_chars([I | Cs]) ->
[iolist_to_chars(I) | iolist_to_chars(Cs)];
iolist_to_chars([]) ->
[];
[];
iolist_to_chars(B) when is_binary(B) ->
binary_to_list(B).
binary_to_list(B).
cdata_to_chars([C|Cs]) when is_integer(C), C >= $\000 ->
[C | cdata_to_chars(Cs)];
cdata_to_chars([I|Cs]) ->
[cdata_to_chars(I) | cdata_to_chars(Cs)];
cdata_to_chars([C | Cs]) when is_integer(C), C >= $\000 ->
[C | cdata_to_chars(Cs)];
cdata_to_chars([I | Cs]) ->
[cdata_to_chars(I) | cdata_to_chars(Cs)];
cdata_to_chars([]) ->
[];
[];
cdata_to_chars(B) when is_binary(B) ->
case catch unicode:characters_to_list(B) of
L when is_list(L) -> L;
_ -> binary_to_list(B)
end.
case catch unicode:characters_to_list(B) of
L when is_list(L) -> L;
_ -> binary_to_list(B)
end.
make_options([], Options) ->
Options;
make_options([{chomp, Bool}|T], Options) when is_boolean(Bool) ->
make_options(T, Options#options{chomp=Bool}).
Options;
make_options([{chomp, Bool} | T], Options) when is_boolean(Bool) ->
make_options(T, Options#options{chomp = Bool}).
-ifdef(UNICODE_AS_BINARIES).
uniconv(C) ->
unicode:characters_to_binary(C,unicode).
unicode:characters_to_binary(C, unicode).
-else.
uniconv(C) ->
C.
C.
-endif.
%% Default integer base
base(none) ->
10;
10;
base(B) when is_integer(B) ->
B.
B.
%% term(TermList, Field, Adjust, Precision, PadChar)
%% Output the characters in a term.
@ -300,33 +300,33 @@ base(B) when is_integer(B) ->
term(T, none, _Adj, none, _Pad) -> T;
term(T, none, Adj, P, Pad) -> term(T, P, Adj, P, Pad);
term(T, F, Adj, P0, Pad) ->
L = lists:flatlength(T),
P = case P0 of none -> erlang:min(L, F); _ -> P0 end,
if
L > P ->
adjust(chars($*, P), chars(Pad, F-P), Adj);
F >= P ->
adjust(T, chars(Pad, F-L), Adj)
end.
L = lists:flatlength(T),
P = case P0 of none -> erlang:min(L, F); _ -> P0 end,
if
L > P ->
adjust(chars($*, P), chars(Pad, F - P), Adj);
F >= P ->
adjust(T, chars(Pad, F - L), Adj)
end.
%% fwrite_e(Float, Field, Adjust, Precision, PadChar)
fwrite_e(Fl, none, Adj, none, Pad) -> %Default values
fwrite_e(Fl, none, Adj, 6, Pad);
fwrite_e(Fl, none, Adj, 6, Pad);
fwrite_e(Fl, none, _Adj, P, _Pad) when P >= 2 ->
float_e(Fl, float_data(Fl), P);
float_e(Fl, float_data(Fl), P);
fwrite_e(Fl, F, Adj, none, Pad) ->
fwrite_e(Fl, F, Adj, 6, Pad);
fwrite_e(Fl, F, Adj, 6, Pad);
fwrite_e(Fl, F, Adj, P, Pad) when P >= 2 ->
term(float_e(Fl, float_data(Fl), P), F, Adj, F, Pad).
term(float_e(Fl, float_data(Fl), P), F, Adj, F, Pad).
float_e(Fl, Fd, P) when Fl < 0.0 -> %Negative numbers
[$-|float_e(-Fl, Fd, P)];
float_e(_Fl, {Ds,E}, P) ->
case float_man(Ds, 1, P-1) of
{[$0|Fs],true} -> [[$1|Fs]|float_exp(E)];
{Fs,false} -> [Fs|float_exp(E-1)]
end.
[$- | float_e(-Fl, Fd, P)];
float_e(_Fl, {Ds, E}, P) ->
case float_man(Ds, 1, P - 1) of
{[$0 | Fs], true} -> [[$1 | Fs] | float_exp(E)];
{Fs, false} -> [Fs | float_exp(E - 1)]
end.
%% float_man([Digit], Icount, Dcount) -> {[Chars],CarryFlag}.
%% Generate the characters in the mantissa from the digits with Icount
@ -334,67 +334,67 @@ float_e(_Fl, {Ds,E}, P) ->
%% caller decide what to do at top.
float_man(Ds, 0, Dc) ->
{Cs,C} = float_man(Ds, Dc),
{[$.|Cs],C};
float_man([D|Ds], I, Dc) ->
case float_man(Ds, I-1, Dc) of
{Cs,true} when D =:= $9 -> {[$0|Cs],true};
{Cs,true} -> {[D+1|Cs],false};
{Cs,false} -> {[D|Cs],false}
end;
{Cs, C} = float_man(Ds, Dc),
{[$. | Cs], C};
float_man([D | Ds], I, Dc) ->
case float_man(Ds, I - 1, Dc) of
{Cs, true} when D =:= $9 -> {[$0 | Cs], true};
{Cs, true} -> {[D + 1 | Cs], false};
{Cs, false} -> {[D | Cs], false}
end;
float_man([], I, Dc) -> %Pad with 0's
{string:chars($0, I, [$.|string:chars($0, Dc)]),false}.
float_man([D|_], 0) when D >= $5 -> {[],true};
float_man([_|_], 0) -> {[],false};
float_man([D|Ds], Dc) ->
case float_man(Ds, Dc-1) of
{Cs,true} when D =:= $9 -> {[$0|Cs],true};
{Cs,true} -> {[D+1|Cs],false};
{Cs,false} -> {[D|Cs],false}
end;
float_man([], Dc) -> {string:chars($0, Dc),false}. %Pad with 0's
{string:chars($0, I, [$. | string:chars($0, Dc)]), false}.
float_man([D | _], 0) when D >= $5 -> {[], true};
float_man([_ | _], 0) -> {[], false};
float_man([D | Ds], Dc) ->
case float_man(Ds, Dc - 1) of
{Cs, true} when D =:= $9 -> {[$0 | Cs], true};
{Cs, true} -> {[D + 1 | Cs], false};
{Cs, false} -> {[D | Cs], false}
end;
float_man([], Dc) -> {string:chars($0, Dc), false}. %Pad with 0's
%% float_exp(Exponent) -> [Char].
%% Generate the exponent of a floating point number. Always include sign.
float_exp(E) when E >= 0 ->
[$e,$+|integer_to_list(E)];
[$e, $+ | integer_to_list(E)];
float_exp(E) ->
[$e|integer_to_list(E)].
[$e | integer_to_list(E)].
%% fwrite_f(FloatData, Field, Adjust, Precision, PadChar)
fwrite_f(Fl, none, Adj, none, Pad) -> %Default values
fwrite_f(Fl, none, Adj, 6, Pad);
fwrite_f(Fl, none, Adj, 6, Pad);
fwrite_f(Fl, none, _Adj, P, _Pad) when P >= 1 ->
float_f(Fl, float_data(Fl), P);
float_f(Fl, float_data(Fl), P);
fwrite_f(Fl, F, Adj, none, Pad) ->
fwrite_f(Fl, F, Adj, 6, Pad);
fwrite_f(Fl, F, Adj, 6, Pad);
fwrite_f(Fl, F, Adj, P, Pad) when P >= 1 ->
term(float_f(Fl, float_data(Fl), P), F, Adj, F, Pad).
term(float_f(Fl, float_data(Fl), P), F, Adj, F, Pad).
float_f(Fl, Fd, P) when Fl < 0.0 ->
[$-|float_f(-Fl, Fd, P)];
float_f(Fl, {Ds,E}, P) when E =< 0 ->
float_f(Fl, {string:chars($0, -E+1, Ds),1}, P); %Prepend enough 0's
float_f(_Fl, {Ds,E}, P) ->
case float_man(Ds, E, P) of
{Fs,true} -> "1" ++ Fs; %Handle carry
{Fs,false} -> Fs
end.
[$- | float_f(-Fl, Fd, P)];
float_f(Fl, {Ds, E}, P) when E =< 0 ->
float_f(Fl, {string:chars($0, -E + 1, Ds), 1}, P); %Prepend enough 0's
float_f(_Fl, {Ds, E}, P) ->
case float_man(Ds, E, P) of
{Fs, true} -> "1" ++ Fs; %Handle carry
{Fs, false} -> Fs
end.
%% float_data([FloatChar]) -> {[Digit],Exponent}
float_data(Fl) ->
float_data(float_to_list(Fl), []).
float_data(float_to_list(Fl), []).
float_data([$e|E], Ds) ->
{lists:reverse(Ds),list_to_integer(E)+1};
float_data([D|Cs], Ds) when D >= $0, D =< $9 ->
float_data(Cs, [D|Ds]);
float_data([_|Cs], Ds) ->
float_data(Cs, Ds).
float_data([$e | E], Ds) ->
{lists:reverse(Ds), list_to_integer(E) + 1};
float_data([D | Cs], Ds) when D >= $0, D =< $9 ->
float_data(Cs, [D | Ds]);
float_data([_ | Cs], Ds) ->
float_data(Cs, Ds).
%% fwrite_g(Float, Field, Adjust, Precision, PadChar)
%% Use the f form if Float is >= 0.1 and < 1.0e4,
@ -402,80 +402,80 @@ float_data([_|Cs], Ds) ->
%% Precision always means the # of significant digits.
fwrite_g(Fl, F, Adj, none, Pad) ->
fwrite_g(Fl, F, Adj, 6, Pad);
fwrite_g(Fl, F, Adj, 6, Pad);
fwrite_g(Fl, F, Adj, P, Pad) when P >= 1 ->
A = abs(Fl),
E = if A < 1.0e-1 -> -2;
A < 1.0e0 -> -1;
A < 1.0e1 -> 0;
A < 1.0e2 -> 1;
A < 1.0e3 -> 2;
A < 1.0e4 -> 3;
true -> fwrite_f
end,
if P =< 1, E =:= -1;
P-1 > E, E >= -1 ->
fwrite_f(Fl, F, Adj, P-1-E, Pad);
P =< 1 ->
fwrite_e(Fl, F, Adj, 2, Pad);
true ->
fwrite_e(Fl, F, Adj, P, Pad)
end.
A = abs(Fl),
E = if A < 1.0e-1 -> -2;
A < 1.0e0 -> -1;
A < 1.0e1 -> 0;
A < 1.0e2 -> 1;
A < 1.0e3 -> 2;
A < 1.0e4 -> 3;
true -> fwrite_f
end,
if P =< 1, E =:= -1;
P - 1 > E, E >= -1 ->
fwrite_f(Fl, F, Adj, P - 1 - E, Pad);
P =< 1 ->
fwrite_e(Fl, F, Adj, 2, Pad);
true ->
fwrite_e(Fl, F, Adj, P, Pad)
end.
%% string(String, Field, Adjust, Precision, PadChar)
string(S, none, _Adj, none, _Pad) -> S;
string(S, F, Adj, none, Pad) ->
string_field(S, F, Adj, lists:flatlength(S), Pad);
string_field(S, F, Adj, lists:flatlength(S), Pad);
string(S, none, _Adj, P, Pad) ->
string_field(S, P, left, lists:flatlength(S), Pad);
string_field(S, P, left, lists:flatlength(S), Pad);
string(S, F, Adj, P, Pad) when F >= P ->
N = lists:flatlength(S),
if F > P ->
if N > P ->
adjust(flat_trunc(S, P), chars(Pad, F-P), Adj);
N < P ->
adjust([S|chars(Pad, P-N)], chars(Pad, F-P), Adj);
true -> % N == P
adjust(S, chars(Pad, F-P), Adj)
end;
true -> % F == P
string_field(S, F, Adj, N, Pad)
end.
N = lists:flatlength(S),
if F > P ->
if N > P ->
adjust(flat_trunc(S, P), chars(Pad, F - P), Adj);
N < P ->
adjust([S | chars(Pad, P - N)], chars(Pad, F - P), Adj);
true -> % N == P
adjust(S, chars(Pad, F - P), Adj)
end;
true -> % F == P
string_field(S, F, Adj, N, Pad)
end.
string_field(S, F, _Adj, N, _Pad) when N > F ->
flat_trunc(S, F);
flat_trunc(S, F);
string_field(S, F, Adj, N, Pad) when N < F ->
adjust(S, chars(Pad, F-N), Adj);
adjust(S, chars(Pad, F - N), Adj);
string_field(S, _, _, _, _) -> % N == F
S.
S.
%% unprefixed_integer(Int, Field, Adjust, Base, PadChar, Lowercase)
%% -> [Char].
unprefixed_integer(Int, F, Adj, Base, Pad, Lowercase)
when Base >= 2, Base =< 1+$Z-$A+10 ->
if Int < 0 ->
S = cond_lowercase(erlang:integer_to_list(-Int, Base), Lowercase),
term([$-|S], F, Adj, none, Pad);
true ->
S = cond_lowercase(erlang:integer_to_list(Int, Base), Lowercase),
term(S, F, Adj, none, Pad)
end.
when Base >= 2, Base =< 1 + $Z - $A + 10 ->
if Int < 0 ->
S = cond_lowercase(erlang:integer_to_list(-Int, Base), Lowercase),
term([$- | S], F, Adj, none, Pad);
true ->
S = cond_lowercase(erlang:integer_to_list(Int, Base), Lowercase),
term(S, F, Adj, none, Pad)
end.
%% prefixed_integer(Int, Field, Adjust, Base, PadChar, Prefix, Lowercase)
%% -> [Char].
prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase)
when Base >= 2, Base =< 1+$Z-$A+10 ->
if Int < 0 ->
S = cond_lowercase(erlang:integer_to_list(-Int, Base), Lowercase),
term([$-,Prefix|S], F, Adj, none, Pad);
true ->
S = cond_lowercase(erlang:integer_to_list(Int, Base), Lowercase),
term([Prefix|S], F, Adj, none, Pad)
end.
when Base >= 2, Base =< 1 + $Z - $A + 10 ->
if Int < 0 ->
S = cond_lowercase(erlang:integer_to_list(-Int, Base), Lowercase),
term([$-, Prefix | S], F, Adj, none, Pad);
true ->
S = cond_lowercase(erlang:integer_to_list(Int, Base), Lowercase),
term([Prefix | S], F, Adj, none, Pad)
end.
%% char(Char, Field, Adjust, Precision, PadChar) -> [Char].
@ -483,7 +483,7 @@ char(C, none, _Adj, none, _Pad) -> [C];
char(C, F, _Adj, none, _Pad) -> chars(C, F);
char(C, none, _Adj, P, _Pad) -> chars(C, P);
char(C, F, Adj, P, Pad) when F >= P ->
adjust(chars(C, P), chars(Pad, F - P), Adj).
adjust(chars(C, P), chars(Pad, F - P), Adj).
%% newline(Field, Adjust, Precision, PadChar) -> [Char].
@ -495,36 +495,36 @@ newline(F, right, _P, _Pad) -> chars($\n, F).
%%
adjust(Data, [], _) -> Data;
adjust(Data, Pad, left) -> [Data|Pad];
adjust(Data, Pad, right) -> [Pad|Data].
adjust(Data, Pad, left) -> [Data | Pad];
adjust(Data, Pad, right) -> [Pad | Data].
%% Flatten and truncate a deep list to at most N elements.
flat_trunc(List, N) when is_integer(N), N >= 0 ->
flat_trunc(List, N, []).
flat_trunc(List, N, []).
flat_trunc(L, 0, R) when is_list(L) ->
lists:reverse(R);
flat_trunc([H|T], N, R) ->
flat_trunc(T, N-1, [H|R]);
lists:reverse(R);
flat_trunc([H | T], N, R) ->
flat_trunc(T, N - 1, [H | R]);
flat_trunc([], _, R) ->
lists:reverse(R).
lists:reverse(R).
%% A deep version of string:chars/2,3
chars(_C, 0) ->
[];
[];
chars(C, 1) ->
[C];
[C];
chars(C, 2) ->
[C,C];
[C, C];
chars(C, 3) ->
[C,C,C];
[C, C, C];
chars(C, N) when is_integer(N), (N band 1) =:= 0 ->
S = chars(C, N bsr 1),
[S|S];
S = chars(C, N bsr 1),
[S | S];
chars(C, N) when is_integer(N) ->
S = chars(C, N bsr 1),
[C,S|S].
S = chars(C, N bsr 1),
[C, S | S].
%chars(C, N, Tail) ->
% [chars(C, N)|Tail].
@ -532,13 +532,13 @@ chars(C, N) when is_integer(N) ->
%% Lowercase conversion
cond_lowercase(String, true) ->
lowercase(String);
cond_lowercase(String,false) ->
String.
lowercase([H|T]) when is_integer(H), H >= $A, H =< $Z ->
[(H-$A+$a)|lowercase(T)];
lowercase([H|T]) ->
[H|lowercase(T)];
lowercase(String);
cond_lowercase(String, false) ->
String.
lowercase([H | T]) when is_integer(H), H >= $A, H =< $Z ->
[(H - $A + $a) | lowercase(T)];
lowercase([H | T]) ->
[H | lowercase(T)];
lowercase([]) ->
[].
[].

+ 643
- 643
src/misc/error_logger_lager_h.erl
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 38
- 38
src/misc/lager_backend_throttle.erl Voir le fichier

@ -27,7 +27,7 @@
-behaviour(gen_event).
-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
code_change/3]).
code_change/3]).
%%
%% Allow test code to verify that we're doing the needful.
@ -41,65 +41,65 @@
-endif.
-record(state, {
sink :: atom(),
hwm :: non_neg_integer(),
window_min :: non_neg_integer(),
async = true :: boolean()
}).
sink :: atom(),
hwm :: non_neg_integer(),
window_min :: non_neg_integer(),
async = true :: boolean()
}).
init([{sink, Sink}, Hwm, Window]) ->
lager_config:set({Sink, async}, true),
{ok, #state{sink=Sink, hwm=Hwm, window_min=Hwm - Window}}.
lager_config:set({Sink, async}, true),
{ok, #state{sink = Sink, hwm = Hwm, window_min = Hwm - Window}}.
handle_call(get_loglevel, State) ->
{ok, {mask, ?LOG_NONE}, State};
{ok, {mask, ?LOG_NONE}, State};
handle_call({set_loglevel, _Level}, State) ->
{ok, ok, State};
{ok, ok, State};
handle_call(_Request, State) ->
{ok, ok, State}.
handle_event({log, _Message},State) ->
{message_queue_len, Len} = erlang:process_info(self(), message_queue_len),
case {Len > State#state.hwm, Len < State#state.window_min, State#state.async} of
{true, _, true} ->
%% need to flip to sync mode
?TOGGLE_SYNC(),
lager_config:set({State#state.sink, async}, false),
{ok, State#state{async=false}};
{_, true, false} ->
%% need to flip to async mode
?TOGGLE_ASYNC(),
lager_config:set({State#state.sink, async}, true),
{ok, State#state{async=true}};
_ ->
%% nothing needs to change
{ok, State}
end;
{ok, ok, State}.
handle_event({log, _Message}, State) ->
{message_queue_len, Len} = erlang:process_info(self(), message_queue_len),
case {Len > State#state.hwm, Len < State#state.window_min, State#state.async} of
{true, _, true} ->
%% need to flip to sync mode
?TOGGLE_SYNC(),
lager_config:set({State#state.sink, async}, false),
{ok, State#state{async = false}};
{_, true, false} ->
%% need to flip to async mode
?TOGGLE_ASYNC(),
lager_config:set({State#state.sink, async}, true),
{ok, State#state{async = true}};
_ ->
%% nothing needs to change
{ok, State}
end;
handle_event(_Event, State) ->
{ok, State}.
{ok, State}.
handle_info(_Info, State) ->
{ok, State}.
{ok, State}.
%% @private
terminate(_Reason, _State) ->
ok.
ok.
%% @private
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
{ok, State}.
-ifdef(TEST).
test_get(Key) ->
get_default(ets:lookup(?ETS_TABLE, Key)).
get_default(ets:lookup(?ETS_TABLE, Key)).
test_increment(Key) ->
ets:insert(?ETS_TABLE,
{Key, test_get(Key) + 1}).
ets:insert(?ETS_TABLE,
{Key, test_get(Key) + 1}).
get_default([]) ->
0;
0;
get_default([{_Key, Value}]) ->
Value.
Value.
-endif.

+ 56
- 56
src/misc/lager_config.erl Voir le fichier

@ -21,7 +21,7 @@
-include("rum.hrl").
-export([new/0, new_sink/1, get/1, get/2, set/2,
global_get/1, global_get/2, global_set/2, cleanup/0]).
global_get/1, global_get/2, global_set/2, cleanup/0]).
-define(TBL, lager_config).
-define(GLOBAL, '_global').
@ -32,42 +32,42 @@
%% {{lager_event, loglevel}, Value} instead of {loglevel, Value}
new() ->
init(),
new_sink(?RumDefSink),
%% Need to be able to find the `lager_handler_watcher' for all handlers
insert_new({?GLOBAL, handlers}, []),
ok.
init(),
new_sink(?RumDefSink),
%% Need to be able to find the `lager_handler_watcher' for all handlers
insert_new({?GLOBAL, handlers}, []),
ok.
new_sink(Sink) ->
%% use insert_new here so that if we're in an appup we don't mess anything up
%%
%% until lager is completely started, allow all messages to go through
insert_new({Sink, loglevel}, {element(2, rumUtil:config_to_mask(debug)), []}).
%% use insert_new here so that if we're in an appup we don't mess anything up
%%
%% until lager is completely started, allow all messages to go through
insert_new({Sink, loglevel}, {element(2, rumUtil:config_to_mask(debug)), []}).
global_get(Key) ->
global_get(Key, undefined).
global_get(Key, undefined).
global_get(Key, Default) ->
get({?GLOBAL, Key}, Default).
get({?GLOBAL, Key}, Default).
global_set(Key, Value) ->
set({?GLOBAL, Key}, Value).
set({?GLOBAL, Key}, Value).
get({_Sink, _Key} = FullKey) ->
get(FullKey, undefined);
get(FullKey, undefined);
get(Key) ->
get({?RumDefSink, Key}, undefined).
get({?RumDefSink, Key}, undefined).
get({Sink, Key}, Default) ->
lookup({Sink, Key}, Default);
lookup({Sink, Key}, Default);
get(Key, Default) ->
get({?RumDefSink, Key}, Default).
get({?RumDefSink, Key}, Default).
set({Sink, Key}, Value) ->
insert({Sink, Key}, Value);
insert({Sink, Key}, Value);
set(Key, Value) ->
set({?RumDefSink, Key}, Value).
set({?RumDefSink, Key}, Value).
%% check if we can use persistent_term for config
%% persistent term was added in OTP 21.2 but we can't
@ -75,61 +75,61 @@ set(Key, Value) ->
%% for OTP 22
-ifdef(HAVE_PERSISTENT_TERM).
init() ->
ok.
ok.
insert(Key, Value) ->
persistent_term:put({?TBL, Key}, Value).
persistent_term:put({?TBL, Key}, Value).
insert_new(Key, Value) ->
try persistent_term:get({?TBL, Key}) of
_Value ->
false
catch error:badarg ->
insert(Key, Value),
true
end.
try persistent_term:get({?TBL, Key}) of
_Value ->
false
catch error:badarg ->
insert(Key, Value),
true
end.
lookup(Key, Default) ->
try persistent_term:get({?TBL, Key}) of
Value -> Value
catch
error:badarg ->
Default
end.
try persistent_term:get({?TBL, Key}) of
Value -> Value
catch
error:badarg ->
Default
end.
cleanup() ->
[ persistent_term:erase(K) || {{?TBL, _} = K, _} <- persistent_term:get() ].
[persistent_term:erase(K) || {{?TBL, _} = K, _} <- persistent_term:get()].
-else.
init() ->
%% set up the ETS configuration table
_ = try ets:new(?TBL, [named_table, public, set, {keypos, 1}, {read_concurrency, true}]) of
_Result ->
ok
catch
error:badarg ->
?INT_LOG(warning, "Table ~p already exists", [?TBL])
end.
%% set up the ETS configuration table
_ = try ets:new(?TBL, [named_table, public, set, {keypos, 1}, {read_concurrency, true}]) of
_Result ->
ok
catch
error:badarg ->
?INT_LOG(warning, "Table ~p already exists", [?TBL])
end.
insert(Key, Value) ->
ets:insert(?TBL, {Key, Value}).
ets:insert(?TBL, {Key, Value}).
insert_new(Key, Value) ->
ets:insert_new(?TBL, {Key, Value}).
ets:insert_new(?TBL, {Key, Value}).
lookup(Key, Default) ->
try
case ets:lookup(?TBL, Key) of
[] ->
Default;
[{Key, Res}] ->
Res
end
catch
_:_ ->
Default
end.
try
case ets:lookup(?TBL, Key) of
[] ->
Default;
[{Key, Res}] ->
Res
end
catch
_:_ ->
Default
end.
cleanup() -> ok.
-endif.

+ 499
- 499
src/misc/lager_console_backend.erl
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 254
- 254
src/misc/lager_crash_log.erl Voir le fichier

@ -44,84 +44,84 @@
%% callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
code_change/3]).
-export([start_link/6, start/6]).
-record(state, {
name :: string(),
fd :: pid() | undefined,
inode :: integer() | undefined,
ctime :: file:date_time() | undefined,
fmtmaxbytes :: integer(),
size :: integer(),
date :: undefined | string(),
count :: integer(),
flap=false :: boolean(),
rotator :: atom()
name :: string(),
fd :: pid() | undefined,
inode :: integer() | undefined,
ctime :: file:date_time() | undefined,
fmtmaxbytes :: integer(),
size :: integer(),
date :: undefined | string(),
count :: integer(),
flap = false :: boolean(),
rotator :: atom()
}).
%% @private
start_link(Filename, MaxBytes, Size, Date, Count, Rotator) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [Filename, MaxBytes,
Size, Date, Count, Rotator], []).
gen_server:start_link({local, ?MODULE}, ?MODULE, [Filename, MaxBytes,
Size, Date, Count, Rotator], []).
%% @private
start(Filename, MaxBytes, Size, Date, Count, Rotator) ->
gen_server:start({local, ?MODULE}, ?MODULE, [Filename, MaxBytes, Size,
Date, Count, Rotator], []).
gen_server:start({local, ?MODULE}, ?MODULE, [Filename, MaxBytes, Size,
Date, Count, Rotator], []).
%% @private
init([RelFilename, MaxBytes, Size, Date, Count, Rotator]) ->
Filename = rumUtil:expand_path(RelFilename),
case Rotator:open_logfile(Filename, false) of
{ok, {FD, Inode, Ctime, _Size}} ->
schedule_rotation(Date),
{ok, #state{name=Filename, fd=FD, inode=Inode, ctime=Ctime,
fmtmaxbytes=MaxBytes, size=Size, count=Count, date=Date,
rotator=Rotator}};
{error, Reason} ->
?INT_LOG(error, "Failed to open crash log file ~ts with error: ~s",
[Filename, file:format_error(Reason)]),
{ok, #state{name=Filename, fmtmaxbytes=MaxBytes, flap=true,
size=Size, count=Count, date=Date, rotator=Rotator}}
end.
Filename = rumUtil:expand_path(RelFilename),
case Rotator:open_logfile(Filename, false) of
{ok, {FD, Inode, Ctime, _Size}} ->
schedule_rotation(Date),
{ok, #state{name = Filename, fd = FD, inode = Inode, ctime = Ctime,
fmtmaxbytes = MaxBytes, size = Size, count = Count, date = Date,
rotator = Rotator}};
{error, Reason} ->
?INT_LOG(error, "Failed to open crash log file ~ts with error: ~s",
[Filename, file:format_error(Reason)]),
{ok, #state{name = Filename, fmtmaxbytes = MaxBytes, flap = true,
size = Size, count = Count, date = Date, rotator = Rotator}}
end.
%% @private
handle_call({log, _} = Log, _From, State) ->
{Reply, NewState} = do_log(Log, State),
{reply, Reply, NewState};
{Reply, NewState} = do_log(Log, State),
{reply, Reply, NewState};
handle_call(_Call, _From, State) ->
{reply, ok, State}.
{reply, ok, State}.
%% @private
handle_cast({log, _} = Log, State) ->
{_, NewState} = do_log(Log, State),
{noreply, NewState};
{_, NewState} = do_log(Log, State),
{noreply, NewState};
handle_cast(_Request, State) ->
{noreply, State}.
{noreply, State}.
%% @private
handle_info(rotate, #state{name=Name, count=Count, date=Date, rotator=Rotator} = State) ->
_ = Rotator:rotate_logfile(Name, Count),
schedule_rotation(Date),
{noreply, State};
handle_info(rotate, #state{name = Name, count = Count, date = Date, rotator = Rotator} = State) ->
_ = Rotator:rotate_logfile(Name, Count),
schedule_rotation(Date),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
{noreply, State}.
%% @private
terminate(_Reason, _State) ->
ok.
ok.
%% @private
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
{ok, State}.
schedule_rotation(undefined) ->
ok;
ok;
schedule_rotation(Date) ->
erlang:send_after(rumUtil:calculate_next_rotation(Date) * 1000, self(), rotate),
ok.
erlang:send_after(rumUtil:calculate_next_rotation(Date) * 1000, self(), rotate),
ok.
%% ===== Begin code lifted from riak_err =====
@ -134,249 +134,249 @@ schedule_rotation(Date) ->
%% to limit the formatted string's size.
limited_fmt(Fmt, Args, FmtMaxBytes) ->
eRum:safe_format(Fmt, Args, FmtMaxBytes).
eRum:safe_format(Fmt, Args, FmtMaxBytes).
limited_str(Term, FmtMaxBytes) ->
{Str, _} = lager_trunc_io:print(Term, FmtMaxBytes),
Str.
{Str, _} = lager_trunc_io:print(Term, FmtMaxBytes),
Str.
other_node_suffix(Pid) when node(Pid) =/= node() ->
"** at node " ++ atom_to_list(node(Pid)) ++ " **\n";
"** at node " ++ atom_to_list(node(Pid)) ++ " **\n";
other_node_suffix(_) ->
"".
"".
perhaps_a_sasl_report(error_report, {Pid, Type, Report}, FmtMaxBytes) ->
case lager_stdlib:is_my_error_report(Type) of
true ->
{sasl_type_to_report_head(Type), Pid,
sasl_limited_str(Type, Report, FmtMaxBytes), true};
false ->
{ignore, ignore, ignore, false}
end;
case lager_stdlib:is_my_error_report(Type) of
true ->
{sasl_type_to_report_head(Type), Pid,
sasl_limited_str(Type, Report, FmtMaxBytes), true};
false ->
{ignore, ignore, ignore, false}
end;
%perhaps_a_sasl_report(info_report, {Pid, Type, Report}, FmtMaxBytes) ->
%case lager_stdlib:is_my_info_report(Type) of
%true ->
%{sasl_type_to_report_head(Type), Pid,
%sasl_limited_str(Type, Report, FmtMaxBytes), false};
%false ->
%{ignore, ignore, ignore, false}
%end;
%case lager_stdlib:is_my_info_report(Type) of
%true ->
%{sasl_type_to_report_head(Type), Pid,
%sasl_limited_str(Type, Report, FmtMaxBytes), false};
%false ->
%{ignore, ignore, ignore, false}
%end;
perhaps_a_sasl_report(_, _, _) ->
{ignore, ignore, ignore, false}.
{ignore, ignore, ignore, false}.
sasl_type_to_report_head(supervisor_report) ->
"SUPERVISOR REPORT";
"SUPERVISOR REPORT";
sasl_type_to_report_head(crash_report) ->
"CRASH REPORT";
"CRASH REPORT";
sasl_type_to_report_head(progress) ->
"PROGRESS REPORT".
"PROGRESS REPORT".
sasl_limited_str(supervisor_report, Report, FmtMaxBytes) ->
Name = lager_stdlib:sup_get(supervisor, Report),
Context = lager_stdlib:sup_get(errorContext, Report),
Reason = lager_stdlib:sup_get(reason, Report),
Offender = lager_stdlib:sup_get(offender, Report),
FmtString = " Supervisor: ~p~n Context: ~p~n Reason: "
"~s~n Offender: ~s~n~n",
{ReasonStr, _} = lager_trunc_io:print(Reason, FmtMaxBytes),
{OffenderStr, _} = lager_trunc_io:print(Offender, FmtMaxBytes),
io_lib:format(FmtString, [Name, Context, ReasonStr, OffenderStr]);
Name = lager_stdlib:sup_get(supervisor, Report),
Context = lager_stdlib:sup_get(errorContext, Report),
Reason = lager_stdlib:sup_get(reason, Report),
Offender = lager_stdlib:sup_get(offender, Report),
FmtString = " Supervisor: ~p~n Context: ~p~n Reason: "
"~s~n Offender: ~s~n~n",
{ReasonStr, _} = lager_trunc_io:print(Reason, FmtMaxBytes),
{OffenderStr, _} = lager_trunc_io:print(Offender, FmtMaxBytes),
io_lib:format(FmtString, [Name, Context, ReasonStr, OffenderStr]);
sasl_limited_str(progress, Report, FmtMaxBytes) ->
[begin
{Str, _} = lager_trunc_io:print(Data, FmtMaxBytes),
io_lib:format(" ~16w: ~s~n", [Tag, Str])
end || {Tag, Data} <- Report];
[begin
{Str, _} = lager_trunc_io:print(Data, FmtMaxBytes),
io_lib:format(" ~16w: ~s~n", [Tag, Str])
end || {Tag, Data} <- Report];
sasl_limited_str(crash_report, Report, FmtMaxBytes) ->
lager_stdlib:proc_lib_format(Report, FmtMaxBytes).
lager_stdlib:proc_lib_format(Report, FmtMaxBytes).
do_log({log, Event}, #state{name=Name, fd=FD, inode=Inode, ctime=Ctime, flap=Flap,
fmtmaxbytes=FmtMaxBytes, size=RotSize, count=Count, rotator=Rotator} = State) ->
%% borrowed from riak_err
{ReportStr, Pid, MsgStr, _ErrorP} = case Event of
{error, _GL, {Pid1, Fmt, Args}} ->
{"ERROR REPORT", Pid1, limited_fmt(Fmt, Args, FmtMaxBytes), true};
{error_report, _GL, {Pid1, std_error, Rep}} ->
{"ERROR REPORT", Pid1, limited_str(Rep, FmtMaxBytes) ++ "\n", true};
{error_report, _GL, Other} ->
perhaps_a_sasl_report(error_report, Other, FmtMaxBytes);
_ ->
{ignore, ignore, ignore, false}
end,
if ReportStr == ignore ->
{ok, State};
true ->
case Rotator:ensure_logfile(Name, FD, Inode, Ctime, false) of
{ok, {_FD, _Inode, _Ctime, Size}} when RotSize /= 0, Size > RotSize ->
_ = Rotator:rotate_logfile(Name, Count),
handle_cast({log, Event}, State);
{ok, {NewFD, NewInode, NewCtime, _Size}} ->
{Date, TS} = rumUtil:format_time(
lager_stdlib:maybe_utc(erlang:localtime())),
Time = [Date, " ", TS," =", ReportStr, "====\n"],
NodeSuffix = other_node_suffix(Pid),
Msg = io_lib:format("~s~s~s", [Time, MsgStr, NodeSuffix]),
case file:write(NewFD, unicode:characters_to_binary(Msg)) of
{error, Reason} when Flap == false ->
?INT_LOG(error, "Failed to write log message to file ~ts: ~s",
[Name, file:format_error(Reason)]),
{ok, State#state{fd=NewFD, inode=NewInode, ctime=NewCtime, flap=true}};
ok ->
{ok, State#state{fd=NewFD, inode=NewInode, ctime=NewCtime, flap=false}};
_ ->
{ok, State#state{fd=NewFD, inode=NewInode, ctime=NewCtime}}
end;
{error, Reason} ->
case Flap of
true ->
{ok, State};
_ ->
?INT_LOG(error, "Failed to reopen crash log ~ts with error: ~s",
[Name, file:format_error(Reason)]),
{ok, State#state{flap=true}}
end
end
end.
do_log({log, Event}, #state{name = Name, fd = FD, inode = Inode, ctime = Ctime, flap = Flap,
fmtmaxbytes = FmtMaxBytes, size = RotSize, count = Count, rotator = Rotator} = State) ->
%% borrowed from riak_err
{ReportStr, Pid, MsgStr, _ErrorP} = case Event of
{error, _GL, {Pid1, Fmt, Args}} ->
{"ERROR REPORT", Pid1, limited_fmt(Fmt, Args, FmtMaxBytes), true};
{error_report, _GL, {Pid1, std_error, Rep}} ->
{"ERROR REPORT", Pid1, limited_str(Rep, FmtMaxBytes) ++ "\n", true};
{error_report, _GL, Other} ->
perhaps_a_sasl_report(error_report, Other, FmtMaxBytes);
_ ->
{ignore, ignore, ignore, false}
end,
if ReportStr == ignore ->
{ok, State};
true ->
case Rotator:ensure_logfile(Name, FD, Inode, Ctime, false) of
{ok, {_FD, _Inode, _Ctime, Size}} when RotSize /= 0, Size > RotSize ->
_ = Rotator:rotate_logfile(Name, Count),
handle_cast({log, Event}, State);
{ok, {NewFD, NewInode, NewCtime, _Size}} ->
{Date, TS} = rumUtil:format_time(
lager_stdlib:maybe_utc(erlang:localtime())),
Time = [Date, " ", TS, " =", ReportStr, "====\n"],
NodeSuffix = other_node_suffix(Pid),
Msg = io_lib:format("~s~s~s", [Time, MsgStr, NodeSuffix]),
case file:write(NewFD, unicode:characters_to_binary(Msg)) of
{error, Reason} when Flap == false ->
?INT_LOG(error, "Failed to write log message to file ~ts: ~s",
[Name, file:format_error(Reason)]),
{ok, State#state{fd = NewFD, inode = NewInode, ctime = NewCtime, flap = true}};
ok ->
{ok, State#state{fd = NewFD, inode = NewInode, ctime = NewCtime, flap = false}};
_ ->
{ok, State#state{fd = NewFD, inode = NewInode, ctime = NewCtime}}
end;
{error, Reason} ->
case Flap of
true ->
{ok, State};
_ ->
?INT_LOG(error, "Failed to reopen crash log ~ts with error: ~s",
[Name, file:format_error(Reason)]),
{ok, State#state{flap = true}}
end
end
end.
-ifdef(TEST).
filesystem_test_() ->
{foreach,
fun() ->
{ok, TestDir} = rumUtil:create_test_dir(),
CrashLog = filename:join(TestDir, "crash_test.log"),
ok = rumUtil:safe_write_file(CrashLog, []),
ok = error_logger:tty(false),
ok = rumUtil:safe_application_load(lager),
ok = application:set_env(lager, handlers, [{lager_test_backend, info}]),
ok = application:set_env(lager, error_logger_redirect, true),
ok = application:unset_env(lager, crash_log),
ok = eRum:start(),
ok = timer:sleep(1000),
ok = lager_test_backend:flush(),
CrashLog
end,
fun(_CrashLog) ->
case whereis(lager_crash_log) of
P when is_pid(P) ->
gen_server:stop(P);
_ ->
ok
end,
ok = application:stop(lager),
ok = application:stop(goldrush),
ok = rumUtil:delete_test_dir(),
ok = error_logger:tty(true)
end, [
fun(CrashLog) ->
{"under normal circumstances, file should be opened",
{foreach,
fun() ->
{ok, TestDir} = rumUtil:create_test_dir(),
CrashLog = filename:join(TestDir, "crash_test.log"),
ok = rumUtil:safe_write_file(CrashLog, []),
ok = error_logger:tty(false),
ok = rumUtil:safe_application_load(lager),
ok = application:set_env(lager, handlers, [{lager_test_backend, info}]),
ok = application:set_env(lager, error_logger_redirect, true),
ok = application:unset_env(lager, crash_log),
ok = eRum:start(),
ok = timer:sleep(1000),
ok = lager_test_backend:flush(),
CrashLog
end,
fun(_CrashLog) ->
case whereis(lager_crash_log) of
P when is_pid(P) ->
gen_server:stop(P);
_ ->
ok
end,
ok = application:stop(lager),
ok = application:stop(goldrush),
ok = rumUtil:delete_test_dir(),
ok = error_logger:tty(true)
end, [
fun(CrashLog) ->
{"under normal circumstances, file should be opened",
fun() ->
{ok, _} = ?MODULE:start_link(CrashLog, 65535, 0, undefined, 0, lager_rotator_default),
_ = gen_event:which_handlers(error_logger),
sync_error_logger:error_msg("Test message\n"),
{ok, Bin} = file:read_file(CrashLog),
?assertMatch([_, "Test message\n"], re:split(Bin, "\n", [{return, list}, {parts, 2}]))
{ok, _} = ?MODULE:start_link(CrashLog, 65535, 0, undefined, 0, lager_rotator_default),
_ = gen_event:which_handlers(error_logger),
sync_error_logger:error_msg("Test message\n"),
{ok, Bin} = file:read_file(CrashLog),
?assertMatch([_, "Test message\n"], re:split(Bin, "\n", [{return, list}, {parts, 2}]))
end}
end,
fun(CrashLog) ->
{"file can't be opened on startup triggers an error message",
end,
fun(CrashLog) ->
{"file can't be opened on startup triggers an error message",
fun() ->
{ok, FInfo0} = file:read_file_info(CrashLog, [raw]),
FInfo1 = FInfo0#file_info{mode = 0},
?assertEqual(ok, file:write_file_info(CrashLog, FInfo1)),
{ok, _} = ?MODULE:start_link(CrashLog, 65535, 0, undefined, 0, lager_rotator_default),
% Note: required on win32, do this early to prevent subsequent failures
% from preventing cleanup
?assertEqual(ok, file:write_file_info(CrashLog, FInfo0)),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, Message,_Metadata} = lager_test_backend:pop(),
?assertEqual(
"Failed to open crash log file " ++ CrashLog ++ " with error: permission denied",
lists:flatten(Message))
{ok, FInfo0} = file:read_file_info(CrashLog, [raw]),
FInfo1 = FInfo0#file_info{mode = 0},
?assertEqual(ok, file:write_file_info(CrashLog, FInfo1)),
{ok, _} = ?MODULE:start_link(CrashLog, 65535, 0, undefined, 0, lager_rotator_default),
% Note: required on win32, do this early to prevent subsequent failures
% from preventing cleanup
?assertEqual(ok, file:write_file_info(CrashLog, FInfo0)),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, Message, _Metadata} = lager_test_backend:pop(),
?assertEqual(
"Failed to open crash log file " ++ CrashLog ++ " with error: permission denied",
lists:flatten(Message))
end}
end,
fun(CrashLog) ->
{"file that becomes unavailable at runtime should trigger an error message",
end,
fun(CrashLog) ->
{"file that becomes unavailable at runtime should trigger an error message",
fun() ->
case os:type() of
{win32, _} ->
% Note: test is skipped on win32 due to the fact that a file can't be deleted or renamed
% while a process has an open file handle referencing it
ok;
_ ->
{ok, _} = ?MODULE:start_link(CrashLog, 65535, 0, undefined, 0, lager_rotator_default),
?assertEqual(0, lager_test_backend:count()),
sync_error_logger:error_msg("Test message\n"),
_ = gen_event:which_handlers(error_logger),
?assertEqual(1, lager_test_backend:count()),
?assertEqual(ok, file:delete(CrashLog)),
?assertEqual(ok, rumUtil:safe_write_file(CrashLog, "")),
{ok, FInfo0} = file:read_file_info(CrashLog, [raw]),
FInfo1 = FInfo0#file_info{mode = 0},
?assertEqual(ok, file:write_file_info(CrashLog, FInfo1)),
sync_error_logger:error_msg("Test message\n"),
_ = gen_event:which_handlers(error_logger),
% Note: required on win32, do this early to prevent subsequent failures
% from preventing cleanup
?assertEqual(ok, file:write_file_info(CrashLog, FInfo0)),
?assertEqual(3, lager_test_backend:count()),
lager_test_backend:pop(),
{_Level, _Time, Message,_Metadata} = lager_test_backend:pop(),
?assertEqual(
"Failed to reopen crash log " ++ CrashLog ++ " with error: permission denied",
lists:flatten(Message))
end
case os:type() of
{win32, _} ->
% Note: test is skipped on win32 due to the fact that a file can't be deleted or renamed
% while a process has an open file handle referencing it
ok;
_ ->
{ok, _} = ?MODULE:start_link(CrashLog, 65535, 0, undefined, 0, lager_rotator_default),
?assertEqual(0, lager_test_backend:count()),
sync_error_logger:error_msg("Test message\n"),
_ = gen_event:which_handlers(error_logger),
?assertEqual(1, lager_test_backend:count()),
?assertEqual(ok, file:delete(CrashLog)),
?assertEqual(ok, rumUtil:safe_write_file(CrashLog, "")),
{ok, FInfo0} = file:read_file_info(CrashLog, [raw]),
FInfo1 = FInfo0#file_info{mode = 0},
?assertEqual(ok, file:write_file_info(CrashLog, FInfo1)),
sync_error_logger:error_msg("Test message\n"),
_ = gen_event:which_handlers(error_logger),
% Note: required on win32, do this early to prevent subsequent failures
% from preventing cleanup
?assertEqual(ok, file:write_file_info(CrashLog, FInfo0)),
?assertEqual(3, lager_test_backend:count()),
lager_test_backend:pop(),
{_Level, _Time, Message, _Metadata} = lager_test_backend:pop(),
?assertEqual(
"Failed to reopen crash log " ++ CrashLog ++ " with error: permission denied",
lists:flatten(Message))
end
end}
end,
fun(CrashLog) ->
{"unavailable files that are fixed at runtime should start having log messages written",
end,
fun(CrashLog) ->
{"unavailable files that are fixed at runtime should start having log messages written",
fun() ->
{ok, FInfo} = file:read_file_info(CrashLog, [raw]),
OldPerms = FInfo#file_info.mode,
file:write_file_info(CrashLog, FInfo#file_info{mode = 0}),
{ok, _} = ?MODULE:start_link(CrashLog, 65535, 0, undefined, 0, lager_rotator_default),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, Message,_Metadata} = lager_test_backend:pop(),
?assertEqual(
"Failed to open crash log file " ++ CrashLog ++ " with error: permission denied",
lists:flatten(Message)),
file:write_file_info(CrashLog, FInfo#file_info{mode = OldPerms}),
sync_error_logger:error_msg("Test message~n"),
_ = gen_event:which_handlers(error_logger),
{ok, Bin} = file:read_file(CrashLog),
?assertMatch([_, "Test message\n"], re:split(Bin, "\n", [{return, list}, {parts, 2}]))
{ok, FInfo} = file:read_file_info(CrashLog, [raw]),
OldPerms = FInfo#file_info.mode,
file:write_file_info(CrashLog, FInfo#file_info{mode = 0}),
{ok, _} = ?MODULE:start_link(CrashLog, 65535, 0, undefined, 0, lager_rotator_default),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, Message, _Metadata} = lager_test_backend:pop(),
?assertEqual(
"Failed to open crash log file " ++ CrashLog ++ " with error: permission denied",
lists:flatten(Message)),
file:write_file_info(CrashLog, FInfo#file_info{mode = OldPerms}),
sync_error_logger:error_msg("Test message~n"),
_ = gen_event:which_handlers(error_logger),
{ok, Bin} = file:read_file(CrashLog),
?assertMatch([_, "Test message\n"], re:split(Bin, "\n", [{return, list}, {parts, 2}]))
end}
end,
fun(CrashLog) ->
{"external logfile rotation/deletion should be handled",
end,
fun(CrashLog) ->
{"external logfile rotation/deletion should be handled",
fun() ->
case os:type() of
{win32, _} ->
% Note: test is skipped on win32 due to the fact that a file can't be deleted or renamed
% while a process has an open file handle referencing it
ok;
_ ->
{ok, _} = ?MODULE:start_link(CrashLog, 65535, 0, undefined, 0, lager_rotator_default),
?assertEqual(0, lager_test_backend:count()),
sync_error_logger:error_msg("Test message~n"),
_ = gen_event:which_handlers(error_logger),
{ok, Bin} = file:read_file(CrashLog),
?assertMatch([_, "Test message\n"], re:split(Bin, "\n", [{return, list}, {parts, 2}])),
?assertEqual(ok, file:delete(CrashLog)),
?assertEqual(ok, rumUtil:safe_write_file(CrashLog, "")),
sync_error_logger:error_msg("Test message1~n"),
_ = gen_event:which_handlers(error_logger),
{ok, Bin1} = file:read_file(CrashLog),
?assertMatch([_, "Test message1\n"], re:split(Bin1, "\n", [{return, list}, {parts, 2}])),
file:rename(CrashLog, CrashLog ++ ".0"),
sync_error_logger:error_msg("Test message2~n"),
_ = gen_event:which_handlers(error_logger),
{ok, Bin2} = file:read_file(CrashLog),
?assertMatch([_, "Test message2\n"], re:split(Bin2, "\n", [{return, list}, {parts, 2}]))
end
case os:type() of
{win32, _} ->
% Note: test is skipped on win32 due to the fact that a file can't be deleted or renamed
% while a process has an open file handle referencing it
ok;
_ ->
{ok, _} = ?MODULE:start_link(CrashLog, 65535, 0, undefined, 0, lager_rotator_default),
?assertEqual(0, lager_test_backend:count()),
sync_error_logger:error_msg("Test message~n"),
_ = gen_event:which_handlers(error_logger),
{ok, Bin} = file:read_file(CrashLog),
?assertMatch([_, "Test message\n"], re:split(Bin, "\n", [{return, list}, {parts, 2}])),
?assertEqual(ok, file:delete(CrashLog)),
?assertEqual(ok, rumUtil:safe_write_file(CrashLog, "")),
sync_error_logger:error_msg("Test message1~n"),
_ = gen_event:which_handlers(error_logger),
{ok, Bin1} = file:read_file(CrashLog),
?assertMatch([_, "Test message1\n"], re:split(Bin1, "\n", [{return, list}, {parts, 2}])),
file:rename(CrashLog, CrashLog ++ ".0"),
sync_error_logger:error_msg("Test message2~n"),
_ = gen_event:which_handlers(error_logger),
{ok, Bin2} = file:read_file(CrashLog),
?assertMatch([_, "Test message2\n"], re:split(Bin2, "\n", [{return, list}, {parts, 2}]))
end
end}
end
]}.
end
]}.
-endif.

+ 853
- 853
src/misc/lager_file_backend.erl
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 157
- 157
src/misc/lager_handler_watcher.erl Voir le fichier

@ -32,212 +32,212 @@
%% callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
code_change/3]).
-export([start_link/3, start/3]).
-record(state, {
module :: atom(),
config :: any(),
sink :: pid() | atom()
}).
module :: atom(),
config :: any(),
sink :: pid() | atom()
}).
start_link(Sink, Module, Config) ->
gen_server:start_link(?MODULE, [Sink, Module, Config], []).
gen_server:start_link(?MODULE, [Sink, Module, Config], []).
start(Sink, Module, Config) ->
gen_server:start(?MODULE, [Sink, Module, Config], []).
gen_server:start(?MODULE, [Sink, Module, Config], []).
init([Sink, Module, Config]) ->
process_flag(trap_exit, true),
install_handler(Sink, Module, Config),
{ok, #state{sink=Sink, module=Module, config=Config}}.
process_flag(trap_exit, true),
install_handler(Sink, Module, Config),
{ok, #state{sink = Sink, module = Module, config = Config}}.
handle_call(_Call, _From, State) ->
{reply, ok, State}.
{reply, ok, State}.
handle_cast(_Request, State) ->
{noreply, State}.
{noreply, State}.
handle_info({gen_event_EXIT, Module, normal}, #state{module=Module} = State) ->
{stop, normal, State};
handle_info({gen_event_EXIT, Module, shutdown}, #state{module=Module} = State) ->
{stop, normal, State};
handle_info({gen_event_EXIT, Module, normal}, #state{module = Module} = State) ->
{stop, normal, State};
handle_info({gen_event_EXIT, Module, shutdown}, #state{module = Module} = State) ->
{stop, normal, State};
handle_info({gen_event_EXIT, Module, {'EXIT', {kill_me, [_KillerHWM, KillerReinstallAfter]}}},
#state{module=Module, sink=Sink, config = Config} = State) ->
%% Brutally kill the manager but stay alive to restore settings.
%%
%% SinkPid here means the gen_event process. Handlers *all* live inside the
%% same gen_event process space, so when the Pid is killed, *all* of the
%% pending log messages in its mailbox will die too.
SinkPid = whereis(Sink),
unlink(SinkPid),
{message_queue_len, Len} = process_info(SinkPid, message_queue_len),
error_logger:error_msg("Killing sink ~p, current message_queue_len:~p~n", [Sink, Len]),
exit(SinkPid, kill),
_ = timer:apply_after(KillerReinstallAfter, lager_app, start_handler, [Sink, Module, Config]),
{stop, normal, State};
handle_info({gen_event_EXIT, Module, Reason}, #state{module=Module,
config=Config, sink=Sink} = State) ->
case eRum:log(error, self(), "Lager event handler ~p exited with reason ~s",
[Module, error_logger_lager_h:format_reason(Reason)]) of
#state{module = Module, sink = Sink, config = Config} = State) ->
%% Brutally kill the manager but stay alive to restore settings.
%%
%% SinkPid here means the gen_event process. Handlers *all* live inside the
%% same gen_event process space, so when the Pid is killed, *all* of the
%% pending log messages in its mailbox will die too.
SinkPid = whereis(Sink),
unlink(SinkPid),
{message_queue_len, Len} = process_info(SinkPid, message_queue_len),
error_logger:error_msg("Killing sink ~p, current message_queue_len:~p~n", [Sink, Len]),
exit(SinkPid, kill),
_ = timer:apply_after(KillerReinstallAfter, lager_app, start_handler, [Sink, Module, Config]),
{stop, normal, State};
handle_info({gen_event_EXIT, Module, Reason}, #state{module = Module,
config = Config, sink = Sink} = State) ->
case eRum:log(error, self(), "Lager event handler ~p exited with reason ~s",
[Module, error_logger_lager_h:format_reason(Reason)]) of
ok ->
install_handler(Sink, Module, Config);
install_handler(Sink, Module, Config);
{error, _} ->
%% lager is not working, so installing a handler won't work
ok
end,
{noreply, State};
handle_info(reinstall_handler, #state{module=Module, config=Config, sink=Sink} = State) ->
install_handler(Sink, Module, Config),
{noreply, State};
%% lager is not working, so installing a handler won't work
ok
end,
{noreply, State};
handle_info(reinstall_handler, #state{module = Module, config = Config, sink = Sink} = State) ->
install_handler(Sink, Module, Config),
{noreply, State};
handle_info({reboot, Sink}, State) ->
_ = lager_app:boot(Sink),
{noreply, State};
_ = lager_app:boot(Sink),
{noreply, State};
handle_info(stop, State) ->
{stop, normal, State};
handle_info({'EXIT', _Pid, killed}, #state{module=Module, config=Config, sink=Sink} = State) ->
Tmr = application:get_env(lager, killer_reinstall_after, 5000),
_ = timer:apply_after(Tmr, lager_app, start_handler, [Sink, Module, Config]),
{stop, normal, State};
{stop, normal, State};
handle_info({'EXIT', _Pid, killed}, #state{module = Module, config = Config, sink = Sink} = State) ->
Tmr = application:get_env(lager, killer_reinstall_after, 5000),
_ = timer:apply_after(Tmr, lager_app, start_handler, [Sink, Module, Config]),
{stop, normal, State};
handle_info(_Info, State) ->
{noreply, State}.
{noreply, State}.
terminate(_Reason, _State) ->
ok.
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
{ok, State}.
%% internal
install_handler(Sink, lager_backend_throttle, Config) ->
%% The lager_backend_throttle needs to know to which sink it is
%% attached, hence this admittedly ugly workaround. Handlers are
%% sensitive to the structure of the configuration sent to `init',
%% sadly, so it's not trivial to add a configuration item to be
%% ignored to backends without breaking 3rd party handlers.
install_handler2(Sink, lager_backend_throttle, [{sink, Sink}|Config]);
%% The lager_backend_throttle needs to know to which sink it is
%% attached, hence this admittedly ugly workaround. Handlers are
%% sensitive to the structure of the configuration sent to `init',
%% sadly, so it's not trivial to add a configuration item to be
%% ignored to backends without breaking 3rd party handlers.
install_handler2(Sink, lager_backend_throttle, [{sink, Sink} | Config]);
install_handler(Sink, Module, Config) ->
install_handler2(Sink, Module, Config).
install_handler2(Sink, Module, Config).
%% private
install_handler2(Sink, Module, Config) ->
case gen_event:add_sup_handler(Sink, Module, Config) of
ok ->
?INT_LOG(debug, "Lager installed handler ~p into ~p", [Module, Sink]),
eRum:update_loglevel_config(Sink),
ok;
{error, {fatal, Reason}} ->
?INT_LOG(error, "Lager fatally failed to install handler ~p into"
" ~p, NOT retrying: ~p", [Module, Sink, Reason]),
%% tell ourselves to stop
self() ! stop,
ok;
Error ->
%% try to reinstall it later
?INT_LOG(error, "Lager failed to install handler ~p into"
" ~p, retrying later : ~p", [Module, Sink, Error]),
erlang:send_after(5000, self(), reinstall_handler),
ok
end.
case gen_event:add_sup_handler(Sink, Module, Config) of
ok ->
?INT_LOG(debug, "Lager installed handler ~p into ~p", [Module, Sink]),
eRum:update_loglevel_config(Sink),
ok;
{error, {fatal, Reason}} ->
?INT_LOG(error, "Lager fatally failed to install handler ~p into"
" ~p, NOT retrying: ~p", [Module, Sink, Reason]),
%% tell ourselves to stop
self() ! stop,
ok;
Error ->
%% try to reinstall it later
?INT_LOG(error, "Lager failed to install handler ~p into"
" ~p, retrying later : ~p", [Module, Sink, Error]),
erlang:send_after(5000, self(), reinstall_handler),
ok
end.
-ifdef(TEST).
from_now(Seconds) ->
{Mega, Secs, Micro} = os:timestamp(),
{Mega, Secs + Seconds, Micro}.
{Mega, Secs, Micro} = os:timestamp(),
{Mega, Secs + Seconds, Micro}.
reinstall_on_initial_failure_test_() ->
{timeout, 60000,
[
fun() ->
error_logger:tty(false),
application:load(lager),
application:set_env(lager, handlers, [{lager_test_backend, info}, {lager_crash_backend, [from_now(2), undefined]}]),
application:set_env(lager, error_logger_redirect, false),
application:unset_env(lager, crash_log),
eRum:start(),
try
{_Level, _Time, Message, _Metadata} = lager_test_backend:pop(),
?assertMatch("Lager failed to install handler lager_crash_backend into lager_event, retrying later :"++_, lists:flatten(Message)),
timer:sleep(6000),
lager_test_backend:flush(),
?assertEqual(0, lager_test_backend:count()),
?assert(lists:member(lager_crash_backend, gen_event:which_handlers(lager_event)))
after
application:stop(lager),
application:stop(goldrush),
error_logger:tty(true)
end
{timeout, 60000,
[
fun() ->
error_logger:tty(false),
application:load(lager),
application:set_env(lager, handlers, [{lager_test_backend, info}, {lager_crash_backend, [from_now(2), undefined]}]),
application:set_env(lager, error_logger_redirect, false),
application:unset_env(lager, crash_log),
eRum:start(),
try
{_Level, _Time, Message, _Metadata} = lager_test_backend:pop(),
?assertMatch("Lager failed to install handler lager_crash_backend into lager_event, retrying later :" ++ _, lists:flatten(Message)),
timer:sleep(6000),
lager_test_backend:flush(),
?assertEqual(0, lager_test_backend:count()),
?assert(lists:member(lager_crash_backend, gen_event:which_handlers(lager_event)))
after
application:stop(lager),
application:stop(goldrush),
error_logger:tty(true)
end
]
}.
end
]
}.
reinstall_on_runtime_failure_test_() ->
{timeout, 60000,
[
fun() ->
error_logger:tty(false),
application:load(lager),
application:set_env(lager, handlers, [{lager_test_backend, info}, {lager_crash_backend, [undefined, from_now(5)]}]),
application:set_env(lager, error_logger_redirect, false),
application:unset_env(lager, crash_log),
eRum:start(),
try
?assert(lists:member(lager_crash_backend, gen_event:which_handlers(lager_event))),
timer:sleep(6000),
pop_until("Lager event handler lager_crash_backend exited with reason crash", fun lists:flatten/1),
pop_until("Lager failed to install handler lager_crash_backend into lager_event, retrying later",
fun(Msg) -> string:substr(lists:flatten(Msg), 1, 84) end),
?assertEqual(false, lists:member(lager_crash_backend, gen_event:which_handlers(lager_event)))
after
application:stop(lager),
application:stop(goldrush),
error_logger:tty(true)
end
{timeout, 60000,
[
fun() ->
error_logger:tty(false),
application:load(lager),
application:set_env(lager, handlers, [{lager_test_backend, info}, {lager_crash_backend, [undefined, from_now(5)]}]),
application:set_env(lager, error_logger_redirect, false),
application:unset_env(lager, crash_log),
eRum:start(),
try
?assert(lists:member(lager_crash_backend, gen_event:which_handlers(lager_event))),
timer:sleep(6000),
pop_until("Lager event handler lager_crash_backend exited with reason crash", fun lists:flatten/1),
pop_until("Lager failed to install handler lager_crash_backend into lager_event, retrying later",
fun(Msg) -> string:substr(lists:flatten(Msg), 1, 84) end),
?assertEqual(false, lists:member(lager_crash_backend, gen_event:which_handlers(lager_event)))
after
application:stop(lager),
application:stop(goldrush),
error_logger:tty(true)
end
]
}.
end
]
}.
reinstall_handlers_after_killer_hwm_test_() ->
{timeout, 60000,
[
fun() ->
error_logger:tty(false),
application:load(lager),
application:set_env(lager, handlers, [{lager_manager_killer, [1000, 5000]}]),
application:set_env(lager, error_logger_redirect, false),
application:set_env(lager, killer_reinstall_after, 5000),
application:unset_env(lager, crash_log),
eRum:start(),
eRum:trace_file("foo", [{foo, "bar"}], error),
L = length(gen_event:which_handlers(lager_event)),
try
lager_manager_killer:kill_me(),
timer:sleep(6000),
?assertEqual(L, length(gen_event:which_handlers(lager_event))),
file:delete("foo")
after
application:stop(lager),
application:stop(goldrush),
error_logger:tty(true)
end
{timeout, 60000,
[
fun() ->
error_logger:tty(false),
application:load(lager),
application:set_env(lager, handlers, [{lager_manager_killer, [1000, 5000]}]),
application:set_env(lager, error_logger_redirect, false),
application:set_env(lager, killer_reinstall_after, 5000),
application:unset_env(lager, crash_log),
eRum:start(),
eRum:trace_file("foo", [{foo, "bar"}], error),
L = length(gen_event:which_handlers(lager_event)),
try
lager_manager_killer:kill_me(),
timer:sleep(6000),
?assertEqual(L, length(gen_event:which_handlers(lager_event))),
file:delete("foo")
after
application:stop(lager),
application:stop(goldrush),
error_logger:tty(true)
end
]
}.
end
]
}.
pop_until(String, Fun) ->
try_backend_pop(lager_test_backend:pop(), String, Fun).
try_backend_pop(lager_test_backend:pop(), String, Fun).
try_backend_pop(undefined, String, _Fun) ->
throw("Not found: " ++ String);
throw("Not found: " ++ String);
try_backend_pop({_Severity, _Date, Msg, _Metadata}, String, Fun) ->
case Fun(Msg) of
String ->
ok;
_ ->
try_backend_pop(lager_test_backend:pop(), String, Fun)
end.
case Fun(Msg) of
String ->
ok;
_ ->
try_backend_pop(lager_test_backend:pop(), String, Fun)
end.
-endif.

+ 6
- 6
src/misc/lager_handler_watcher_sup.erl Voir le fichier

@ -29,11 +29,11 @@
-export([init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
{ok, {{simple_one_for_one, 10, 60},
[
{lager_handler_watcher, {lager_handler_watcher, start_link, []},
temporary, 5000, worker, [lager_handler_watcher]}
]}}.
{ok, {{simple_one_for_one, 10, 60},
[
{lager_handler_watcher, {lager_handler_watcher, start_link, []},
temporary, 5000, worker, [lager_handler_watcher]}
]}}.

+ 24
- 24
src/misc/lager_manager_killer.erl Voir le fichier

@ -9,45 +9,45 @@
-include("rum.hrl").
-record(state, {
killer_hwm :: non_neg_integer(),
killer_reinstall_after :: non_neg_integer()
}).
killer_hwm :: non_neg_integer(),
killer_reinstall_after :: non_neg_integer()
}).
kill_me() ->
gen_event:call(lager_event, ?MODULE, kill_self).
gen_event:call(lager_event, ?MODULE, kill_self).
init([KillerHWM, KillerReinstallAfter]) ->
{ok, #state{killer_hwm=KillerHWM, killer_reinstall_after=KillerReinstallAfter}}.
{ok, #state{killer_hwm = KillerHWM, killer_reinstall_after = KillerReinstallAfter}}.
handle_call(get_loglevel, State) ->
{ok, {mask, ?LOG_NONE}, State};
{ok, {mask, ?LOG_NONE}, State};
handle_call({set_loglevel, _Level}, State) ->
{ok, ok, State};
handle_call(get_settings, State = #state{killer_hwm=KillerHWM, killer_reinstall_after=KillerReinstallAfter}) ->
{ok, [KillerHWM, KillerReinstallAfter], State};
handle_call(kill_self, #state{killer_hwm=KillerHWM, killer_reinstall_after=KillerReinstallAfter}) ->
exit({kill_me, [KillerHWM, KillerReinstallAfter]});
{ok, ok, State};
handle_call(get_settings, State = #state{killer_hwm = KillerHWM, killer_reinstall_after = KillerReinstallAfter}) ->
{ok, [KillerHWM, KillerReinstallAfter], State};
handle_call(kill_self, #state{killer_hwm = KillerHWM, killer_reinstall_after = KillerReinstallAfter}) ->
exit({kill_me, [KillerHWM, KillerReinstallAfter]});
handle_call(_Request, State) ->
{ok, ok, State}.
{ok, ok, State}.
%% It's not the best idea in the world to check the queue length for every
%% log message. We can make this operation work on a poll timer in the
%% future.
handle_event({log, _Message}, State = #state{killer_hwm=KillerHWM, killer_reinstall_after=KillerReinstallAfter}) ->
{message_queue_len, Len} = process_info(self(), message_queue_len),
case Len > KillerHWM of
true ->
exit({kill_me, [KillerHWM, KillerReinstallAfter]});
_ ->
{ok, State}
end;
handle_event({log, _Message}, State = #state{killer_hwm = KillerHWM, killer_reinstall_after = KillerReinstallAfter}) ->
{message_queue_len, Len} = process_info(self(), message_queue_len),
case Len > KillerHWM of
true ->
exit({kill_me, [KillerHWM, KillerReinstallAfter]});
_ ->
{ok, State}
end;
handle_event(_Event, State) ->
{ok, State}.
{ok, State}.
handle_info(_Info, State) ->
{ok, State}.
{ok, State}.
terminate(_Reason, _State) ->
ok.
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
{ok, State}.

+ 20
- 20
src/misc/lager_msg.erl Voir le fichier

@ -9,14 +9,14 @@
-export([metadata/1]).
-export([destinations/1]).
-record(lager_msg,{
destinations :: list(),
metadata :: [tuple()],
severity :: eRum:log_level(),
datetime :: {string(), string()},
timestamp :: erlang:timestamp(),
message :: list()
}).
-record(lager_msg, {
destinations :: list(),
metadata :: [tuple()],
severity :: eRum:log_level(),
datetime :: {string(), string()},
timestamp :: erlang:timestamp(),
message :: list()
}).
-opaque lager_msg() :: #lager_msg{}.
-export_type([lager_msg/0]).
@ -24,41 +24,41 @@
%% create with provided timestamp, handy for testing mostly
-spec new(list(), erlang:timestamp(), eRum:log_level(), [tuple()], list()) -> lager_msg().
new(Msg, Timestamp, Severity, Metadata, Destinations) ->
{Date, Time} = rumUtil:format_time(rumUtil:maybe_utc(rumUtil:localtime_ms(Timestamp))),
#lager_msg{message=Msg, datetime={Date, Time}, timestamp=Timestamp, severity=Severity,
metadata=Metadata, destinations=Destinations}.
{Date, Time} = rumUtil:format_time(rumUtil:maybe_utc(rumUtil:localtime_ms(Timestamp))),
#lager_msg{message = Msg, datetime = {Date, Time}, timestamp = Timestamp, severity = Severity,
metadata = Metadata, destinations = Destinations}.
-spec new(list(), eRum:log_level(), [tuple()], list()) -> lager_msg().
new(Msg, Severity, Metadata, Destinations) ->
Now = os:timestamp(),
new(Msg, Now, Severity, Metadata, Destinations).
Now = os:timestamp(),
new(Msg, Now, Severity, Metadata, Destinations).
-spec message(lager_msg()) -> list().
message(Msg) ->
Msg#lager_msg.message.
Msg#lager_msg.message.
-spec timestamp(lager_msg()) -> erlang:timestamp().
timestamp(Msg) ->
Msg#lager_msg.timestamp.
Msg#lager_msg.timestamp.
-spec datetime(lager_msg()) -> {string(), string()}.
datetime(Msg) ->
Msg#lager_msg.datetime.
Msg#lager_msg.datetime.
-spec severity(lager_msg()) -> eRum:log_level().
severity(Msg) ->
Msg#lager_msg.severity.
Msg#lager_msg.severity.
-spec severity_as_int(lager_msg()) -> eRum:log_level_number().
severity_as_int(Msg) ->
rumUtil:level_to_num(Msg#lager_msg.severity).
rumUtil:level_to_num(Msg#lager_msg.severity).
-spec metadata(lager_msg()) -> [tuple()].
metadata(Msg) ->
Msg#lager_msg.metadata.
Msg#lager_msg.metadata.
-spec destinations(lager_msg()) -> list().
destinations(Msg) ->
Msg#lager_msg.destinations.
Msg#lager_msg.destinations.

+ 337
- 337
src/misc/lager_stdlib.erl Voir le fichier

@ -35,73 +35,73 @@
%% from error_logger_file_h
string_p([]) ->
false;
false;
string_p(Term) ->
string_p1(Term).
string_p1([H|T]) when is_integer(H), H >= $\s, H < 256 ->
string_p1(T);
string_p1([$\n|T]) -> string_p1(T);
string_p1([$\r|T]) -> string_p1(T);
string_p1([$\t|T]) -> string_p1(T);
string_p1([$\v|T]) -> string_p1(T);
string_p1([$\b|T]) -> string_p1(T);
string_p1([$\f|T]) -> string_p1(T);
string_p1([$\e|T]) -> string_p1(T);
string_p1([H|T]) when is_list(H) ->
case string_p1(H) of
true -> string_p1(T);
_ -> false
end;
string_p1(Term).
string_p1([H | T]) when is_integer(H), H >= $\s, H < 256 ->
string_p1(T);
string_p1([$\n | T]) -> string_p1(T);
string_p1([$\r | T]) -> string_p1(T);
string_p1([$\t | T]) -> string_p1(T);
string_p1([$\v | T]) -> string_p1(T);
string_p1([$\b | T]) -> string_p1(T);
string_p1([$\f | T]) -> string_p1(T);
string_p1([$\e | T]) -> string_p1(T);
string_p1([H | T]) when is_list(H) ->
case string_p1(H) of
true -> string_p1(T);
_ -> false
end;
string_p1([]) -> true;
string_p1(_) -> false.
string_p1(_) -> false.
%% From calendar
-type year1970() :: 1970..10000. % should probably be 1970..
-type month() :: 1..12.
-type day() :: 1..31.
-type hour() :: 0..23.
-type minute() :: 0..59.
-type second() :: 0..59.
-type t_time() :: {hour(),minute(),second()}.
-type t_datetime1970() :: {{year1970(),month(),day()},t_time()}.
-type month() :: 1..12.
-type day() :: 1..31.
-type hour() :: 0..23.
-type minute() :: 0..59.
-type second() :: 0..59.
-type t_time() :: {hour(), minute(), second()}.
-type t_datetime1970() :: {{year1970(), month(), day()}, t_time()}.
%% From OTP stdlib's error_logger_tty_h.erl ... These functions aren't
%% exported.
-spec write_time({utc, t_datetime1970()} | t_datetime1970(), string()) -> string().
write_time({utc,{{Y,Mo,D},{H,Mi,S}}},Type) ->
io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
[Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
write_time({{Y,Mo,D},{H,Mi,S}},Type) ->
io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s ===~n",
[Type,D,month(Mo),Y,t(H),t(Mi),t(S)]).
write_time({utc, {{Y, Mo, D}, {H, Mi, S}}}, Type) ->
io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
[Type, D, month(Mo), Y, t(H), t(Mi), t(S)]);
write_time({{Y, Mo, D}, {H, Mi, S}}, Type) ->
io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s ===~n",
[Type, D, month(Mo), Y, t(H), t(Mi), t(S)]).
-spec maybe_utc(t_datetime1970()) -> {utc, t_datetime1970()} | t_datetime1970().
maybe_utc(Time) ->
UTC = case application:get_env(sasl, utc_log) of
{ok, Val} ->
Val;
undefined ->
%% Backwards compatible:
application:get_env(stdlib, utc_log, false)
end,
if
UTC =:= true ->
UTCTime = case calendar:local_time_to_universal_time_dst(Time) of
[] -> calendar:local_time();
[T0|_] -> T0
end,
{utc, UTCTime};
true ->
Time
end.
UTC = case application:get_env(sasl, utc_log) of
{ok, Val} ->
Val;
undefined ->
%% Backwards compatible:
application:get_env(stdlib, utc_log, false)
end,
if
UTC =:= true ->
UTCTime = case calendar:local_time_to_universal_time_dst(Time) of
[] -> calendar:local_time();
[T0 | _] -> T0
end,
{utc, UTCTime};
true ->
Time
end.
t(X) when is_integer(X) ->
t1(integer_to_list(X));
t1(integer_to_list(X));
t(_) ->
"".
t1([X]) -> [$0,X];
t1(X) -> X.
"".
t1([X]) -> [$0, X];
t1(X) -> X.
month(1) -> "Jan";
month(2) -> "Feb";
@ -119,384 +119,384 @@ month(12) -> "Dec".
%% From OTP sasl's sasl_report.erl ... These functions aren't
%% exported.
-spec is_my_error_report(atom()) -> boolean().
is_my_error_report(supervisor_report) -> true;
is_my_error_report(crash_report) -> true;
is_my_error_report(_) -> false.
is_my_error_report(supervisor_report) -> true;
is_my_error_report(crash_report) -> true;
is_my_error_report(_) -> false.
-spec is_my_info_report(atom()) -> boolean().
is_my_info_report(progress) -> true;
is_my_info_report(_) -> false.
is_my_info_report(progress) -> true;
is_my_info_report(_) -> false.
-spec sup_get(term(), [proplists:property()]) -> term().
sup_get(Tag, Report) ->
case lists:keysearch(Tag, 1, Report) of
{value, {_, Value}} ->
Value;
_ ->
""
end.
case lists:keysearch(Tag, 1, Report) of
{value, {_, Value}} ->
Value;
_ ->
""
end.
%% From OTP stdlib's proc_lib.erl ... These functions aren't exported.
-spec proc_lib_format([term()], pos_integer()) -> string().
proc_lib_format([OwnReport,LinkReport], FmtMaxBytes) ->
OwnFormat = format_report(OwnReport, FmtMaxBytes),
LinkFormat = format_report(LinkReport, FmtMaxBytes),
%% io_lib:format here is OK because we're limiting max length elsewhere.
Str = io_lib:format(" crasher:~n~s neighbours:~n~s",[OwnFormat,LinkFormat]),
lists:flatten(Str).
proc_lib_format([OwnReport, LinkReport], FmtMaxBytes) ->
OwnFormat = format_report(OwnReport, FmtMaxBytes),
LinkFormat = format_report(LinkReport, FmtMaxBytes),
%% io_lib:format here is OK because we're limiting max length elsewhere.
Str = io_lib:format(" crasher:~n~s neighbours:~n~s", [OwnFormat, LinkFormat]),
lists:flatten(Str).
format_report(Rep, FmtMaxBytes) when is_list(Rep) ->
format_rep(Rep, FmtMaxBytes);
format_rep(Rep, FmtMaxBytes);
format_report(Rep, FmtMaxBytes) ->
{Str, _} = lager_trunc_io:print(Rep, FmtMaxBytes),
io_lib:format("~p~n", [Str]).
format_rep([{initial_call,InitialCall}|Rep], FmtMaxBytes) ->
[format_mfa(InitialCall, FmtMaxBytes)|format_rep(Rep, FmtMaxBytes)];
format_rep([{error_info,{Class,Reason,StackTrace}}|Rep], FmtMaxBytes) ->
[format_exception(Class, Reason, StackTrace, FmtMaxBytes)|format_rep(Rep, FmtMaxBytes)];
format_rep([{Tag,Data}|Rep], FmtMaxBytes) ->
[format_tag(Tag, Data, FmtMaxBytes)|format_rep(Rep, FmtMaxBytes)];
{Str, _} = lager_trunc_io:print(Rep, FmtMaxBytes),
io_lib:format("~p~n", [Str]).
format_rep([{initial_call, InitialCall} | Rep], FmtMaxBytes) ->
[format_mfa(InitialCall, FmtMaxBytes) | format_rep(Rep, FmtMaxBytes)];
format_rep([{error_info, {Class, Reason, StackTrace}} | Rep], FmtMaxBytes) ->
[format_exception(Class, Reason, StackTrace, FmtMaxBytes) | format_rep(Rep, FmtMaxBytes)];
format_rep([{Tag, Data} | Rep], FmtMaxBytes) ->
[format_tag(Tag, Data, FmtMaxBytes) | format_rep(Rep, FmtMaxBytes)];
format_rep(_, _S) ->
[].
[].
format_exception(Class, Reason, StackTrace, FmtMaxBytes) ->
PF = pp_fun(FmtMaxBytes),
StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
%% EI = " exception: ",
EI = " ",
[EI, lib_format_exception(1+length(EI), Class, Reason,
StackTrace, StackFun, PF), "\n"].
format_mfa({M,F,Args}=StartF, FmtMaxBytes) ->
try
A = length(Args),
[" initial call: ",atom_to_list(M),$:,atom_to_list(F),$/,
integer_to_list(A),"\n"]
catch
error:_ ->
format_tag(initial_call, StartF, FmtMaxBytes)
end.
PF = pp_fun(FmtMaxBytes),
StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
%% EI = " exception: ",
EI = " ",
[EI, lib_format_exception(1 + length(EI), Class, Reason,
StackTrace, StackFun, PF), "\n"].
format_mfa({M, F, Args} = StartF, FmtMaxBytes) ->
try
A = length(Args),
[" initial call: ", atom_to_list(M), $:, atom_to_list(F), $/,
integer_to_list(A), "\n"]
catch
error:_ ->
format_tag(initial_call, StartF, FmtMaxBytes)
end.
pp_fun(FmtMaxBytes) ->
fun(Term, _I) ->
{Str, _} = lager_trunc_io:print(Term, FmtMaxBytes),
io_lib:format("~s", [Str])
end.
fun(Term, _I) ->
{Str, _} = lager_trunc_io:print(Term, FmtMaxBytes),
io_lib:format("~s", [Str])
end.
format_tag(Tag, Data, FmtMaxBytes) ->
{Str, _} = lager_trunc_io:print(Data, FmtMaxBytes),
io_lib:format(" ~p: ~s~n", [Tag, Str]).
{Str, _} = lager_trunc_io:print(Data, FmtMaxBytes),
io_lib:format(" ~p: ~s~n", [Tag, Str]).
%% From OTP stdlib's lib.erl ... These functions aren't exported.
lib_format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun)
when is_integer(I), I >= 1, is_function(StackFun, 3),
is_function(FormatFun, 2) ->
Str = n_spaces(I-1),
{Term,Trace1,Trace} = analyze_exception(Class, Reason, StackTrace),
Expl0 = explain_reason(Term, Class, Trace1, FormatFun, Str),
Expl = io_lib:fwrite(<<"~s~s">>, [exited(Class), Expl0]),
case format_stacktrace1(Str, Trace, FormatFun, StackFun) of
[] -> Expl;
Stack -> [Expl, $\n, Stack]
end.
lib_format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun)
when is_integer(I), I >= 1, is_function(StackFun, 3),
is_function(FormatFun, 2) ->
Str = n_spaces(I - 1),
{Term, Trace1, Trace} = analyze_exception(Class, Reason, StackTrace),
Expl0 = explain_reason(Term, Class, Trace1, FormatFun, Str),
Expl = io_lib:fwrite(<<"~s~s">>, [exited(Class), Expl0]),
case format_stacktrace1(Str, Trace, FormatFun, StackFun) of
[] -> Expl;
Stack -> [Expl, $\n, Stack]
end.
analyze_exception(error, Term, Stack) ->
case {is_stacktrace(Stack), Stack, Term} of
{true, [{_M,_F,As}=MFA|MFAs], function_clause} when is_list(As) ->
{Term,[MFA],MFAs};
{true, [{shell,F,A}], function_clause} when is_integer(A) ->
{Term, [{F,A}], []};
{true, [{_M,_F,_AorAs}=MFA|MFAs], undef} ->
{Term,[MFA],MFAs};
{true, _, _} ->
{Term,[],Stack};
{false, _, _} ->
{{Term,Stack},[],[]}
end;
case {is_stacktrace(Stack), Stack, Term} of
{true, [{_M, _F, As} = MFA | MFAs], function_clause} when is_list(As) ->
{Term, [MFA], MFAs};
{true, [{shell, F, A}], function_clause} when is_integer(A) ->
{Term, [{F, A}], []};
{true, [{_M, _F, _AorAs} = MFA | MFAs], undef} ->
{Term, [MFA], MFAs};
{true, _, _} ->
{Term, [], Stack};
{false, _, _} ->
{{Term, Stack}, [], []}
end;
analyze_exception(_Class, Term, Stack) ->
case is_stacktrace(Stack) of
true ->
{Term,[],Stack};
false ->
{{Term,Stack},[],[]}
end.
case is_stacktrace(Stack) of
true ->
{Term, [], Stack};
false ->
{{Term, Stack}, [], []}
end.
is_stacktrace([]) ->
true;
is_stacktrace([{M,F,A}|Fs]) when is_atom(M), is_atom(F), is_integer(A) ->
is_stacktrace(Fs);
is_stacktrace([{M,F,As}|Fs]) when is_atom(M), is_atom(F), length(As) >= 0 ->
is_stacktrace(Fs);
true;
is_stacktrace([{M, F, A} | Fs]) when is_atom(M), is_atom(F), is_integer(A) ->
is_stacktrace(Fs);
is_stacktrace([{M, F, As} | Fs]) when is_atom(M), is_atom(F), length(As) >= 0 ->
is_stacktrace(Fs);
is_stacktrace(_) ->
false.
false.
%% ERTS exit codes (some of them are also returned by erl_eval):
explain_reason(badarg, error, [], _PF, _Str) ->
<<"bad argument">>;
explain_reason({badarg,V}, error=Cl, [], PF, Str) -> % orelse, andalso
format_value(V, <<"bad argument: ">>, Cl, PF, Str);
<<"bad argument">>;
explain_reason({badarg, V}, error = Cl, [], PF, Str) -> % orelse, andalso
format_value(V, <<"bad argument: ">>, Cl, PF, Str);
explain_reason(badarith, error, [], _PF, _Str) ->
<<"bad argument in an arithmetic expression">>;
explain_reason({badarity,{Fun,As}}, error, [], _PF, _Str)
when is_function(Fun) ->
%% Only the arity is displayed, not the arguments As.
io_lib:fwrite(<<"~s called with ~s">>,
[format_fun(Fun), argss(length(As))]);
explain_reason({badfun,Term}, error=Cl, [], PF, Str) ->
format_value(Term, <<"bad function ">>, Cl, PF, Str);
explain_reason({badmatch,Term}, error=Cl, [], PF, Str) ->
format_value(Term, <<"no match of right hand side value ">>, Cl, PF, Str);
explain_reason({case_clause,V}, error=Cl, [], PF, Str) ->
%% "there is no case clause with a true guard sequence and a
%% pattern matching..."
format_value(V, <<"no case clause matching ">>, Cl, PF, Str);
explain_reason(function_clause, error, [{F,A}], _PF, _Str) ->
%% Shell commands
FAs = io_lib:fwrite(<<"~w/~w">>, [F, A]),
[<<"no function clause matching call to ">> | FAs];
explain_reason(function_clause, error=Cl, [{M,F,As}], PF, Str) ->
String = <<"no function clause matching ">>,
format_errstr_call(String, Cl, {M,F}, As, PF, Str);
<<"bad argument in an arithmetic expression">>;
explain_reason({badarity, {Fun, As}}, error, [], _PF, _Str)
when is_function(Fun) ->
%% Only the arity is displayed, not the arguments As.
io_lib:fwrite(<<"~s called with ~s">>,
[format_fun(Fun), argss(length(As))]);
explain_reason({badfun, Term}, error = Cl, [], PF, Str) ->
format_value(Term, <<"bad function ">>, Cl, PF, Str);
explain_reason({badmatch, Term}, error = Cl, [], PF, Str) ->
format_value(Term, <<"no match of right hand side value ">>, Cl, PF, Str);
explain_reason({case_clause, V}, error = Cl, [], PF, Str) ->
%% "there is no case clause with a true guard sequence and a
%% pattern matching..."
format_value(V, <<"no case clause matching ">>, Cl, PF, Str);
explain_reason(function_clause, error, [{F, A}], _PF, _Str) ->
%% Shell commands
FAs = io_lib:fwrite(<<"~w/~w">>, [F, A]),
[<<"no function clause matching call to ">> | FAs];
explain_reason(function_clause, error = Cl, [{M, F, As}], PF, Str) ->
String = <<"no function clause matching ">>,
format_errstr_call(String, Cl, {M, F}, As, PF, Str);
explain_reason(if_clause, error, [], _PF, _Str) ->
<<"no true branch found when evaluating an if expression">>;
<<"no true branch found when evaluating an if expression">>;
explain_reason(noproc, error, [], _PF, _Str) ->
<<"no such process or port">>;
<<"no such process or port">>;
explain_reason(notalive, error, [], _PF, _Str) ->
<<"the node cannot be part of a distributed system">>;
<<"the node cannot be part of a distributed system">>;
explain_reason(system_limit, error, [], _PF, _Str) ->
<<"a system limit has been reached">>;
<<"a system limit has been reached">>;
explain_reason(timeout_value, error, [], _PF, _Str) ->
<<"bad receive timeout value">>;
explain_reason({try_clause,V}, error=Cl, [], PF, Str) ->
%% "there is no try clause with a true guard sequence and a
%% pattern matching..."
format_value(V, <<"no try clause matching ">>, Cl, PF, Str);
explain_reason(undef, error, [{M,F,A}], _PF, _Str) ->
%% Only the arity is displayed, not the arguments, if there are any.
io_lib:fwrite(<<"undefined function ~s">>,
[mfa_to_string(M, F, n_args(A))]);
explain_reason({shell_undef,F,A}, error, [], _PF, _Str) ->
%% Give nicer reports for undefined shell functions
%% (but not when the user actively calls shell_default:F(...)).
io_lib:fwrite(<<"undefined shell command ~s/~w">>, [F, n_args(A)]);
<<"bad receive timeout value">>;
explain_reason({try_clause, V}, error = Cl, [], PF, Str) ->
%% "there is no try clause with a true guard sequence and a
%% pattern matching..."
format_value(V, <<"no try clause matching ">>, Cl, PF, Str);
explain_reason(undef, error, [{M, F, A}], _PF, _Str) ->
%% Only the arity is displayed, not the arguments, if there are any.
io_lib:fwrite(<<"undefined function ~s">>,
[mfa_to_string(M, F, n_args(A))]);
explain_reason({shell_undef, F, A}, error, [], _PF, _Str) ->
%% Give nicer reports for undefined shell functions
%% (but not when the user actively calls shell_default:F(...)).
io_lib:fwrite(<<"undefined shell command ~s/~w">>, [F, n_args(A)]);
%% Exit codes returned by erl_eval only:
explain_reason({argument_limit,_Fun}, error, [], _PF, _Str) ->
io_lib:fwrite(<<"limit of number of arguments to interpreted function"
" exceeded">>, []);
explain_reason({bad_filter,V}, error=Cl, [], PF, Str) ->
format_value(V, <<"bad filter ">>, Cl, PF, Str);
explain_reason({bad_generator,V}, error=Cl, [], PF, Str) ->
format_value(V, <<"bad generator ">>, Cl, PF, Str);
explain_reason({unbound,V}, error, [], _PF, _Str) ->
io_lib:fwrite(<<"variable ~w is unbound">>, [V]);
explain_reason({argument_limit, _Fun}, error, [], _PF, _Str) ->
io_lib:fwrite(<<"limit of number of arguments to interpreted function"
" exceeded">>, []);
explain_reason({bad_filter, V}, error = Cl, [], PF, Str) ->
format_value(V, <<"bad filter ">>, Cl, PF, Str);
explain_reason({bad_generator, V}, error = Cl, [], PF, Str) ->
format_value(V, <<"bad generator ">>, Cl, PF, Str);
explain_reason({unbound, V}, error, [], _PF, _Str) ->
io_lib:fwrite(<<"variable ~w is unbound">>, [V]);
%% Exit codes local to the shell module (restricted shell):
explain_reason({restricted_shell_bad_return, V}, exit=Cl, [], PF, Str) ->
String = <<"restricted shell module returned bad value ">>,
format_value(V, String, Cl, PF, Str);
explain_reason({restricted_shell_disallowed,{ForMF,As}},
exit=Cl, [], PF, Str) ->
%% ForMF can be a fun, but not a shell fun.
String = <<"restricted shell does not allow ">>,
format_errstr_call(String, Cl, ForMF, As, PF, Str);
explain_reason({restricted_shell_bad_return, V}, exit = Cl, [], PF, Str) ->
String = <<"restricted shell module returned bad value ">>,
format_value(V, String, Cl, PF, Str);
explain_reason({restricted_shell_disallowed, {ForMF, As}},
exit = Cl, [], PF, Str) ->
%% ForMF can be a fun, but not a shell fun.
String = <<"restricted shell does not allow ">>,
format_errstr_call(String, Cl, ForMF, As, PF, Str);
explain_reason(restricted_shell_started, exit, [], _PF, _Str) ->
<<"restricted shell starts now">>;
<<"restricted shell starts now">>;
explain_reason(restricted_shell_stopped, exit, [], _PF, _Str) ->
<<"restricted shell stopped">>;
<<"restricted shell stopped">>;
%% Other exit code:
explain_reason(Reason, Class, [], PF, Str) ->
PF(Reason, (iolist_size(Str)+1) + exited_size(Class)).
PF(Reason, (iolist_size(Str) + 1) + exited_size(Class)).
n_spaces(N) ->
lists:duplicate(N, $\s).
lists:duplicate(N, $\s).
exited_size(Class) ->
iolist_size(exited(Class)).
iolist_size(exited(Class)).
exited(error) ->
<<"exception error: ">>;
<<"exception error: ">>;
exited(exit) ->
<<"exception exit: ">>;
<<"exception exit: ">>;
exited(throw) ->
<<"exception throw: ">>.
<<"exception throw: ">>.
format_stacktrace1(S0, Stack0, PF, SF) ->
Stack1 = lists:dropwhile(fun({M,F,A}) -> SF(M, F, A)
end, lists:reverse(Stack0)),
S = [" " | S0],
Stack = lists:reverse(Stack1),
format_stacktrace2(S, Stack, 1, PF).
format_stacktrace2(S, [{M,F,A}|Fs], N, PF) when is_integer(A) ->
[io_lib:fwrite(<<"~s~s ~s">>,
[sep(N, S), origin(N, M, F, A), mfa_to_string(M, F, A)])
| format_stacktrace2(S, Fs, N + 1, PF)];
format_stacktrace2(S, [{M,F,As}|Fs], N, PF) when is_list(As) ->
A = length(As),
CalledAs = [S,<<" called as ">>],
C = format_call("", CalledAs, {M,F}, As, PF),
[io_lib:fwrite(<<"~s~s ~s\n~s~s">>,
[sep(N, S), origin(N, M, F, A), mfa_to_string(M, F, A),
CalledAs, C])
| format_stacktrace2(S, Fs, N + 1, PF)];
Stack1 = lists:dropwhile(fun({M, F, A}) -> SF(M, F, A)
end, lists:reverse(Stack0)),
S = [" " | S0],
Stack = lists:reverse(Stack1),
format_stacktrace2(S, Stack, 1, PF).
format_stacktrace2(S, [{M, F, A} | Fs], N, PF) when is_integer(A) ->
[io_lib:fwrite(<<"~s~s ~s">>,
[sep(N, S), origin(N, M, F, A), mfa_to_string(M, F, A)])
| format_stacktrace2(S, Fs, N + 1, PF)];
format_stacktrace2(S, [{M, F, As} | Fs], N, PF) when is_list(As) ->
A = length(As),
CalledAs = [S, <<" called as ">>],
C = format_call("", CalledAs, {M, F}, As, PF),
[io_lib:fwrite(<<"~s~s ~s\n~s~s">>,
[sep(N, S), origin(N, M, F, A), mfa_to_string(M, F, A),
CalledAs, C])
| format_stacktrace2(S, Fs, N + 1, PF)];
format_stacktrace2(_S, [], _N, _PF) ->
"".
"".
argss(0) ->
<<"no arguments">>;
<<"no arguments">>;
argss(1) ->
<<"one argument">>;
<<"one argument">>;
argss(2) ->
<<"two arguments">>;
<<"two arguments">>;
argss(I) ->
io_lib:fwrite(<<"~w arguments">>, [I]).
io_lib:fwrite(<<"~w arguments">>, [I]).
format_value(V, ErrStr, Class, PF, Str) ->
Pre1Sz = exited_size(Class),
Str1 = PF(V, Pre1Sz + iolist_size([Str, ErrStr])+1),
[ErrStr | case count_nl(Str1) of
N1 when N1 > 1 ->
Str2 = PF(V, iolist_size(Str) + 1 + Pre1Sz),
case count_nl(Str2) < N1 of
true ->
[$\n, Str, n_spaces(Pre1Sz) | Str2];
false ->
Str1
end;
_ ->
Str1
end].
Pre1Sz = exited_size(Class),
Str1 = PF(V, Pre1Sz + iolist_size([Str, ErrStr]) + 1),
[ErrStr | case count_nl(Str1) of
N1 when N1 > 1 ->
Str2 = PF(V, iolist_size(Str) + 1 + Pre1Sz),
case count_nl(Str2) < N1 of
true ->
[$\n, Str, n_spaces(Pre1Sz) | Str2];
false ->
Str1
end;
_ ->
Str1
end].
format_fun(Fun) when is_function(Fun) ->
{module, M} = erlang:fun_info(Fun, module),
{name, F} = erlang:fun_info(Fun, name),
{arity, A} = erlang:fun_info(Fun, arity),
case erlang:fun_info(Fun, type) of
{type, local} when F =:= "" ->
io_lib:fwrite(<<"~w">>, [Fun]);
{type, local} when M =:= erl_eval ->
io_lib:fwrite(<<"interpreted function with arity ~w">>, [A]);
{type, local} ->
mfa_to_string(M, F, A);
{type, external} ->
mfa_to_string(M, F, A)
end.
{module, M} = erlang:fun_info(Fun, module),
{name, F} = erlang:fun_info(Fun, name),
{arity, A} = erlang:fun_info(Fun, arity),
case erlang:fun_info(Fun, type) of
{type, local} when F =:= "" ->
io_lib:fwrite(<<"~w">>, [Fun]);
{type, local} when M =:= erl_eval ->
io_lib:fwrite(<<"interpreted function with arity ~w">>, [A]);
{type, local} ->
mfa_to_string(M, F, A);
{type, external} ->
mfa_to_string(M, F, A)
end.
format_errstr_call(ErrStr, Class, ForMForFun, As, PF, Pre0) ->
Pre1 = [Pre0 | n_spaces(exited_size(Class))],
format_call(ErrStr, Pre1, ForMForFun, As, PF).
Pre1 = [Pre0 | n_spaces(exited_size(Class))],
format_call(ErrStr, Pre1, ForMForFun, As, PF).
format_call(ErrStr, Pre1, ForMForFun, As, PF) ->
Arity = length(As),
[ErrStr |
case is_op(ForMForFun, Arity) of
{yes,Op} ->
format_op(ErrStr, Pre1, Op, As, PF);
Arity = length(As),
[ErrStr |
case is_op(ForMForFun, Arity) of
{yes, Op} ->
format_op(ErrStr, Pre1, Op, As, PF);
no ->
MFs = mf_to_string(ForMForFun, Arity),
I1 = iolist_size([Pre1,ErrStr|MFs]),
S1 = pp_arguments(PF, As, I1),
S2 = pp_arguments(PF, As, iolist_size([Pre1|MFs])),
Long = count_nl(pp_arguments(PF, [a2345,b2345], I1)) > 0,
case Long or (count_nl(S2) < count_nl(S1)) of
true ->
[$\n, Pre1, MFs, S2];
false ->
[MFs, S1]
end
end].
MFs = mf_to_string(ForMForFun, Arity),
I1 = iolist_size([Pre1, ErrStr | MFs]),
S1 = pp_arguments(PF, As, I1),
S2 = pp_arguments(PF, As, iolist_size([Pre1 | MFs])),
Long = count_nl(pp_arguments(PF, [a2345, b2345], I1)) > 0,
case Long or (count_nl(S2) < count_nl(S1)) of
true ->
[$\n, Pre1, MFs, S2];
false ->
[MFs, S1]
end
end].
mfa_to_string(M, F, A) ->
io_lib:fwrite(<<"~s/~w">>, [mf_to_string({M, F}, A), A]).
io_lib:fwrite(<<"~s/~w">>, [mf_to_string({M, F}, A), A]).
mf_to_string({M, F}, A) ->
case erl_internal:bif(M, F, A) of
true ->
io_lib:fwrite(<<"~w">>, [F]);
false ->
case is_op({M, F}, A) of
{yes, '/'} ->
io_lib:fwrite(<<"~w">>, [F]);
{yes, F} ->
atom_to_list(F);
no ->
io_lib:fwrite(<<"~w:~w">>, [M, F])
end
end;
case erl_internal:bif(M, F, A) of
true ->
io_lib:fwrite(<<"~w">>, [F]);
false ->
case is_op({M, F}, A) of
{yes, '/'} ->
io_lib:fwrite(<<"~w">>, [F]);
{yes, F} ->
atom_to_list(F);
no ->
io_lib:fwrite(<<"~w:~w">>, [M, F])
end
end;
mf_to_string(Fun, _A) when is_function(Fun) ->
format_fun(Fun);
format_fun(Fun);
mf_to_string(F, _A) ->
io_lib:fwrite(<<"~w">>, [F]).
io_lib:fwrite(<<"~w">>, [F]).
n_args(A) when is_integer(A) ->
A;
A;
n_args(As) when is_list(As) ->
length(As).
length(As).
origin(1, M, F, A) ->
case is_op({M, F}, n_args(A)) of
{yes, F} -> <<"in operator ">>;
no -> <<"in function ">>
end;
case is_op({M, F}, n_args(A)) of
{yes, F} -> <<"in operator ">>;
no -> <<"in function ">>
end;
origin(_N, _M, _F, _A) ->
<<"in call from">>.
<<"in call from">>.
sep(1, S) -> S;
sep(_, S) -> [$\n | S].
count_nl([E | Es]) ->
count_nl(E) + count_nl(Es);
count_nl(E) + count_nl(Es);
count_nl($\n) ->
1;
1;
count_nl(Bin) when is_binary(Bin) ->
count_nl(binary_to_list(Bin));
count_nl(binary_to_list(Bin));
count_nl(_) ->
0.
0.
is_op(ForMForFun, A) ->
try
{erlang,F} = ForMForFun,
_ = erl_internal:op_type(F, A),
{yes,F}
catch error:_ -> no
end.
try
{erlang, F} = ForMForFun,
_ = erl_internal:op_type(F, A),
{yes, F}
catch error:_ -> no
end.
format_op(ErrStr, Pre, Op, [A1, A2], PF) ->
I1 = iolist_size([ErrStr,Pre]),
S1 = PF(A1, I1+1),
S2 = PF(A2, I1+1),
OpS = atom_to_list(Op),
Pre1 = [$\n | n_spaces(I1)],
case count_nl(S1) > 0 of
true ->
[S1,Pre1,OpS,Pre1|S2];
false ->
OpS2 = io_lib:fwrite(<<" ~s ">>, [Op]),
S2_2 = PF(A2, iolist_size([ErrStr,Pre,S1|OpS2])+1),
case count_nl(S2) < count_nl(S2_2) of
true ->
[S1,Pre1,OpS,Pre1|S2];
false ->
[S1,OpS2|S2_2]
end
end.
I1 = iolist_size([ErrStr, Pre]),
S1 = PF(A1, I1 + 1),
S2 = PF(A2, I1 + 1),
OpS = atom_to_list(Op),
Pre1 = [$\n | n_spaces(I1)],
case count_nl(S1) > 0 of
true ->
[S1, Pre1, OpS, Pre1 | S2];
false ->
OpS2 = io_lib:fwrite(<<" ~s ">>, [Op]),
S2_2 = PF(A2, iolist_size([ErrStr, Pre, S1 | OpS2]) + 1),
case count_nl(S2) < count_nl(S2_2) of
true ->
[S1, Pre1, OpS, Pre1 | S2];
false ->
[S1, OpS2 | S2_2]
end
end.
pp_arguments(PF, As, I) ->
case {As, io_lib:printable_list(As)} of
{[Int | T], true} ->
L = integer_to_list(Int),
Ll = length(L),
A = list_to_atom(lists:duplicate(Ll, $a)),
S0 = binary_to_list(iolist_to_binary(PF([A | T], I+1))),
brackets_to_parens([$[,L,string:sub_string(S0, 2+Ll)]);
_ ->
brackets_to_parens(PF(As, I+1))
end.
case {As, io_lib:printable_list(As)} of
{[Int | T], true} ->
L = integer_to_list(Int),
Ll = length(L),
A = list_to_atom(lists:duplicate(Ll, $a)),
S0 = binary_to_list(iolist_to_binary(PF([A | T], I + 1))),
brackets_to_parens([$[, L, string:sub_string(S0, 2 + Ll)]);
_ ->
brackets_to_parens(PF(As, I + 1))
end.
brackets_to_parens(S) ->
B = iolist_to_binary(S),
Sz = byte_size(B) - 2,
<<$[,R:Sz/binary,$]>> = B,
[$(,R,$)].
B = iolist_to_binary(S),
Sz = byte_size(B) - 2,
<<$[, R:Sz/binary, $]>> = B,
[$(, R, $)].

+ 640
- 640
src/misc/lager_trunc_io.erl
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 752
- 752
src/misc/rumUtil.erl
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 9
- 9
src/rotator/lager_rotator_behaviour.erl Voir le fichier

@ -1,18 +1,18 @@
-module(lager_rotator_behaviour).
%% Create a log file
-callback(create_logfile(Name:: list(), Buffer::{integer(), integer()} | any()) ->
{ok, {file:io_device(), integer(), file:date_time(), integer()}} | {error, any()}).
-callback(create_logfile(Name :: list(), Buffer :: {integer(), integer()} | any()) ->
{ok, {file:io_device(), integer(), file:date_time(), integer()}} | {error, any()}).
%% Open a log file
-callback(open_logfile(Name:: list(), Buffer::{integer(), integer()} | any()) ->
{ok, {file:io_device(), integer(), file:date_time(), integer()}} | {error, any()}).
-callback(open_logfile(Name :: list(), Buffer :: {integer(), integer()} | any()) ->
{ok, {file:io_device(), integer(), file:date_time(), integer()}} | {error, any()}).
%% Ensure reference to current target, could be rotated
-callback(ensure_logfile(Name:: list(), FD::file:io_device(), Inode:: integer(), Ctime::file:date_time(),
Buffer::{integer(), integer()} | any()) ->
{ok, {file:io_device(), integer(), file:date_time(), integer()}} | {error, any()}).
-callback(ensure_logfile(Name :: list(), FD :: file:io_device(), Inode :: integer(), Ctime :: file:date_time(),
Buffer :: {integer(), integer()} | any()) ->
{ok, {file:io_device(), integer(), file:date_time(), integer()}} | {error, any()}).
%% Rotate the log file
-callback(rotate_logfile(Name:: list(), Count:: integer()) ->
ok).
-callback(rotate_logfile(Name :: list(), Count :: integer()) ->
ok).

+ 152
- 152
src/rotator/lager_rotator_default.erl Voir le fichier

@ -5,7 +5,7 @@
-behaviour(lager_rotator_behaviour).
-export([
create_logfile/2, open_logfile/2, ensure_logfile/5, rotate_logfile/2
create_logfile/2, open_logfile/2, ensure_logfile/5, rotate_logfile/2
]).
-ifdef(TEST).
@ -13,185 +13,185 @@
-endif.
create_logfile(Name, Buffer) ->
open_logfile(Name, Buffer).
open_logfile(Name, Buffer).
open_logfile(Name, Buffer) ->
case filelib:ensure_dir(Name) of
ok ->
Options = [append, raw] ++
case filelib:ensure_dir(Name) of
ok ->
Options = [append, raw] ++
case Buffer of
{Size0, Interval} when is_integer(Interval), Interval >= 0, is_integer(Size0), Size0 >= 0 ->
[{delayed_write, Size0, Interval}];
_ -> []
{Size0, Interval} when is_integer(Interval), Interval >= 0, is_integer(Size0), Size0 >= 0 ->
[{delayed_write, Size0, Interval}];
_ -> []
end,
case file:open(Name, Options) of
{ok, FD} ->
case file:read_file_info(Name, [raw]) of
{ok, FInfo0} ->
Inode = FInfo0#file_info.inode,
{ok, Ctime} = maybe_update_ctime(Name, FInfo0),
Size1 = FInfo0#file_info.size,
{ok, {FD, Inode, Ctime, Size1}};
X -> X
end;
Y -> Y
end;
Z -> Z
end.
case file:open(Name, Options) of
{ok, FD} ->
case file:read_file_info(Name, [raw]) of
{ok, FInfo0} ->
Inode = FInfo0#file_info.inode,
{ok, Ctime} = maybe_update_ctime(Name, FInfo0),
Size1 = FInfo0#file_info.size,
{ok, {FD, Inode, Ctime, Size1}};
X -> X
end;
Y -> Y
end;
Z -> Z
end.
ensure_logfile(Name, undefined, _Inode, _Ctime, Buffer) ->
open_logfile(Name, Buffer);
open_logfile(Name, Buffer);
ensure_logfile(Name, FD, Inode0, Ctime0, Buffer) ->
case rumUtil:has_file_changed(Name, Inode0, Ctime0) of
{true, _FInfo} ->
reopen_logfile(Name, FD, Buffer);
{_, FInfo} ->
{ok, {FD, Inode0, Ctime0, FInfo#file_info.size}}
end.
case rumUtil:has_file_changed(Name, Inode0, Ctime0) of
{true, _FInfo} ->
reopen_logfile(Name, FD, Buffer);
{_, FInfo} ->
{ok, {FD, Inode0, Ctime0, FInfo#file_info.size}}
end.
reopen_logfile(Name, FD0, Buffer) ->
%% Flush and close any file handles.
%% delayed write can cause file:close not to do a close
_ = file:datasync(FD0),
_ = file:close(FD0),
_ = file:close(FD0),
case open_logfile(Name, Buffer) of
{ok, {_FD1, _Inode, _Size, _Ctime}=FileInfo} ->
%% inode changed, file was probably moved and
%% recreated
{ok, FileInfo};
Error ->
Error
end.
%% Flush and close any file handles.
%% delayed write can cause file:close not to do a close
_ = file:datasync(FD0),
_ = file:close(FD0),
_ = file:close(FD0),
case open_logfile(Name, Buffer) of
{ok, {_FD1, _Inode, _Size, _Ctime} = FileInfo} ->
%% inode changed, file was probably moved and
%% recreated
{ok, FileInfo};
Error ->
Error
end.
%% renames failing are OK
rotate_logfile(File, 0) ->
%% open the file in write-only mode to truncate/create it
case file:open(File, [write]) of
{ok, FD} ->
_ = file:close(FD),
_ = file:close(FD),
{ok, _Ctime} = maybe_update_ctime(File),
ok;
Error ->
Error
end;
%% open the file in write-only mode to truncate/create it
case file:open(File, [write]) of
{ok, FD} ->
_ = file:close(FD),
_ = file:close(FD),
{ok, _Ctime} = maybe_update_ctime(File),
ok;
Error ->
Error
end;
rotate_logfile(File0, 1) ->
File1 = File0 ++ ".0",
_ = file:rename(File0, File1),
rotate_logfile(File0, 0);
File1 = File0 ++ ".0",
_ = file:rename(File0, File1),
rotate_logfile(File0, 0);
rotate_logfile(File0, Count) ->
File1 = File0 ++ "." ++ integer_to_list(Count - 2),
File2 = File0 ++ "." ++ integer_to_list(Count - 1),
_ = file:rename(File1, File2),
rotate_logfile(File0, Count - 1).
File1 = File0 ++ "." ++ integer_to_list(Count - 2),
File2 = File0 ++ "." ++ integer_to_list(Count - 1),
_ = file:rename(File1, File2),
rotate_logfile(File0, Count - 1).
maybe_update_ctime(Name) ->
case file:read_file_info(Name, [raw]) of
{ok, FInfo} ->
maybe_update_ctime(Name, FInfo);
_ ->
{ok, calendar:local_time()}
end.
case file:read_file_info(Name, [raw]) of
{ok, FInfo} ->
maybe_update_ctime(Name, FInfo);
_ ->
{ok, calendar:local_time()}
end.
maybe_update_ctime(Name, FInfo) ->
{OsType, _} = os:type(),
do_update_ctime(OsType, Name, FInfo).
{OsType, _} = os:type(),
do_update_ctime(OsType, Name, FInfo).
do_update_ctime(win32, Name, FInfo0) ->
% Note: we force the creation time to be the current time.
% On win32 this may prevent the ctime from being updated:
% https://stackoverflow.com/q/8804342/1466825
NewCtime = calendar:local_time(),
FInfo1 = FInfo0#file_info{ctime = NewCtime},
ok = file:write_file_info(Name, FInfo1, [raw]),
{ok, NewCtime};
% Note: we force the creation time to be the current time.
% On win32 this may prevent the ctime from being updated:
% https://stackoverflow.com/q/8804342/1466825
NewCtime = calendar:local_time(),
FInfo1 = FInfo0#file_info{ctime = NewCtime},
ok = file:write_file_info(Name, FInfo1, [raw]),
{ok, NewCtime};
do_update_ctime(_, _Name, FInfo) ->
{ok, FInfo#file_info.ctime}.
{ok, FInfo#file_info.ctime}.
-ifdef(TEST).
rotate_file_test() ->
RotCount = 10,
{ok, TestDir} = rumUtil:create_test_dir(),
TestLog = filename:join(TestDir, "rotation.log"),
Outer = fun(N) ->
?assertEqual(ok, rumUtil:safe_write_file(TestLog, erlang:integer_to_list(N))),
Inner = fun(M) ->
File = lists:flatten([TestLog, $., erlang:integer_to_list(M)]),
?assert(filelib:is_regular(File)),
%% check the expected value is in the file
Number = erlang:list_to_binary(integer_to_list(N - M - 1)),
?assertEqual({ok, Number}, file:read_file(File))
end,
Count = erlang:min(N, RotCount),
% The first time through, Count == 0, so the sequence is empty,
% effectively skipping the inner loop so a rotation can occur that
% creates the file that Inner looks for.
% Don't shoot the messenger, it was worse before this refactoring.
lists:foreach(Inner, lists:seq(0, Count-1)),
rotate_logfile(TestLog, RotCount)
end,
lists:foreach(Outer, lists:seq(0, (RotCount * 2))),
rumUtil:delete_test_dir(TestDir).
RotCount = 10,
{ok, TestDir} = rumUtil:create_test_dir(),
TestLog = filename:join(TestDir, "rotation.log"),
Outer = fun(N) ->
?assertEqual(ok, rumUtil:safe_write_file(TestLog, erlang:integer_to_list(N))),
Inner = fun(M) ->
File = lists:flatten([TestLog, $., erlang:integer_to_list(M)]),
?assert(filelib:is_regular(File)),
%% check the expected value is in the file
Number = erlang:list_to_binary(integer_to_list(N - M - 1)),
?assertEqual({ok, Number}, file:read_file(File))
end,
Count = erlang:min(N, RotCount),
% The first time through, Count == 0, so the sequence is empty,
% effectively skipping the inner loop so a rotation can occur that
% creates the file that Inner looks for.
% Don't shoot the messenger, it was worse before this refactoring.
lists:foreach(Inner, lists:seq(0, Count - 1)),
rotate_logfile(TestLog, RotCount)
end,
lists:foreach(Outer, lists:seq(0, (RotCount * 2))),
rumUtil:delete_test_dir(TestDir).
rotate_file_zero_count_test() ->
%% Test that a rotation count of 0 simply truncates the file
{ok, TestDir} = rumUtil:create_test_dir(),
TestLog = filename:join(TestDir, "rotation.log"),
?assertMatch(ok, rotate_logfile(TestLog, 0)),
?assertNot(filelib:is_regular(TestLog ++ ".0")),
?assertEqual(true, filelib:is_regular(TestLog)),
?assertEqual(1, length(filelib:wildcard(TestLog++"*"))),
%% assert the new file is 0 size:
case file:read_file_info(TestLog, [raw]) of
{ok, FInfo} ->
?assertEqual(0, FInfo#file_info.size);
_ ->
?assert(false)
end,
rumUtil:delete_test_dir(TestDir).
%% Test that a rotation count of 0 simply truncates the file
{ok, TestDir} = rumUtil:create_test_dir(),
TestLog = filename:join(TestDir, "rotation.log"),
?assertMatch(ok, rotate_logfile(TestLog, 0)),
?assertNot(filelib:is_regular(TestLog ++ ".0")),
?assertEqual(true, filelib:is_regular(TestLog)),
?assertEqual(1, length(filelib:wildcard(TestLog ++ "*"))),
%% assert the new file is 0 size:
case file:read_file_info(TestLog, [raw]) of
{ok, FInfo} ->
?assertEqual(0, FInfo#file_info.size);
_ ->
?assert(false)
end,
rumUtil:delete_test_dir(TestDir).
rotate_file_fail_test() ->
{ok, TestDir} = rumUtil:create_test_dir(),
TestLog = filename:join(TestDir, "rotation.log"),
%% set known permissions on it
ok = rumUtil:set_dir_permissions("u+rwx", TestDir),
%% write a file
?assertEqual(ok, rumUtil:safe_write_file(TestLog, "hello")),
case os:type() of
{win32, _} -> ok;
_ ->
%% hose up the permissions
ok = rumUtil:set_dir_permissions("u-w", TestDir),
?assertMatch({error, _}, rotate_logfile(TestLog, 10))
end,
%% check we still only have one file, rotation.log
?assertEqual([TestLog], filelib:wildcard(TestLog++"*")),
?assert(filelib:is_regular(TestLog)),
%% fix the permissions
ok = rumUtil:set_dir_permissions("u+w", TestDir),
?assertMatch(ok, rotate_logfile(TestLog, 10)),
?assert(filelib:is_regular(TestLog ++ ".0")),
?assertEqual(true, filelib:is_regular(TestLog)),
?assertEqual(2, length(filelib:wildcard(TestLog++"*"))),
%% assert the new file is 0 size:
case file:read_file_info(TestLog, [raw]) of
{ok, FInfo} ->
?assertEqual(0, FInfo#file_info.size);
_ ->
?assert(false)
end,
%% check that the .0 file now has the contents "hello"
?assertEqual({ok, <<"hello">>}, file:read_file(TestLog++".0")),
rumUtil:delete_test_dir(TestDir).
{ok, TestDir} = rumUtil:create_test_dir(),
TestLog = filename:join(TestDir, "rotation.log"),
%% set known permissions on it
ok = rumUtil:set_dir_permissions("u+rwx", TestDir),
%% write a file
?assertEqual(ok, rumUtil:safe_write_file(TestLog, "hello")),
case os:type() of
{win32, _} -> ok;
_ ->
%% hose up the permissions
ok = rumUtil:set_dir_permissions("u-w", TestDir),
?assertMatch({error, _}, rotate_logfile(TestLog, 10))
end,
%% check we still only have one file, rotation.log
?assertEqual([TestLog], filelib:wildcard(TestLog ++ "*")),
?assert(filelib:is_regular(TestLog)),
%% fix the permissions
ok = rumUtil:set_dir_permissions("u+w", TestDir),
?assertMatch(ok, rotate_logfile(TestLog, 10)),
?assert(filelib:is_regular(TestLog ++ ".0")),
?assertEqual(true, filelib:is_regular(TestLog)),
?assertEqual(2, length(filelib:wildcard(TestLog ++ "*"))),
%% assert the new file is 0 size:
case file:read_file_info(TestLog, [raw]) of
{ok, FInfo} ->
?assertEqual(0, FInfo#file_info.size);
_ ->
?assert(false)
end,
%% check that the .0 file now has the contents "hello"
?assertEqual({ok, <<"hello">>}, file:read_file(TestLog ++ ".0")),
rumUtil:delete_test_dir(TestDir).
-endif.

+ 62
- 62
src/test/lager_common_test_backend.erl Voir le fichier

@ -4,23 +4,23 @@
%% gen_event callbacks
-export([init/1,
handle_call/2,
handle_event/2,
handle_info/2,
terminate/2,
code_change/3]).
handle_call/2,
handle_event/2,
handle_info/2,
terminate/2,
code_change/3]).
-export([get_logs/0,
bounce/0,
bounce/1]).
bounce/0,
bounce/1]).
%% holds the log messages for retreival on terminate
-record(state, {level :: {mask, integer()},
formatter :: atom(),
format_config :: any(),
log = [] :: list()}).
formatter :: atom(),
format_config :: any(),
log = [] :: list()}).
-include("rum.hrl").
-define(TERSE_FORMAT,[time, " ", color, "[", severity,"] ", message]).
-define(TERSE_FORMAT, [time, " ", color, "[", severity, "] ", message]).
%% @doc Before every test, just
%% lager_common_test_backend:bounce(Level) with the log level of your
@ -33,89 +33,89 @@
-spec get_logs() -> [iolist()] | {error, term()}.
get_logs() ->
gen_event:call(lager_event, ?MODULE, get_logs, infinity).
gen_event:call(lager_event, ?MODULE, get_logs, infinity).
bounce() ->
bounce(error).
bounce(error).
bounce(Level) ->
_ = application:stop(lager),
application:set_env(lager, suppress_application_start_stop, true),
application:set_env(lager, handlers,
[
{lager_common_test_backend, [Level, false]}
]),
ok = eRum:start(),
%% we care more about getting all of our messages here than being
%% careful with the amount of memory that we're using.
error_logger_lager_h:set_high_water(100000),
ok.
_ = application:stop(lager),
application:set_env(lager, suppress_application_start_stop, true),
application:set_env(lager, handlers,
[
{lager_common_test_backend, [Level, false]}
]),
ok = eRum:start(),
%% we care more about getting all of our messages here than being
%% careful with the amount of memory that we're using.
error_logger_lager_h:set_high_water(100000),
ok.
-spec(init(integer()|atom()|[term()]) -> {ok, #state{}} | {error, atom()}).
%% @private
%% @doc Initializes the event handler
init([Level, true]) -> % for backwards compatibility
init([Level,{lager_default_formatter,[{eol, "\n"}]}]);
init([Level,false]) -> % for backwards compatibility
init([Level,{lager_default_formatter,?TERSE_FORMAT ++ ["\n"]}]);
init([Level,{Formatter,FormatterConfig}]) when is_atom(Formatter) ->
case lists:member(Level, ?RumLevels) of
true ->
{ok, #state{level= rumUtil:config_to_mask(Level),
formatter=Formatter,
format_config=FormatterConfig}};
_ ->
{error, bad_log_level}
end;
init([Level, {lager_default_formatter, [{eol, "\n"}]}]);
init([Level, false]) -> % for backwards compatibility
init([Level, {lager_default_formatter, ?TERSE_FORMAT ++ ["\n"]}]);
init([Level, {Formatter, FormatterConfig}]) when is_atom(Formatter) ->
case lists:member(Level, ?RumLevels) of
true ->
{ok, #state{level = rumUtil:config_to_mask(Level),
formatter = Formatter,
format_config = FormatterConfig}};
_ ->
{error, bad_log_level}
end;
init(Level) ->
init([Level,{lager_default_formatter,?TERSE_FORMAT ++ ["\n"]}]).
init([Level, {lager_default_formatter, ?TERSE_FORMAT ++ ["\n"]}]).
-spec(handle_event(tuple(), #state{}) -> {ok, #state{}}).
%% @private
handle_event({log, Message},
#state{level=L,formatter=Formatter,format_config=FormatConfig,log=Logs} = State) ->
case rumUtil:is_loggable(Message,L,?MODULE) of
true ->
Log = Formatter:format(Message,FormatConfig),
ct:pal(Log),
{ok, State#state{log=[Log|Logs]}};
false ->
{ok, State}
end;
#state{level = L, formatter = Formatter, format_config = FormatConfig, log = Logs} = State) ->
case rumUtil:is_loggable(Message, L, ?MODULE) of
true ->
Log = Formatter:format(Message, FormatConfig),
ct:pal(Log),
{ok, State#state{log = [Log | Logs]}};
false ->
{ok, State}
end;
handle_event(Event, State) ->
ct:pal(Event),
{ok, State#state{log = [Event|State#state.log]}}.
ct:pal(Event),
{ok, State#state{log = [Event | State#state.log]}}.
-spec(handle_call(any(), #state{}) -> {ok, any(), #state{}}).
%% @private
%% @doc gets and sets loglevel. This is part of the lager backend api.
handle_call(get_loglevel, #state{level=Level} = State) ->
{ok, Level, State};
handle_call(get_loglevel, #state{level = Level} = State) ->
{ok, Level, State};
handle_call({set_loglevel, Level}, State) ->
case lists:member(Level, ?RumLevels) of
true ->
{ok, ok, State#state{level= rumUtil:config_to_mask(Level)}};
_ ->
{ok, {error, bad_log_level}, State}
end;
case lists:member(Level, ?RumLevels) of
true ->
{ok, ok, State#state{level = rumUtil:config_to_mask(Level)}};
_ ->
{ok, {error, bad_log_level}, State}
end;
handle_call(get_logs, #state{log = Logs} = State) ->
{ok, lists:reverse(Logs), State};
{ok, lists:reverse(Logs), State};
handle_call(_, State) ->
{ok, ok, State}.
{ok, ok, State}.
-spec(handle_info(any(), #state{}) -> {ok, #state{}}).
%% @private
%% @doc gen_event callback, does nothing.
handle_info(_, State) ->
{ok, State}.
{ok, State}.
-spec(code_change(any(), #state{}, any()) -> {ok, #state{}}).
%% @private
%% @doc gen_event callback, does nothing.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
{ok, State}.
-spec(terminate(any(), #state{}) -> {ok, list()}).
%% @doc gen_event callback, does nothing.
terminate(_Reason, #state{log=Logs}) ->
{ok, lists:reverse(Logs)}.
terminate(_Reason, #state{log = Logs}) ->
{ok, lists:reverse(Logs)}.

+ 251
- 251
src/transform/lager_transform.erl Voir le fichier

@ -40,300 +40,300 @@ parse_transform(AST, Options) ->
erlang:put(records, []),
%% .app file should either be in the outdir, or the same dir as the source file
guess_application(proplists:get_value(outdir, Options), hd(AST)),
walk_ast([], AST).
walk_ast([], AST).
walk_ast(Acc, []) ->
case get(print_records_flag) of
true ->
insert_record_attribute(Acc);
false ->
lists:reverse(Acc)
end;
walk_ast(Acc, [{attribute, _, module, {Module, _PmodArgs}}=H|T]) ->
%% A wild parameterized module appears!
put(module, Module),
walk_ast([H|Acc], T);
walk_ast(Acc, [{attribute, _, module, Module}=H|T]) ->
put(module, Module),
walk_ast([H|Acc], T);
walk_ast(Acc, [{attribute, _, lager_function_transforms, FromModule }=H|T]) ->
%% Merge transform options from the module over the compile options
FromOptions = get(functions),
put(functions, orddict:merge(fun(_Key, _V1, V2) -> V2 end, FromOptions, lists:keysort(1, FromModule))),
walk_ast([H|Acc], T);
walk_ast(Acc, [{function, Line, Name, Arity, Clauses}|T]) ->
put(function, Name),
walk_ast([{function, Line, Name, Arity,
walk_clauses([], Clauses)}|Acc], T);
walk_ast(Acc, [{attribute, _, record, {Name, Fields}}=H|T]) ->
FieldNames = lists:map(fun record_field_name/1, Fields),
stash_record({Name, FieldNames}),
walk_ast([H|Acc], T);
walk_ast(Acc, [H|T]) ->
walk_ast([H|Acc], T).
case get(print_records_flag) of
true ->
insert_record_attribute(Acc);
false ->
lists:reverse(Acc)
end;
walk_ast(Acc, [{attribute, _, module, {Module, _PmodArgs}} = H | T]) ->
%% A wild parameterized module appears!
put(module, Module),
walk_ast([H | Acc], T);
walk_ast(Acc, [{attribute, _, module, Module} = H | T]) ->
put(module, Module),
walk_ast([H | Acc], T);
walk_ast(Acc, [{attribute, _, lager_function_transforms, FromModule} = H | T]) ->
%% Merge transform options from the module over the compile options
FromOptions = get(functions),
put(functions, orddict:merge(fun(_Key, _V1, V2) -> V2 end, FromOptions, lists:keysort(1, FromModule))),
walk_ast([H | Acc], T);
walk_ast(Acc, [{function, Line, Name, Arity, Clauses} | T]) ->
put(function, Name),
walk_ast([{function, Line, Name, Arity,
walk_clauses([], Clauses)} | Acc], T);
walk_ast(Acc, [{attribute, _, record, {Name, Fields}} = H | T]) ->
FieldNames = lists:map(fun record_field_name/1, Fields),
stash_record({Name, FieldNames}),
walk_ast([H | Acc], T);
walk_ast(Acc, [H | T]) ->
walk_ast([H | Acc], T).
record_field_name({record_field, _, {atom, _, FieldName}}) ->
FieldName;
FieldName;
record_field_name({record_field, _, {atom, _, FieldName}, _Default}) ->
FieldName;
FieldName;
record_field_name({typed_record_field, Field, _Type}) ->
record_field_name(Field).
record_field_name(Field).
walk_clauses(Acc, []) ->
lists:reverse(Acc);
walk_clauses(Acc, [{clause, Line, Arguments, Guards, Body}|T]) ->
walk_clauses([{clause, Line, Arguments, Guards, walk_body([], Body)}|Acc], T).
lists:reverse(Acc);
walk_clauses(Acc, [{clause, Line, Arguments, Guards, Body} | T]) ->
walk_clauses([{clause, Line, Arguments, Guards, walk_body([], Body)} | Acc], T).
walk_body(Acc, []) ->
lists:reverse(Acc);
walk_body(Acc, [H|T]) ->
walk_body([transform_statement(H, get(sinks))|Acc], T).
lists:reverse(Acc);
walk_body(Acc, [H | T]) ->
walk_body([transform_statement(H, get(sinks)) | Acc], T).
transform_statement({call, Line, {remote, _Line1, {atom, _Line2, Module},
{atom, _Line3, Function}}, Arguments0} = Stmt,
Sinks) ->
case lists:member(Module, Sinks) of
true ->
case lists:member(Function, ?RumLevels) of
true ->
SinkName = rumUtil:make_internal_sink_name(Module),
do_transform(Line, SinkName, Function, Arguments0);
false ->
case lists:keyfind(Function, 1, ?RumLevelsUnsafe) of
{Function, Severity} ->
SinkName = rumUtil:make_internal_sink_name(Module),
do_transform(Line, SinkName, Severity, Arguments0, unsafe);
false ->
Stmt
end
end;
false ->
list_to_tuple(transform_statement(tuple_to_list(Stmt), Sinks))
end;
{atom, _Line3, Function}}, Arguments0} = Stmt,
Sinks) ->
case lists:member(Module, Sinks) of
true ->
case lists:member(Function, ?RumLevels) of
true ->
SinkName = rumUtil:make_internal_sink_name(Module),
do_transform(Line, SinkName, Function, Arguments0);
false ->
case lists:keyfind(Function, 1, ?RumLevelsUnsafe) of
{Function, Severity} ->
SinkName = rumUtil:make_internal_sink_name(Module),
do_transform(Line, SinkName, Severity, Arguments0, unsafe);
false ->
Stmt
end
end;
false ->
list_to_tuple(transform_statement(tuple_to_list(Stmt), Sinks))
end;
transform_statement(Stmt, Sinks) when is_tuple(Stmt) ->
list_to_tuple(transform_statement(tuple_to_list(Stmt), Sinks));
list_to_tuple(transform_statement(tuple_to_list(Stmt), Sinks));
transform_statement(Stmt, Sinks) when is_list(Stmt) ->
[transform_statement(S, Sinks) || S <- Stmt];
[transform_statement(S, Sinks) || S <- Stmt];
transform_statement(Stmt, _Sinks) ->
Stmt.
Stmt.
add_function_transforms(_Line, DefaultAttrs, []) ->
DefaultAttrs;
add_function_transforms(Line, DefaultAttrs, [{Atom, on_emit, {Module, Function}}|Remainder]) ->
NewFunction = {tuple, Line, [
{atom, Line, Atom},
{'fun', Line, {
function, {atom, Line, Module}, {atom, Line, Function}, {integer, Line, 0}
}}
]},
add_function_transforms(Line, {cons, Line, NewFunction, DefaultAttrs}, Remainder);
add_function_transforms(Line, DefaultAttrs, [{Atom, on_log, {Module, Function}}|Remainder]) ->
NewFunction = {tuple, Line, [
{atom, Line, Atom},
{call, Line, {remote, Line, {atom, Line, Module}, {atom, Line, Function}}, []}
]},
add_function_transforms(Line, {cons, Line, NewFunction, DefaultAttrs}, Remainder).
DefaultAttrs;
add_function_transforms(Line, DefaultAttrs, [{Atom, on_emit, {Module, Function}} | Remainder]) ->
NewFunction = {tuple, Line, [
{atom, Line, Atom},
{'fun', Line, {
function, {atom, Line, Module}, {atom, Line, Function}, {integer, Line, 0}
}}
]},
add_function_transforms(Line, {cons, Line, NewFunction, DefaultAttrs}, Remainder);
add_function_transforms(Line, DefaultAttrs, [{Atom, on_log, {Module, Function}} | Remainder]) ->
NewFunction = {tuple, Line, [
{atom, Line, Atom},
{call, Line, {remote, Line, {atom, Line, Module}, {atom, Line, Function}}, []}
]},
add_function_transforms(Line, {cons, Line, NewFunction, DefaultAttrs}, Remainder).
do_transform(Line, SinkName, Severity, Arguments0) ->
do_transform(Line, SinkName, Severity, Arguments0, safe).
do_transform(Line, SinkName, Severity, Arguments0, safe).
do_transform(Line, SinkName, Severity, Arguments0, Safety) ->
SeverityAsInt= rumUtil:level_to_num(Severity),
DefaultAttrs0 = {cons, Line, {tuple, Line, [
{atom, Line, module}, {atom, Line, get(module)}]},
{cons, Line, {tuple, Line, [
{atom, Line, function}, {atom, Line, get(function)}]},
{cons, Line, {tuple, Line, [
{atom, Line, line},
{integer, Line, Line}]},
{cons, Line, {tuple, Line, [
{atom, Line, pid},
{call, Line, {atom, Line, pid_to_list}, [
{call, Line, {atom, Line ,self}, []}]}]},
{cons, Line, {tuple, Line, [
{atom, Line, node},
{call, Line, {atom, Line, node}, []}]},
%% get the metadata with lager:md(), this will always return a list so we can use it as the tail here
{call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, md}}, []}}}}}},
%{nil, Line}}}}}}},
Functions = get(functions),
DefaultAttrs1 = add_function_transforms(Line, DefaultAttrs0, Functions),
DefaultAttrs = case erlang:get(application) of
undefined ->
DefaultAttrs1;
App ->
%% stick the application in the attribute list
concat_lists({cons, Line, {tuple, Line, [
{atom, Line, application},
{atom, Line, App}]},
{nil, Line}}, DefaultAttrs1)
end,
{Meta, Message, Arguments} = handle_args(DefaultAttrs, Line, Arguments0),
%% Generate some unique variable names so we don't accidentally export from case clauses.
%% Note that these are not actual atoms, but the AST treats variable names as atoms.
LevelVar = make_varname("__Level", Line),
TracesVar = make_varname("__Traces", Line),
PidVar = make_varname("__Pid", Line),
LogFun = case Safety of
safe ->
do_log;
unsafe ->
do_log_unsafe
end,
%% Wrap the call to lager:dispatch_log/6 in case that will avoid doing any work if this message is not elegible for logging
%% See lager.erl (lines 89-100) for lager:dispatch_log/6
%% case {whereis(Sink), whereis(?DEFAULT_SINK), lager_config:get({Sink, loglevel}, {?LOG_NONE, []})} of
{'case',Line,
{tuple,Line,
[{call, Line, {atom, Line, whereis}, [{atom, Line, SinkName}]},
{call, Line, {atom, Line, whereis}, [{atom, Line, ?RumDefSink}]},
{call, Line,
{remote, Line, {atom, Line, lager_config}, {atom, Line, get}},
[{tuple, Line, [{atom, Line, SinkName}, {atom, Line, loglevel}]},
{tuple, Line, [{integer, Line, 0}, {nil, Line}]}]}]},
%% {undefined, undefined, _} -> {error, lager_not_running};
[{clause,Line,
[{tuple,Line,
[{atom,Line,undefined},{atom,Line,undefined},{var,Line,'_'}]}],
[],
%% trick the linter into avoiding a 'term constructed but not used' error:
%% (fun() -> {error, lager_not_running} end)()
[{call, Line, {'fun', Line, {clauses, [{clause, Line, [],[], [{tuple, Line, [{atom, Line, error},{atom, Line, lager_not_running}]}]}]}}, []}]
},
%% {undefined, _, _} -> {error, {sink_not_configured, Sink}};
{clause,Line,
[{tuple,Line,
[{atom,Line,undefined},{var,Line,'_'},{var,Line,'_'}]}],
[],
%% same trick as above to avoid linter error
[{call, Line, {'fun', Line, {clauses, [{clause, Line, [],[], [{tuple,Line, [{atom,Line,error}, {tuple,Line,[{atom,Line,sink_not_configured},{atom,Line,SinkName}]}]}]}]}}, []}]
},
%% {SinkPid, _, {Level, Traces}} when ... -> lager:do_log/9;
{clause,Line,
[{tuple,Line,
[{var,Line,PidVar},
{var,Line,'_'},
{tuple,Line,[{var,Line,LevelVar},{var,Line,TracesVar}]}]}],
[[{op, Line, 'orelse',
{op, Line, '/=', {op, Line, 'band', {var, Line, LevelVar}, {integer, Line, SeverityAsInt}}, {integer, Line, 0}},
{op, Line, '/=', {var, Line, TracesVar}, {nil, Line}}}]],
[{call,Line,{remote, Line, {atom, Line, lager}, {atom, Line, LogFun}},
[{atom,Line,Severity},
Meta,
Message,
Arguments,
{integer, Line, get(truncation_size)},
{integer, Line, SeverityAsInt},
{var, Line, LevelVar},
{var, Line, TracesVar},
{atom, Line, SinkName},
{var, Line, PidVar}]}]},
%% _ -> ok
{clause,Line,[{var,Line,'_'}],[],[{atom,Line,ok}]}]}.
SeverityAsInt = rumUtil:level_to_num(Severity),
DefaultAttrs0 = {cons, Line, {tuple, Line, [
{atom, Line, module}, {atom, Line, get(module)}]},
{cons, Line, {tuple, Line, [
{atom, Line, function}, {atom, Line, get(function)}]},
{cons, Line, {tuple, Line, [
{atom, Line, line},
{integer, Line, Line}]},
{cons, Line, {tuple, Line, [
{atom, Line, pid},
{call, Line, {atom, Line, pid_to_list}, [
{call, Line, {atom, Line, self}, []}]}]},
{cons, Line, {tuple, Line, [
{atom, Line, node},
{call, Line, {atom, Line, node}, []}]},
%% get the metadata with lager:md(), this will always return a list so we can use it as the tail here
{call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, md}}, []}}}}}},
%{nil, Line}}}}}}},
Functions = get(functions),
DefaultAttrs1 = add_function_transforms(Line, DefaultAttrs0, Functions),
DefaultAttrs = case erlang:get(application) of
undefined ->
DefaultAttrs1;
App ->
%% stick the application in the attribute list
concat_lists({cons, Line, {tuple, Line, [
{atom, Line, application},
{atom, Line, App}]},
{nil, Line}}, DefaultAttrs1)
end,
{Meta, Message, Arguments} = handle_args(DefaultAttrs, Line, Arguments0),
%% Generate some unique variable names so we don't accidentally export from case clauses.
%% Note that these are not actual atoms, but the AST treats variable names as atoms.
LevelVar = make_varname("__Level", Line),
TracesVar = make_varname("__Traces", Line),
PidVar = make_varname("__Pid", Line),
LogFun = case Safety of
safe ->
do_log;
unsafe ->
do_log_unsafe
end,
%% Wrap the call to lager:dispatch_log/6 in case that will avoid doing any work if this message is not elegible for logging
%% See lager.erl (lines 89-100) for lager:dispatch_log/6
%% case {whereis(Sink), whereis(?DEFAULT_SINK), lager_config:get({Sink, loglevel}, {?LOG_NONE, []})} of
{'case', Line,
{tuple, Line,
[{call, Line, {atom, Line, whereis}, [{atom, Line, SinkName}]},
{call, Line, {atom, Line, whereis}, [{atom, Line, ?RumDefSink}]},
{call, Line,
{remote, Line, {atom, Line, lager_config}, {atom, Line, get}},
[{tuple, Line, [{atom, Line, SinkName}, {atom, Line, loglevel}]},
{tuple, Line, [{integer, Line, 0}, {nil, Line}]}]}]},
%% {undefined, undefined, _} -> {error, lager_not_running};
[{clause, Line,
[{tuple, Line,
[{atom, Line, undefined}, {atom, Line, undefined}, {var, Line, '_'}]}],
[],
%% trick the linter into avoiding a 'term constructed but not used' error:
%% (fun() -> {error, lager_not_running} end)()
[{call, Line, {'fun', Line, {clauses, [{clause, Line, [], [], [{tuple, Line, [{atom, Line, error}, {atom, Line, lager_not_running}]}]}]}}, []}]
},
%% {undefined, _, _} -> {error, {sink_not_configured, Sink}};
{clause, Line,
[{tuple, Line,
[{atom, Line, undefined}, {var, Line, '_'}, {var, Line, '_'}]}],
[],
%% same trick as above to avoid linter error
[{call, Line, {'fun', Line, {clauses, [{clause, Line, [], [], [{tuple, Line, [{atom, Line, error}, {tuple, Line, [{atom, Line, sink_not_configured}, {atom, Line, SinkName}]}]}]}]}}, []}]
},
%% {SinkPid, _, {Level, Traces}} when ... -> lager:do_log/9;
{clause, Line,
[{tuple, Line,
[{var, Line, PidVar},
{var, Line, '_'},
{tuple, Line, [{var, Line, LevelVar}, {var, Line, TracesVar}]}]}],
[[{op, Line, 'orelse',
{op, Line, '/=', {op, Line, 'band', {var, Line, LevelVar}, {integer, Line, SeverityAsInt}}, {integer, Line, 0}},
{op, Line, '/=', {var, Line, TracesVar}, {nil, Line}}}]],
[{call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, LogFun}},
[{atom, Line, Severity},
Meta,
Message,
Arguments,
{integer, Line, get(truncation_size)},
{integer, Line, SeverityAsInt},
{var, Line, LevelVar},
{var, Line, TracesVar},
{atom, Line, SinkName},
{var, Line, PidVar}]}]},
%% _ -> ok
{clause, Line, [{var, Line, '_'}], [], [{atom, Line, ok}]}]}.
handle_args(DefaultAttrs, Line, [{cons, LineNum, {tuple, _, _}, _} = Attrs]) ->
{concat_lists(DefaultAttrs, Attrs), {string, LineNum, ""}, {atom, Line, none}};
{concat_lists(DefaultAttrs, Attrs), {string, LineNum, ""}, {atom, Line, none}};
handle_args(DefaultAttrs, Line, [Format]) ->
{DefaultAttrs, Format, {atom, Line, none}};
{DefaultAttrs, Format, {atom, Line, none}};
handle_args(DefaultAttrs, Line, [Arg1, Arg2]) ->
%% some ambiguity here, figure out if these arguments are
%% [Format, Args] or [Attr, Format].
%% The trace attributes will be a list of tuples, so check
%% for that.
case {element(1, Arg1), Arg1} of
{_, {cons, _, {tuple, _, _}, _}} ->
{concat_lists(Arg1, DefaultAttrs),
Arg2, {atom, Line, none}};
{Type, _} when Type == var;
Type == lc;
Type == call;
Type == record_field ->
%% crap, its not a literal. look at the second
%% argument to see if it is a string
case Arg2 of
{string, _, _} ->
{concat_lists(Arg1, DefaultAttrs),
Arg2, {atom, Line, none}};
_ ->
%% not a string, going to have to guess
%% it's the argument list
{DefaultAttrs, Arg1, Arg2}
end;
_ ->
{DefaultAttrs, Arg1, Arg2}
end;
%% some ambiguity here, figure out if these arguments are
%% [Format, Args] or [Attr, Format].
%% The trace attributes will be a list of tuples, so check
%% for that.
case {element(1, Arg1), Arg1} of
{_, {cons, _, {tuple, _, _}, _}} ->
{concat_lists(Arg1, DefaultAttrs),
Arg2, {atom, Line, none}};
{Type, _} when Type == var;
Type == lc;
Type == call;
Type == record_field ->
%% crap, its not a literal. look at the second
%% argument to see if it is a string
case Arg2 of
{string, _, _} ->
{concat_lists(Arg1, DefaultAttrs),
Arg2, {atom, Line, none}};
_ ->
%% not a string, going to have to guess
%% it's the argument list
{DefaultAttrs, Arg1, Arg2}
end;
_ ->
{DefaultAttrs, Arg1, Arg2}
end;
handle_args(DefaultAttrs, _Line, [Attrs, Format, Args]) ->
{concat_lists(Attrs, DefaultAttrs), Format, Args}.
{concat_lists(Attrs, DefaultAttrs), Format, Args}.
make_varname(Prefix, Line) ->
list_to_atom(Prefix ++ atom_to_list(get(module)) ++ integer_to_list(Line)).
list_to_atom(Prefix ++ atom_to_list(get(module)) ++ integer_to_list(Line)).
%% concat 2 list ASTs by replacing the terminating [] in A with the contents of B
concat_lists({var, Line, _Name}=Var, B) ->
%% concatenating a var with a cons
{call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}},
[{cons, Line, Var, B}]};
concat_lists({var, Line, _Name} = Var, B) ->
%% concatenating a var with a cons
{call, Line, {remote, Line, {atom, Line, lists}, {atom, Line, flatten}},
[{cons, Line, Var, B}]};
concat_lists({lc, Line, _Body, _Generator} = LC, B) ->
%% concatenating a LC with a cons
{call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}},
[{cons, Line, LC, B}]};
%% concatenating a LC with a cons
{call, Line, {remote, Line, {atom, Line, lists}, {atom, Line, flatten}},
[{cons, Line, LC, B}]};
concat_lists({call, Line, _Function, _Args} = Call, B) ->
%% concatenating a call with a cons
{call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}},
[{cons, Line, Call, B}]};
%% concatenating a call with a cons
{call, Line, {remote, Line, {atom, Line, lists}, {atom, Line, flatten}},
[{cons, Line, Call, B}]};
concat_lists({record_field, Line, _Var, _Record, _Field} = Rec, B) ->
%% concatenating a record_field with a cons
{call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}},
[{cons, Line, Rec, B}]};
%% concatenating a record_field with a cons
{call, Line, {remote, Line, {atom, Line, lists}, {atom, Line, flatten}},
[{cons, Line, Rec, B}]};
concat_lists({nil, _Line}, B) ->
B;
B;
concat_lists({cons, Line, Element, Tail}, B) ->
{cons, Line, Element, concat_lists(Tail, B)}.
{cons, Line, Element, concat_lists(Tail, B)}.
stash_record(Record) ->
Records = case erlang:get(records) of
undefined ->
[];
R ->
R
end,
erlang:put(records, [Record|Records]).
Records = case erlang:get(records) of
undefined ->
[];
R ->
R
end,
erlang:put(records, [Record | Records]).
insert_record_attribute(AST) ->
lists:foldl(fun({attribute, Line, module, _}=E, Acc) ->
[E, {attribute, Line, lager_records, erlang:get(records)}|Acc];
(E, Acc) ->
[E|Acc]
end, [], AST).
lists:foldl(fun({attribute, Line, module, _} = E, Acc) ->
[E, {attribute, Line, lager_records, erlang:get(records)} | Acc];
(E, Acc) ->
[E | Acc]
end, [], AST).
guess_application(Dirname, Attr) when Dirname /= undefined ->
case find_app_file(Dirname) of
no_idea ->
%% try it based on source file directory (app.src most likely)
guess_application(undefined, Attr);
_ ->
ok
end;
case find_app_file(Dirname) of
no_idea ->
%% try it based on source file directory (app.src most likely)
guess_application(undefined, Attr);
_ ->
ok
end;
guess_application(undefined, {attribute, _, file, {Filename, _}}) ->
Dir = filename:dirname(Filename),
find_app_file(Dir);
Dir = filename:dirname(Filename),
find_app_file(Dir);
guess_application(_, _) ->
ok.
ok.
find_app_file(Dir) ->
case filelib:wildcard(Dir++"/*.{app,app.src}") of
[] ->
no_idea;
[File] ->
case file:consult(File) of
{ok, [{application, Appname, _Attributes}|_]} ->
erlang:put(application, Appname);
_ ->
no_idea
end;
_ ->
%% multiple files, uh oh
no_idea
end.
case filelib:wildcard(Dir ++ "/*.{app,app.src}") of
[] ->
no_idea;
[File] ->
case file:consult(File) of
{ok, [{application, Appname, _Attributes} | _]} ->
erlang:put(application, Appname);
_ ->
no_idea
end;
_ ->
%% multiple files, uh oh
no_idea
end.

+ 11
- 11
test/compress_pr_record_test.erl Voir le fichier

@ -3,20 +3,20 @@
-compile([{parse_transform, lager_transform}]).
-record(a, {field1 :: term(),
field2 :: term(),
foo :: term(),
bar :: term(),
baz :: term(),
zyu :: term(),
zix :: term()}).
field2 :: term(),
foo :: term(),
bar :: term(),
baz :: term(),
zyu :: term(),
zix :: term()}).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
nested_record_test() ->
A = #a{field1 = "Notice me senpai"},
Pr_A = eRum:pr(A, ?MODULE),
Pr_A_Comp = eRum:pr(A, ?MODULE, [compress]),
?assertMatch({'$lager_record', a, [{field1, "Notice me senpai"}, {field2, undefined} | _]}, Pr_A),
?assertEqual({'$lager_record', a, [{field1, "Notice me senpai"}]}, Pr_A_Comp).
A = #a{field1 = "Notice me senpai"},
Pr_A = eRum:pr(A, ?MODULE),
Pr_A_Comp = eRum:pr(A, ?MODULE, [compress]),
?assertMatch({'$lager_record', a, [{field1, "Notice me senpai"}, {field2, undefined} | _]}, Pr_A),
?assertEqual({'$lager_record', a, [{field1, "Notice me senpai"}]}, Pr_A_Comp).

+ 60
- 60
test/crash.erl Voir le fichier

@ -10,100 +10,100 @@
-export([start/0]).
-record(state, {
host :: term(),
port :: term()
}).
host :: term(),
port :: term()
}).
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
init(_) ->
{ok, {}}.
{ok, {}}.
handle_call(undef, _, State) ->
{reply, ?MODULE:booger(), State};
{reply, ?MODULE:booger(), State};
handle_call(badfun, _, State) ->
M = booger,
{reply, M(), State};
M = booger,
{reply, M(), State};
handle_call(bad_return, _, _) ->
bleh;
bleh;
handle_call(bad_return_string, _, _) ->
{tuple, {tuple, "string"}};
{tuple, {tuple, "string"}};
handle_call(case_clause, _, State) ->
case State of
goober ->
{reply, ok, State}
end;
case State of
goober ->
{reply, ok, State}
end;
handle_call(case_clause_string, _, State) ->
Foo = atom_to_list(?MODULE),
case Foo of
State ->
{reply, ok, State}
end;
Foo = atom_to_list(?MODULE),
case Foo of
State ->
{reply, ok, State}
end;
handle_call(if_clause, _, State) ->
if State == 1 ->
{reply, ok, State}
end;
if State == 1 ->
{reply, ok, State}
end;
handle_call(try_clause, _, State) ->
Res = try tuple_to_list(State) of
[_A, _B] -> ok
catch
_:_ -> ok
end,
{reply, Res, State};
Res = try tuple_to_list(State) of
[_A, _B] -> ok
catch
_:_ -> ok
end,
{reply, Res, State};
handle_call(badmatch, _, State) ->
{A, B, C} = State,
{reply, [A, B, C], State};
{A, B, C} = State,
{reply, [A, B, C], State};
handle_call(badrecord, _, State) ->
Host = State#state.host,
{reply, Host, State};
Host = State#state.host,
{reply, Host, State};
handle_call(function_clause, _, State) ->
{reply, function(State), State};
{reply, function(State), State};
handle_call(badarith, _, State) ->
Res = 1 / length(tuple_to_list(State)),
{reply, Res, State};
Res = 1 / length(tuple_to_list(State)),
{reply, Res, State};
handle_call(badarg1, _, State) ->
Res = list_to_binary(["foo", bar]),
{reply, Res, State};
Res = list_to_binary(["foo", bar]),
{reply, Res, State};
handle_call(badarg2, _, State) ->
Res = erlang:iolist_to_binary(["foo", bar]),
{reply, Res, State};
Res = erlang:iolist_to_binary(["foo", bar]),
{reply, Res, State};
handle_call(system_limit, _, State) ->
Res = list_to_atom(lists:flatten(lists:duplicate(256, "a"))),
{reply, Res, State};
Res = list_to_atom(lists:flatten(lists:duplicate(256, "a"))),
{reply, Res, State};
handle_call(process_limit, _, State) ->
%% run with +P 300 to make this crash
[erlang:spawn(fun() -> timer:sleep(5000) end) || _ <- lists:seq(0, 500)],
{reply, ok, State};
%% run with +P 300 to make this crash
[erlang:spawn(fun() -> timer:sleep(5000) end) || _ <- lists:seq(0, 500)],
{reply, ok, State};
handle_call(port_limit, _, State) ->
[erlang:open_port({spawn, "ls"}, []) || _ <- lists:seq(0, 1024)],
{reply, ok, State};
[erlang:open_port({spawn, "ls"}, []) || _ <- lists:seq(0, 1024)],
{reply, ok, State};
handle_call(noproc, _, State) ->
Res = gen_event:call(foo, bar, baz),
{reply, Res, State};
Res = gen_event:call(foo, bar, baz),
{reply, Res, State};
handle_call(noproc_proc_lib, _, State) ->
Res = proc_lib:stop(foo),
{reply, Res, State};
Res = proc_lib:stop(foo),
{reply, Res, State};
handle_call(badarity, _, State) ->
F = fun(A, B, C) -> A + B + C end,
Res = F(State),
{reply, Res, State};
F = fun(A, B, C) -> A + B + C end,
Res = F(State),
{reply, Res, State};
handle_call(throw, _, _State) ->
throw(a_ball);
throw(a_ball);
handle_call(_Call, _From, State) ->
{reply, ok, State}.
{reply, ok, State}.
handle_cast(_Cast, State) ->
{noreply, State}.
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
{noreply, State}.
terminate(_, _) ->
ok.
ok.
code_change(_, State, _) ->
{ok, State}.
{ok, State}.
function(X) when is_list(X) ->
ok.
ok.

+ 10
- 10
test/crash_fsm.erl Voir le fichier

@ -6,30 +6,30 @@
%% gen_fsm callbacks
-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
terminate/3, code_change/4]).
terminate/3, code_change/4]).
-record(state, {}).
start() ->
gen_fsm:start({local, ?MODULE}, ?MODULE, [], []).
gen_fsm:start({local, ?MODULE}, ?MODULE, [], []).
crash() ->
gen_fsm:sync_send_event(?MODULE, crash).
gen_fsm:sync_send_event(?MODULE, crash).
%% gen_fsm callbacks
init([]) ->
{ok, state1, #state{}}.
{ok, state1, #state{}}.
handle_event(_Event, StateName, State) ->
{next_state, StateName, State}.
{next_state, StateName, State}.
handle_sync_event(_Event, _From, StateName, State) ->
Reply = ok,
{reply, Reply, StateName, State}.
Reply = ok,
{reply, Reply, StateName, State}.
handle_info(_Info, StateName, State) ->
{next_state, StateName, State}.
{next_state, StateName, State}.
terminate(_Reason, _StateName, _State) ->
ok.
ok.
code_change(_OldVersion, StateName, State, _Extra) ->
{ok, StateName, State}.
{ok, StateName, State}.
state1(_Event, S) -> {next_state, state1, S}.

+ 19
- 19
test/crash_statem.erl Voir le fichier

@ -4,47 +4,47 @@
-behaviour(gen_statem).
-export([
start/0,
crash/0,
stop/1,
timeout/0,
handle_event/4
start/0,
crash/0,
stop/1,
timeout/0,
handle_event/4
]).
-export([terminate/3,code_change/4,init/1,callback_mode/0]).
-export([terminate/3, code_change/4, init/1, callback_mode/0]).
start() ->
gen_statem:start({local,?MODULE}, ?MODULE, [], []).
gen_statem:start({local, ?MODULE}, ?MODULE, [], []).
crash() ->
gen_statem:call(?MODULE, boom).
gen_statem:call(?MODULE, boom).
stop(Reason) ->
gen_statem:call(?MODULE, {stop, Reason}).
gen_statem:call(?MODULE, {stop, Reason}).
timeout() ->
gen_statem:call(?MODULE, timeout).
gen_statem:call(?MODULE, timeout).
%% Mandatory callback functions
terminate(_Reason, _State, _Data) -> ok.
code_change(_Vsn, State, Data, _Extra) -> {ok,State,Data}.
code_change(_Vsn, State, Data, _Extra) -> {ok, State, Data}.
init([]) ->
%% insert rant here about breaking changes in minor versions...
case erlang:system_info(version) of
"8.0" -> {callback_mode(),state1,undefined};
_ -> {ok, state1, undefined}
end.
%% insert rant here about breaking changes in minor versions...
case erlang:system_info(version) of
"8.0" -> {callback_mode(), state1, undefined};
_ -> {ok, state1, undefined}
end.
callback_mode() -> handle_event_function.
%%% state callback(s)
handle_event(state_timeout, timeout, state1, _) ->
{stop, timeout};
{stop, timeout};
handle_event({call, _From}, timeout, _Arg, _Data) ->
{keep_state_and_data, [{state_timeout, 0, timeout}]};
{keep_state_and_data, [{state_timeout, 0, timeout}]};
handle_event({call, _From}, {stop, Reason}, state1, _Data) ->
{stop, Reason}.
{stop, Reason}.
-else.
-export([start/0, crash/0]).

+ 4
- 4
test/lager_app_tests.erl Voir le fichier

@ -6,10 +6,10 @@
get_env_test() ->
application:set_env(myapp, mykey1, <<"Value">>),
application:set_env(myapp, mykey1, <<"Value">>),
?assertEqual(<<"Some">>, lager_app:get_env(myapp, mykey0, <<"Some">>)),
?assertEqual(<<"Value">>, lager_app:get_env(myapp, mykey1, <<"Some">>)),
?assertEqual(<<"Some">>, lager_app:get_env(myapp, mykey0, <<"Some">>)),
?assertEqual(<<"Value">>, lager_app:get_env(myapp, mykey1, <<"Some">>)),
ok.
ok.

+ 28
- 28
test/lager_crash_backend.erl Voir le fichier

@ -21,48 +21,48 @@
-behaviour(gen_event).
-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
code_change/3]).
code_change/3]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
init([CrashBefore, CrashAfter]) ->
case is_tuple(CrashBefore) andalso (timer:now_diff(CrashBefore, os:timestamp()) > 0) of
true ->
%?debugFmt("crashing!~n", []),
{error, crashed};
_ ->
%?debugFmt("Not crashing!~n", []),
case is_tuple(CrashAfter) of
true ->
CrashTime = timer:now_diff(CrashAfter, os:timestamp()) div 1000,
case CrashTime > 0 of
true ->
%?debugFmt("crashing in ~p~n", [CrashTime]),
erlang:send_after(CrashTime, self(), crash),
{ok, {}};
_ -> {error, crashed}
end;
_ ->
{ok, {}}
end
end.
case is_tuple(CrashBefore) andalso (timer:now_diff(CrashBefore, os:timestamp()) > 0) of
true ->
%?debugFmt("crashing!~n", []),
{error, crashed};
_ ->
%?debugFmt("Not crashing!~n", []),
case is_tuple(CrashAfter) of
true ->
CrashTime = timer:now_diff(CrashAfter, os:timestamp()) div 1000,
case CrashTime > 0 of
true ->
%?debugFmt("crashing in ~p~n", [CrashTime]),
erlang:send_after(CrashTime, self(), crash),
{ok, {}};
_ -> {error, crashed}
end;
_ ->
{ok, {}}
end
end.
handle_call(_Request, State) ->
{ok, ok, State}.
{ok, ok, State}.
handle_event(_Event, State) ->
{ok, State}.
{ok, State}.
handle_info(crash, _State) ->
%?debugFmt("Time to crash!~n", []),
crash;
%?debugFmt("Time to crash!~n", []),
crash;
handle_info(_Info, State) ->
{ok, State}.
{ok, State}.
terminate(_Reason, _State) ->
ok.
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
{ok, State}.

+ 100
- 100
test/lager_manager_killer_test.erl Voir le fichier

@ -10,116 +10,116 @@
-define(TEST_SINK_EVENT, '__lager_test_sink_lager_event'). %% <-- used by lager API calls and internals for gen_event
overload_test_() ->
{timeout, 60,
fun() ->
application:stop(lager),
application:load(lager),
Delay = 1000, % sleep 1 sec on every log
KillerHWM = 10, % kill the manager if there are more than 10 pending logs
KillerReinstallAfter = 1000, % reinstall killer after 1 sec
application:set_env(lager, handlers, [{lager_slow_backend, [{delay, Delay}]}]),
application:set_env(lager, async_threshold, undefined),
application:set_env(lager, error_logger_redirect, true),
application:set_env(lager, killer_hwm, KillerHWM),
application:set_env(lager, killer_reinstall_after, KillerReinstallAfter),
ensure_started(lager),
lager_config:set(async, true),
Manager = whereis(lager_event),
erlang:trace(all, true, [procs]),
[eRum:info("~p'th message", [N]) || N <- lists:seq(1,KillerHWM+2)],
Margin = 100,
ok = confirm_manager_exit(Manager, Delay+Margin),
ok = confirm_sink_reregister(lager_event, Margin),
erlang:trace(all, false, [procs]),
wait_until(fun() ->
case proplists:get_value(lager_manager_killer, gen_event:which_handlers(lager_event)) of
[] -> false;
_ -> true
end
end, Margin, 15),
wait_until(fun() ->
case gen_event:call(lager_event, lager_manager_killer, get_settings) of
[KillerHWM, KillerReinstallAfter] -> true;
_Other -> false
end
end, Margin, 15),
application:stop(lager)
end}.
{timeout, 60,
fun() ->
application:stop(lager),
application:load(lager),
Delay = 1000, % sleep 1 sec on every log
KillerHWM = 10, % kill the manager if there are more than 10 pending logs
KillerReinstallAfter = 1000, % reinstall killer after 1 sec
application:set_env(lager, handlers, [{lager_slow_backend, [{delay, Delay}]}]),
application:set_env(lager, async_threshold, undefined),
application:set_env(lager, error_logger_redirect, true),
application:set_env(lager, killer_hwm, KillerHWM),
application:set_env(lager, killer_reinstall_after, KillerReinstallAfter),
ensure_started(lager),
lager_config:set(async, true),
Manager = whereis(lager_event),
erlang:trace(all, true, [procs]),
[eRum:info("~p'th message", [N]) || N <- lists:seq(1, KillerHWM + 2)],
Margin = 100,
ok = confirm_manager_exit(Manager, Delay + Margin),
ok = confirm_sink_reregister(lager_event, Margin),
erlang:trace(all, false, [procs]),
wait_until(fun() ->
case proplists:get_value(lager_manager_killer, gen_event:which_handlers(lager_event)) of
[] -> false;
_ -> true
end
end, Margin, 15),
wait_until(fun() ->
case gen_event:call(lager_event, lager_manager_killer, get_settings) of
[KillerHWM, KillerReinstallAfter] -> true;
_Other -> false
end
end, Margin, 15),
application:stop(lager)
end}.
overload_alternate_sink_test_() ->
{timeout, 60,
fun() ->
application:stop(lager),
application:load(lager),
Delay = 1000, % sleep 1 sec on every log
KillerHWM = 10, % kill the manager if there are more than 10 pending logs
KillerReinstallAfter = 1000, % reinstall killer after 1 sec
application:set_env(lager, handlers, []),
application:set_env(lager, extra_sinks, [{?TEST_SINK_EVENT, [
{handlers, [{lager_slow_backend, [{delay, Delay}]}]},
{killer_hwm, KillerHWM},
{killer_reinstall_after, KillerReinstallAfter},
{async_threshold, undefined}
]}]),
application:set_env(lager, error_logger_redirect, true),
ensure_started(lager),
lager_config:set({?TEST_SINK_EVENT, async}, true),
Manager = whereis(?TEST_SINK_EVENT),
erlang:trace(all, true, [procs]),
[?TEST_SINK_NAME:info("~p'th message", [N]) || N <- lists:seq(1,KillerHWM+2)],
Margin = 100,
ok = confirm_manager_exit(Manager, Delay+Margin),
ok = confirm_sink_reregister(?TEST_SINK_EVENT, Margin),
erlang:trace(all, false, [procs]),
wait_until(fun() ->
case proplists:get_value(lager_manager_killer, gen_event:which_handlers(?TEST_SINK_EVENT)) of
[] -> false;
_ -> true
end
end, Margin, 15),
wait_until(fun() ->
case gen_event:call(?TEST_SINK_EVENT, lager_manager_killer, get_settings) of
[KillerHWM, KillerReinstallAfter] -> true;
_Other -> false
end
end, Margin, 15),
application:stop(lager)
end}.
{timeout, 60,
fun() ->
application:stop(lager),
application:load(lager),
Delay = 1000, % sleep 1 sec on every log
KillerHWM = 10, % kill the manager if there are more than 10 pending logs
KillerReinstallAfter = 1000, % reinstall killer after 1 sec
application:set_env(lager, handlers, []),
application:set_env(lager, extra_sinks, [{?TEST_SINK_EVENT, [
{handlers, [{lager_slow_backend, [{delay, Delay}]}]},
{killer_hwm, KillerHWM},
{killer_reinstall_after, KillerReinstallAfter},
{async_threshold, undefined}
]}]),
application:set_env(lager, error_logger_redirect, true),
ensure_started(lager),
lager_config:set({?TEST_SINK_EVENT, async}, true),
Manager = whereis(?TEST_SINK_EVENT),
erlang:trace(all, true, [procs]),
[?TEST_SINK_NAME:info("~p'th message", [N]) || N <- lists:seq(1, KillerHWM + 2)],
Margin = 100,
ok = confirm_manager_exit(Manager, Delay + Margin),
ok = confirm_sink_reregister(?TEST_SINK_EVENT, Margin),
erlang:trace(all, false, [procs]),
wait_until(fun() ->
case proplists:get_value(lager_manager_killer, gen_event:which_handlers(?TEST_SINK_EVENT)) of
[] -> false;
_ -> true
end
end, Margin, 15),
wait_until(fun() ->
case gen_event:call(?TEST_SINK_EVENT, lager_manager_killer, get_settings) of
[KillerHWM, KillerReinstallAfter] -> true;
_Other -> false
end
end, Margin, 15),
application:stop(lager)
end}.
ensure_started(App) ->
case application:start(App) of
ok ->
ok;
{error, {not_started, Dep}} ->
ensure_started(Dep),
ensure_started(App)
end.
case application:start(App) of
ok ->
ok;
{error, {not_started, Dep}} ->
ensure_started(Dep),
ensure_started(App)
end.
confirm_manager_exit(Manager, Delay) ->
receive
{trace, Manager, exit, killed} ->
?debugFmt("Manager ~p killed", [Manager]);
Other ->
?debugFmt("OTHER MSG: ~p", [Other]),
confirm_manager_exit(Manager, Delay)
after Delay ->
?assert(false)
end.
receive
{trace, Manager, exit, killed} ->
?debugFmt("Manager ~p killed", [Manager]);
Other ->
?debugFmt("OTHER MSG: ~p", [Other]),
confirm_manager_exit(Manager, Delay)
after Delay ->
?assert(false)
end.
confirm_sink_reregister(Sink, Delay) ->
receive
{trace, _Pid, register, Sink} ->
?assertNot(lists:member(lager_manager_killer, gen_event:which_handlers(Sink)))
after Delay ->
?assert(false)
end.
receive
{trace, _Pid, register, Sink} ->
?assertNot(lists:member(lager_manager_killer, gen_event:which_handlers(Sink)))
after Delay ->
?assert(false)
end.
wait_until(_Fun, _Delay, 0) ->
{error, too_many_retries};
{error, too_many_retries};
wait_until(Fun, Delay, Retries) ->
case Fun() of
true -> ok;
false -> timer:sleep(Delay), wait_until(Fun, Delay, Retries-1)
end.
case Fun() of
true -> ok;
false -> timer:sleep(Delay), wait_until(Fun, Delay, Retries - 1)
end.
-endif.

+ 38
- 39
test/lager_metadata_whitelist_test.erl Voir le fichier

@ -1,4 +1,3 @@
-module(lager_metadata_whitelist_test).
@ -6,67 +5,67 @@
-include_lib("eunit/include/eunit.hrl").
setup() ->
ok = error_logger:tty(false),
ok = rumUtil:safe_application_load(lager),
ok = application:set_env(lager, handlers, [{lager_common_test_backend, info}]),
ok = application:set_env(lager, error_logger_redirect, false),
ok = application:unset_env(lager, traces),
ok = eRum:start(),
ok = timer:sleep(250),
ok.
ok = error_logger:tty(false),
ok = rumUtil:safe_application_load(lager),
ok = application:set_env(lager, handlers, [{lager_common_test_backend, info}]),
ok = application:set_env(lager, error_logger_redirect, false),
ok = application:unset_env(lager, traces),
ok = eRum:start(),
ok = timer:sleep(250),
ok.
cleanup(_) ->
ok = application:unset_env(lager, metadata_whitelist),
catch ets:delete(lager_config), %% kill the ets config table with fire
ok = application:stop(lager),
ok = application:stop(goldrush),
ok = error_logger:tty(true).
ok = application:unset_env(lager, metadata_whitelist),
catch ets:delete(lager_config), %% kill the ets config table with fire
ok = application:stop(lager),
ok = application:stop(goldrush),
ok = error_logger:tty(true).
date_time_now() ->
Now = os:timestamp(),
{Date, Time} = rumUtil:format_time(rumUtil:maybe_utc(rumUtil:localtime_ms(Now))),
{Date, Time, Now}.
Now = os:timestamp(),
{Date, Time} = rumUtil:format_time(rumUtil:maybe_utc(rumUtil:localtime_ms(Now))),
{Date, Time, Now}.
basic_test_() ->
{Date, Time, Now} = date_time_now(),
{
foreach,
fun setup/0,
fun cleanup/1,
[{"Meta", fun() ->
Whitelist = [meta0],
ok = application:set_env(lager, metadata_whitelist, Whitelist),
Msg = lager_msg:new("Message", Now, error, [], []),
Expected = iolist_to_binary([Date, " ", Time, " [error] Message\n"]),
Got = iolist_to_binary(lager_default_formatter:format(Msg, [])),
?assertEqual(Expected, Got)
end},
{"Meta1", fun() ->
{Date, Time, Now} = date_time_now(),
{
foreach,
fun setup/0,
fun cleanup/1,
[{"Meta", fun() ->
Whitelist = [meta0],
ok = application:set_env(lager, metadata_whitelist, Whitelist),
Msg = lager_msg:new("Message", Now, error, [], []),
Expected = iolist_to_binary([Date, " ", Time, " [error] Message\n"]),
Got = iolist_to_binary(lager_default_formatter:format(Msg, [])),
?assertEqual(Expected, Got)
end},
{"Meta1", fun() ->
Whitelist = [meta1],
ok = application:set_env(lager, metadata_whitelist, Whitelist),
Msg = lager_msg:new("Message", Now, error, [{meta1, "value1"}], []),
Expected = iolist_to_binary([Date, " ", Time, " [error] meta1=value1 Message\n"]),
Expected = iolist_to_binary([Date, " ", Time, " [error] meta1=value1 Message\n"]),
Got = iolist_to_binary(lager_default_formatter:format(Msg, [])),
?assertEqual(Expected, Got)
end},
end},
{"Meta2", fun() ->
Whitelist = [meta1, meta2],
ok = application:set_env(lager, metadata_whitelist, Whitelist),
Msg = lager_msg:new("Message", Now, error, [{meta1, "value1"}, {meta2, 2}], []),
Expected = iolist_to_binary([Date, " ", Time, " [error] meta1=value1 meta2=2 Message\n"]),
Expected = iolist_to_binary([Date, " ", Time, " [error] meta1=value1 meta2=2 Message\n"]),
Got = iolist_to_binary(lager_default_formatter:format(Msg, [])),
?assertEqual(Expected, Got)
end},
end},
{"Meta3", fun() ->
Whitelist = [meta1, meta2],
ok = application:set_env(lager, metadata_whitelist, Whitelist),
Msg = lager_msg:new("Message", Now, error, [{meta1, "value1"}, {meta3, 3}], []),
Expected = iolist_to_binary([Date, " ", Time, " [error] meta1=value1 Message\n"]),
Expected = iolist_to_binary([Date, " ", Time, " [error] meta1=value1 Message\n"]),
Got = iolist_to_binary(lager_default_formatter:format(Msg, [])),
?assertEqual(Expected, Got)
end}
]
}.
end}
]
}.
-endif.

+ 130
- 130
test/lager_rotate.erl Voir le fichier

@ -25,161 +25,161 @@
-endif.
-record(state, {
dir :: string(),
log1 :: string(),
log1r :: string(),
log2 :: string(),
log2r :: string(),
sink :: string(),
sinkr :: string()
dir :: string(),
log1 :: string(),
log1r :: string(),
log2 :: string(),
log2r :: string(),
sink :: string(),
sinkr :: string()
}).
rotate_test_() ->
{foreach,
fun() ->
{ok, Dir} = rumUtil:create_test_dir(),
Log1 = filename:join(Dir, "test1.log"),
Log2 = filename:join(Dir, "test2.log"),
Sink = filename:join(Dir, "sink.log"),
State = #state{
dir = Dir,
log1 = Log1,
log1r = Log1 ++ ".0",
log2 = Log2,
log2r = Log2 ++ ".0",
sink = Sink,
sinkr = Sink ++ ".0"
},
file:write_file(Log1, []),
file:write_file(Log2, []),
file:write_file(Sink, []),
error_logger:tty(false),
application:load(lager),
application:set_env(lager, handlers, [
{lager_file_backend, [{file, Log1}, {level, info}]},
{lager_file_backend, [{file, Log2}, {level, info}]} ]),
application:set_env(lager, extra_sinks, [
{sink_event,
[{handlers,
[{lager_file_backend, [{file, Sink}, {level, info}]}]}
]}]),
application:set_env(lager, error_logger_redirect, false),
application:set_env(lager, async_threshold, undefined),
eRum:start(),
timer:sleep(1000),
State
end,
fun(#state{}) ->
ok = application:stop(lager),
ok = application:stop(goldrush),
ok = rumUtil:delete_test_dir(),
ok = error_logger:tty(true)
end, [
fun(State) ->
{"Rotate single file",
{foreach,
fun() ->
{ok, Dir} = rumUtil:create_test_dir(),
Log1 = filename:join(Dir, "test1.log"),
Log2 = filename:join(Dir, "test2.log"),
Sink = filename:join(Dir, "sink.log"),
State = #state{
dir = Dir,
log1 = Log1,
log1r = Log1 ++ ".0",
log2 = Log2,
log2r = Log2 ++ ".0",
sink = Sink,
sinkr = Sink ++ ".0"
},
file:write_file(Log1, []),
file:write_file(Log2, []),
file:write_file(Sink, []),
error_logger:tty(false),
application:load(lager),
application:set_env(lager, handlers, [
{lager_file_backend, [{file, Log1}, {level, info}]},
{lager_file_backend, [{file, Log2}, {level, info}]}]),
application:set_env(lager, extra_sinks, [
{sink_event,
[{handlers,
[{lager_file_backend, [{file, Sink}, {level, info}]}]}
]}]),
application:set_env(lager, error_logger_redirect, false),
application:set_env(lager, async_threshold, undefined),
eRum:start(),
timer:sleep(1000),
State
end,
fun(#state{}) ->
ok = application:stop(lager),
ok = application:stop(goldrush),
ok = rumUtil:delete_test_dir(),
ok = error_logger:tty(true)
end, [
fun(State) ->
{"Rotate single file",
fun() ->
eRum:log(error, self(), "Test message 1"),
eRum:log(sink_event, error, self(), "Sink test message 1", []),
eRum:rotate_handler({lager_file_backend, State#state.log1}),
ok = wait_until(fun() -> filelib:is_regular(State#state.log1r) end, 10),
eRum:log(error, self(), "Test message 2"),
eRum:log(sink_event, error, self(), "Sink test message 2", []),
eRum:log(error, self(), "Test message 1"),
eRum:log(sink_event, error, self(), "Sink test message 1", []),
eRum:rotate_handler({lager_file_backend, State#state.log1}),
ok = wait_until(fun() -> filelib:is_regular(State#state.log1r) end, 10),
eRum:log(error, self(), "Test message 2"),
eRum:log(sink_event, error, self(), "Sink test message 2", []),
{ok, File1} = file:read_file(State#state.log1),
{ok, File2} = file:read_file(State#state.log2),
{ok, SinkFile} = file:read_file(State#state.sink),
{ok, File1Old} = file:read_file(State#state.log1r),
{ok, File1} = file:read_file(State#state.log1),
{ok, File2} = file:read_file(State#state.log2),
{ok, SinkFile} = file:read_file(State#state.sink),
{ok, File1Old} = file:read_file(State#state.log1r),
have_no_log(File1, <<"Test message 1">>),
have_log(File1, <<"Test message 2">>),
have_no_log(File1, <<"Test message 1">>),
have_log(File1, <<"Test message 2">>),
have_log(File2, <<"Test message 1">>),
have_log(File2, <<"Test message 2">>),
have_log(File2, <<"Test message 1">>),
have_log(File2, <<"Test message 2">>),
have_log(File1Old, <<"Test message 1">>),
have_no_log(File1Old, <<"Test message 2">>),
have_log(File1Old, <<"Test message 1">>),
have_no_log(File1Old, <<"Test message 2">>),
have_log(SinkFile, <<"Sink test message 1">>),
have_log(SinkFile, <<"Sink test message 2">>)
have_log(SinkFile, <<"Sink test message 1">>),
have_log(SinkFile, <<"Sink test message 2">>)
end}
end,
fun(State) ->
{"Rotate sink",
end,
fun(State) ->
{"Rotate sink",
fun() ->
eRum:log(error, self(), "Test message 1"),
eRum:log(sink_event, error, self(), "Sink test message 1", []),
eRum:rotate_sink(sink_event),
ok = wait_until(fun() -> filelib:is_regular(State#state.sinkr) end, 10),
eRum:log(error, self(), "Test message 2"),
eRum:log(sink_event, error, self(), "Sink test message 2", []),
{ok, File1} = file:read_file(State#state.log1),
{ok, File2} = file:read_file(State#state.log2),
{ok, SinkFile} = file:read_file(State#state.sink),
{ok, SinkFileOld} = file:read_file(State#state.sinkr),
have_log(File1, <<"Test message 1">>),
have_log(File1, <<"Test message 2">>),
have_log(File2, <<"Test message 1">>),
have_log(File2, <<"Test message 2">>),
have_log(SinkFileOld, <<"Sink test message 1">>),
have_no_log(SinkFileOld, <<"Sink test message 2">>),
have_no_log(SinkFile, <<"Sink test message 1">>),
have_log(SinkFile, <<"Sink test message 2">>)
eRum:log(error, self(), "Test message 1"),
eRum:log(sink_event, error, self(), "Sink test message 1", []),
eRum:rotate_sink(sink_event),
ok = wait_until(fun() -> filelib:is_regular(State#state.sinkr) end, 10),
eRum:log(error, self(), "Test message 2"),
eRum:log(sink_event, error, self(), "Sink test message 2", []),
{ok, File1} = file:read_file(State#state.log1),
{ok, File2} = file:read_file(State#state.log2),
{ok, SinkFile} = file:read_file(State#state.sink),
{ok, SinkFileOld} = file:read_file(State#state.sinkr),
have_log(File1, <<"Test message 1">>),
have_log(File1, <<"Test message 2">>),
have_log(File2, <<"Test message 1">>),
have_log(File2, <<"Test message 2">>),
have_log(SinkFileOld, <<"Sink test message 1">>),
have_no_log(SinkFileOld, <<"Sink test message 2">>),
have_no_log(SinkFile, <<"Sink test message 1">>),
have_log(SinkFile, <<"Sink test message 2">>)
end}
end,
fun(State) ->
{"Rotate all",
end,
fun(State) ->
{"Rotate all",
fun() ->
eRum:log(error, self(), "Test message 1"),
eRum:log(sink_event, error, self(), "Sink test message 1", []),
eRum:rotate_all(),
ok = wait_until(fun() -> filelib:is_regular(State#state.sinkr) end, 10),
eRum:log(error, self(), "Test message 2"),
eRum:log(sink_event, error, self(), "Sink test message 2", []),
{ok, File1} = file:read_file(State#state.log1),
{ok, File2} = file:read_file(State#state.log2),
{ok, SinkFile} = file:read_file(State#state.sink),
{ok, File1Old} = file:read_file(State#state.log1r),
{ok, File2Old} = file:read_file(State#state.log2r),
{ok, SinkFileOld} = file:read_file(State#state.sinkr),
eRum:log(error, self(), "Test message 1"),
eRum:log(sink_event, error, self(), "Sink test message 1", []),
eRum:rotate_all(),
ok = wait_until(fun() -> filelib:is_regular(State#state.sinkr) end, 10),
eRum:log(error, self(), "Test message 2"),
eRum:log(sink_event, error, self(), "Sink test message 2", []),
{ok, File1} = file:read_file(State#state.log1),
{ok, File2} = file:read_file(State#state.log2),
{ok, SinkFile} = file:read_file(State#state.sink),
{ok, File1Old} = file:read_file(State#state.log1r),
{ok, File2Old} = file:read_file(State#state.log2r),
{ok, SinkFileOld} = file:read_file(State#state.sinkr),
have_no_log(File1, <<"Test message 1">>),
have_log(File1, <<"Test message 2">>),
have_no_log(File1, <<"Test message 1">>),
have_log(File1, <<"Test message 2">>),
have_no_log(File2, <<"Test message 1">>),
have_log(File2, <<"Test message 2">>),
have_no_log(File2, <<"Test message 1">>),
have_log(File2, <<"Test message 2">>),
have_no_log(SinkFile, <<"Sink test message 1">>),
have_log(SinkFile, <<"Sink test message 2">>),
have_no_log(SinkFile, <<"Sink test message 1">>),
have_log(SinkFile, <<"Sink test message 2">>),
have_log(SinkFileOld, <<"Sink test message 1">>),
have_no_log(SinkFileOld, <<"Sink test message 2">>),
have_log(SinkFileOld, <<"Sink test message 1">>),
have_no_log(SinkFileOld, <<"Sink test message 2">>),
have_log(File1Old, <<"Test message 1">>),
have_no_log(File1Old, <<"Test message 2">>),
have_log(File1Old, <<"Test message 1">>),
have_no_log(File1Old, <<"Test message 2">>),
have_log(File2Old, <<"Test message 1">>),
have_no_log(File2Old, <<"Test message 2">>)
have_log(File2Old, <<"Test message 1">>),
have_no_log(File2Old, <<"Test message 2">>)
end}
end
]}.
end
]}.
have_log(Data, Log) ->
{_,_} = binary:match(Data, Log).
{_, _} = binary:match(Data, Log).
have_no_log(Data, Log) ->
nomatch = binary:match(Data, Log).
nomatch = binary:match(Data, Log).
wait_until(_Fun, 0) -> {error, too_many_retries};
wait_until(Fun, Retry) ->
case Fun() of
true -> ok;
false ->
timer:sleep(500),
wait_until(Fun, Retry-1)
end.
case Fun() of
true -> ok;
false ->
timer:sleep(500),
wait_until(Fun, Retry - 1)
end.

+ 10
- 10
test/lager_slow_backend.erl Voir le fichier

@ -7,28 +7,28 @@
-include("rum.hrl").
-record(state, {
delay :: non_neg_integer()
delay :: non_neg_integer()
}).
init([{delay, Delay}]) ->
{ok, #state{delay=Delay}}.
{ok, #state{delay = Delay}}.
handle_call(get_loglevel, State) ->
{ok, rumUtil:config_to_mask(debug), State};
{ok, rumUtil:config_to_mask(debug), State};
handle_call(_Request, State) ->
{ok, ok, State}.
{ok, ok, State}.
handle_event({log, _Message}, State) ->
timer:sleep(State#state.delay),
{ok, State};
timer:sleep(State#state.delay),
{ok, State};
handle_event(_Event, State) ->
{ok, State}.
{ok, State}.
handle_info(_Info, State) ->
{ok, State}.
{ok, State}.
terminate(_Reason, _State) ->
ok.
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
{ok, State}.

+ 1689
- 1689
test/lager_test_backend.erl
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 145
- 145
test/lager_test_function_transform.erl Voir le fichier

@ -25,12 +25,12 @@
-compile([{nowarn_deprecated_function, [{erlang, now, 0}]}]).
-lager_function_transforms([
{returns_static_emit, on_emit, {lager_test_function_transform, transform_static}},
{returns_dynamic_emit, on_emit, {lager_test_function_transform, transform_dynamic}},
{returns_undefined_emit, on_emit, {not_real_module_fake, fake_not_real_function}},
{returns_static_emit, on_emit, {lager_test_function_transform, transform_static}},
{returns_dynamic_emit, on_emit, {lager_test_function_transform, transform_dynamic}},
{returns_undefined_emit, on_emit, {not_real_module_fake, fake_not_real_function}},
{returns_static_log, on_log, {lager_test_function_transform, transform_static}},
{returns_dynamic_log, on_log, {lager_test_function_transform, transform_dynamic}}
{returns_static_log, on_log, {lager_test_function_transform, transform_static}},
{returns_dynamic_log, on_log, {lager_test_function_transform, transform_dynamic}}
]).
-compile({parse_transform, lager_transform}).
@ -38,161 +38,161 @@
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-export([
transform_static/0,
transform_dynamic/0
transform_static/0,
transform_dynamic/0
]).
-endif.
-ifdef(TEST).
transform_static() ->
static_result.
static_result.
transform_dynamic() ->
case rumUtil:otp_version() >= 18 of
true ->
erlang:monotonic_time();
false ->
erlang:now()
end.
case rumUtil:otp_version() >= 18 of
true ->
erlang:monotonic_time();
false ->
erlang:now()
end.
not_running_test() ->
?assertEqual({error, lager_not_running}, eRum:log(info, self(), "not running")).
?assertEqual({error, lager_not_running}, eRum:log(info, self(), "not running")).
setup() ->
ok = error_logger:tty(false),
ok = rumUtil:safe_application_load(lager),
ok = application:set_env(lager, handlers, [{lager_test_backend, info}]),
ok = application:set_env(lager, error_logger_redirect, false),
ok = application:unset_env(lager, traces),
ok = eRum:start(),
%% There is a race condition between the application start up, lager logging its own
%% start up condition and several tests that count messages or parse the output of
%% tests. When the lager start up message wins the race, it causes these tests
%% which parse output or count message arrivals to fail.
%%
%% We introduce a sleep here to allow `flush' to arrive *after* the start up
%% message has been received and processed.
%%
%% This race condition was first exposed during the work on
%% 4b5260c4524688b545cc12da6baa2dfa4f2afec9 which introduced the lager
%% manager killer PR.
ok = timer:sleep(250),
ok = gen_event:call(lager_event, lager_test_backend, flush).
ok = error_logger:tty(false),
ok = rumUtil:safe_application_load(lager),
ok = application:set_env(lager, handlers, [{lager_test_backend, info}]),
ok = application:set_env(lager, error_logger_redirect, false),
ok = application:unset_env(lager, traces),
ok = eRum:start(),
%% There is a race condition between the application start up, lager logging its own
%% start up condition and several tests that count messages or parse the output of
%% tests. When the lager start up message wins the race, it causes these tests
%% which parse output or count message arrivals to fail.
%%
%% We introduce a sleep here to allow `flush' to arrive *after* the start up
%% message has been received and processed.
%%
%% This race condition was first exposed during the work on
%% 4b5260c4524688b545cc12da6baa2dfa4f2afec9 which introduced the lager
%% manager killer PR.
ok = timer:sleep(250),
ok = gen_event:call(lager_event, lager_test_backend, flush).
cleanup(_) ->
catch ets:delete(lager_config), %% kill the ets config table with fire
ok = application:stop(lager),
ok = application:stop(goldrush),
ok = error_logger:tty(true).
catch ets:delete(lager_config), %% kill the ets config table with fire
ok = application:stop(lager),
ok = application:stop(goldrush),
ok = error_logger:tty(true).
transform_function_test_() ->
{foreach,
fun setup/0,
fun cleanup/1,
[
{"observe that there is nothing up my sleeve",
fun() ->
?assertEqual(undefined, lager_test_backend:pop()),
?assertEqual(0, lager_test_backend:count())
end
},
{"logging works",
fun() ->
eRum:warning("test message"),
?assertEqual(1, lager_test_backend:count()),
{Level, _Time, Message, _Metadata} = lager_test_backend:pop(),
?assertMatch(Level, rumUtil:level_to_num(warning)),
?assertEqual("test message", Message),
ok
end
},
{"Testing calling a function returns the same content on emit",
fun() ->
eRum:warning("static message"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Function = proplists:get_value(returns_static_emit, Metadata),
?assertEqual(transform_static(), Function()),
ok
end
},
{"Testing calling a function which returns content which can change on emit",
fun() ->
eRum:warning("dynamic message"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Function = proplists:get_value(returns_dynamic_emit, Metadata),
?assert(Function() =< Function()),
?assert(Function() =< Function()),
?assert(Function() =< Function()),
?assert(Function() =< Function()),
ok
end
},
{"Testing a undefined function returns undefined on emit",
fun() ->
eRum:warning("Undefined error"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Function = proplists:get_value(returns_undefined_emit, Metadata),
[{module, Module}, {name, Name}|_] = erlang:fun_info(Function),
?assertNot(erlang:function_exported(Module, Name, 0)),
ok
end
},
{"Testing calling a function returns the same content on log",
fun() ->
eRum:warning("static message"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
?assertEqual(transform_static(), proplists:get_value(returns_static_log, Metadata)),
ok
end
},
{"Testing calling a dynamic function on log which returns the same value",
fun() ->
eRum:warning("dynamic message"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Value = proplists:get_value(returns_dynamic_log, Metadata),
?assert(Value =< transform_dynamic()),
?assert(Value =< transform_dynamic()),
?assert(Value =< transform_dynamic()),
?assert(Value =< transform_dynamic()),
?assert(Value =< transform_dynamic()),
ok
end
},
{"Testing differences in results for on_log vs on emit from dynamic function",
fun() ->
eRum:warning("on_log vs on emit"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Value = proplists:get_value(returns_dynamic_log, Metadata),
Function = proplists:get_value(returns_dynamic_emit, Metadata),
FunctionResult = Function(),
?assert(Value =< FunctionResult),
?assert(Value =< Function()),
?assert(FunctionResult =< Function()),
ok
end
},
{"Testing a function provided via metadata",
fun()->
Provided = fun() ->
provided_metadata
end,
eRum:md([{provided, Provided}]),
eRum:warning("Provided metadata"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Function = proplists:get_value(provided, Metadata),
?assertEqual(Provided(), Function()),
ok
end
}
]
}.
{foreach,
fun setup/0,
fun cleanup/1,
[
{"observe that there is nothing up my sleeve",
fun() ->
?assertEqual(undefined, lager_test_backend:pop()),
?assertEqual(0, lager_test_backend:count())
end
},
{"logging works",
fun() ->
eRum:warning("test message"),
?assertEqual(1, lager_test_backend:count()),
{Level, _Time, Message, _Metadata} = lager_test_backend:pop(),
?assertMatch(Level, rumUtil:level_to_num(warning)),
?assertEqual("test message", Message),
ok
end
},
{"Testing calling a function returns the same content on emit",
fun() ->
eRum:warning("static message"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Function = proplists:get_value(returns_static_emit, Metadata),
?assertEqual(transform_static(), Function()),
ok
end
},
{"Testing calling a function which returns content which can change on emit",
fun() ->
eRum:warning("dynamic message"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Function = proplists:get_value(returns_dynamic_emit, Metadata),
?assert(Function() =< Function()),
?assert(Function() =< Function()),
?assert(Function() =< Function()),
?assert(Function() =< Function()),
ok
end
},
{"Testing a undefined function returns undefined on emit",
fun() ->
eRum:warning("Undefined error"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Function = proplists:get_value(returns_undefined_emit, Metadata),
[{module, Module}, {name, Name} | _] = erlang:fun_info(Function),
?assertNot(erlang:function_exported(Module, Name, 0)),
ok
end
},
{"Testing calling a function returns the same content on log",
fun() ->
eRum:warning("static message"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
?assertEqual(transform_static(), proplists:get_value(returns_static_log, Metadata)),
ok
end
},
{"Testing calling a dynamic function on log which returns the same value",
fun() ->
eRum:warning("dynamic message"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Value = proplists:get_value(returns_dynamic_log, Metadata),
?assert(Value =< transform_dynamic()),
?assert(Value =< transform_dynamic()),
?assert(Value =< transform_dynamic()),
?assert(Value =< transform_dynamic()),
?assert(Value =< transform_dynamic()),
ok
end
},
{"Testing differences in results for on_log vs on emit from dynamic function",
fun() ->
eRum:warning("on_log vs on emit"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Value = proplists:get_value(returns_dynamic_log, Metadata),
Function = proplists:get_value(returns_dynamic_emit, Metadata),
FunctionResult = Function(),
?assert(Value =< FunctionResult),
?assert(Value =< Function()),
?assert(FunctionResult =< Function()),
ok
end
},
{"Testing a function provided via metadata",
fun() ->
Provided = fun() ->
provided_metadata
end,
eRum:md([{provided, Provided}]),
eRum:warning("Provided metadata"),
?assertEqual(1, lager_test_backend:count()),
{_Level, _Time, _Message, Metadata} = lager_test_backend:pop(),
Function = proplists:get_value(provided, Metadata),
?assertEqual(Provided(), Function()),
ok
end
}
]
}.
-endif.

+ 68
- 68
test/lager_trace_test.erl Voir le fichier

@ -15,89 +15,89 @@
-define(FNAME, "test/test1.log").
trace_test_() ->
{timeout,
10,
{foreach,
{timeout,
10,
{foreach,
fun() ->
file:write_file(?FNAME, ""),
error_logger:tty(false),
application:load(lager),
application:set_env(lager, log_root, "test"),
application:set_env(lager, handlers,
[{lager_file_backend,
[{file, "test1.log"},
{level, none},
{formatter, lager_default_formatter},
{formatter_config, [message, "\n"]}
]}]),
application:set_env(lager, traces,
[{{lager_file_backend, "test1.log"},
[{tag, mytag}], info}]),
application:set_env(lager, error_logger_redirect, false),
application:set_env(lager, async_threshold, undefined),
eRum:start()
file:write_file(?FNAME, ""),
error_logger:tty(false),
application:load(lager),
application:set_env(lager, log_root, "test"),
application:set_env(lager, handlers,
[{lager_file_backend,
[{file, "test1.log"},
{level, none},
{formatter, lager_default_formatter},
{formatter_config, [message, "\n"]}
]}]),
application:set_env(lager, traces,
[{{lager_file_backend, "test1.log"},
[{tag, mytag}], info}]),
application:set_env(lager, error_logger_redirect, false),
application:set_env(lager, async_threshold, undefined),
eRum:start()
end,
fun(_) ->
file:delete(?FNAME),
application:stop(lager),
application:stop(goldrush),
application:unset_env(lager, log_root),
application:unset_env(lager, handlers),
application:unset_env(lager, traces),
application:unset_env(lager, error_logger_redirect),
application:unset_env(lager, async_threshold),
error_logger:tty(true)
file:delete(?FNAME),
application:stop(lager),
application:stop(goldrush),
application:unset_env(lager, log_root),
application:unset_env(lager, handlers),
application:unset_env(lager, traces),
application:unset_env(lager, error_logger_redirect),
application:unset_env(lager, async_threshold),
error_logger:tty(true)
end,
[{"Trace combined with log_root",
fun() ->
eRum:info([{tag, mytag}], "Test message"),
fun() ->
eRum:info([{tag, mytag}], "Test message"),
% Wait until we have the expected log entry in the log file.
case wait_until(fun() ->
count_lines(?FNAME) >= 1
end, ?FIRST_LOG_ENTRY_TIMEOUT) of
ok ->
ok;
{error, timeout} ->
throw({file_empty, file:read_file(?FNAME)})
end,
% Wait until we have the expected log entry in the log file.
case wait_until(fun() ->
count_lines(?FNAME) >= 1
end, ?FIRST_LOG_ENTRY_TIMEOUT) of
ok ->
ok;
{error, timeout} ->
throw({file_empty, file:read_file(?FNAME)})
end,
% Let's wait a little to see that we don't get a duplicate log
% entry.
case wait_until(fun() ->
count_lines(?FNAME) >= 2
end, ?SECOND_LOG_ENTRY_TIMEOUT) of
ok ->
throw({too_many_entries, file:read_file(?FNAME)});
{error, timeout} ->
ok
end
end}
]}}.
% Let's wait a little to see that we don't get a duplicate log
% entry.
case wait_until(fun() ->
count_lines(?FNAME) >= 2
end, ?SECOND_LOG_ENTRY_TIMEOUT) of
ok ->
throw({too_many_entries, file:read_file(?FNAME)});
{error, timeout} ->
ok
end
end}
]}}.
% Wait until Fun() returns true.
wait_until(Fun, Timeout) ->
wait_until(Fun, Timeout, {8, 13}).
wait_until(Fun, Timeout, {8, 13}).
wait_until(_Fun, Timeout, {T1, _}) when T1 > Timeout ->
{error, timeout};
{error, timeout};
wait_until(Fun, Timeout, {T1, T2}) ->
case Fun() of
true ->
ok;
false ->
timer:sleep(T1),
wait_until(Fun, Timeout, {T2, T1 + T2})
end.
case Fun() of
true ->
ok;
false ->
timer:sleep(T1),
wait_until(Fun, Timeout, {T2, T1 + T2})
end.
% Return the number of lines in a file. Return 0 for a non-existent file.
count_lines(Filename) ->
case file:read_file(Filename) of
{ok, Content} ->
Lines = binary:split(Content, <<"\n">>, [global, trim]),
length(Lines);
{error, _} ->
0
end.
case file:read_file(Filename) of
{ok, Content} ->
Lines = binary:split(Content, <<"\n">>, [global, trim]),
length(Lines);
{error, _} ->
0
end.
-endif.

+ 31
- 31
test/pr_composite_test.erl Voir le fichier

@ -3,45 +3,45 @@
-compile([{parse_transform, lager_transform}]).
-record(a, {field1 :: term(), field2 :: term()}).
-record(b, {field1 :: term() , field2 :: term()}).
-record(b, {field1 :: term(), field2 :: term()}).
-include_lib("eunit/include/eunit.hrl").
nested_record_test() ->
A = #a{field1 = x, field2 = y},
B = #b{field1 = A, field2 = {}},
Pr_B = eRum:pr(B, ?MODULE),
?assertEqual({'$lager_record', b,
[{field1, {'$lager_record', a,
[{field1, x},{field2, y}]}},
{field2, {}}]},
Pr_B).
A = #a{field1 = x, field2 = y},
B = #b{field1 = A, field2 = {}},
Pr_B = eRum:pr(B, ?MODULE),
?assertEqual({'$lager_record', b,
[{field1, {'$lager_record', a,
[{field1, x}, {field2, y}]}},
{field2, {}}]},
Pr_B).
list_field_test() ->
As = [#a{field1 = 1, field2 = a2},
#a{field1 = 2, field2 = a2}],
B = #b{field1 = As, field2 = b2},
Pr_B = eRum:pr(B, ?MODULE),
?assertEqual({'$lager_record', b,
[{field1, [{'$lager_record', a,
[{field1, 1},{field2, a2}]},
{'$lager_record', a,
[{field1, 2},{field2, a2}]}]},
{field2, b2}]},
Pr_B).
As = [#a{field1 = 1, field2 = a2},
#a{field1 = 2, field2 = a2}],
B = #b{field1 = As, field2 = b2},
Pr_B = eRum:pr(B, ?MODULE),
?assertEqual({'$lager_record', b,
[{field1, [{'$lager_record', a,
[{field1, 1}, {field2, a2}]},
{'$lager_record', a,
[{field1, 2}, {field2, a2}]}]},
{field2, b2}]},
Pr_B).
list_of_records_test() ->
As = [#a{field1 = 1, field2 = a2},
#a{field1 = 2, field2 = a2}],
Pr_As = eRum:pr(As, ?MODULE),
?assertEqual([{'$lager_record', a, [{field1, 1},{field2, a2}]},
{'$lager_record', a, [{field1, 2},{field2, a2}]}],
Pr_As).
As = [#a{field1 = 1, field2 = a2},
#a{field1 = 2, field2 = a2}],
Pr_As = eRum:pr(As, ?MODULE),
?assertEqual([{'$lager_record', a, [{field1, 1}, {field2, a2}]},
{'$lager_record', a, [{field1, 2}, {field2, a2}]}],
Pr_As).
improper_list_test() ->
A = #a{field1 = [1|2], field2 = a2},
Pr_A = eRum:pr(A, ?MODULE),
?assertEqual({'$lager_record',a,
[{field1,[1|2]},{field2,a2}]},
Pr_A).
A = #a{field1 = [1 | 2], field2 = a2},
Pr_A = eRum:pr(A, ?MODULE),
?assertEqual({'$lager_record', a,
[{field1, [1 | 2]}, {field2, a2}]},
Pr_A).

+ 36
- 36
test/pr_stacktrace_test.erl Voir le fichier

@ -13,51 +13,51 @@
-include_lib("eunit/include/eunit.hrl").
make_throw() ->
throw({test, exception}).
throw({test, exception}).
bad_arity() ->
lists:concat([], []).
lists:concat([], []).
bad_arg() ->
integer_to_list(1.0).
integer_to_list(1.0).
pr_stacktrace_throw_test() ->
Got = try
make_throw()
catch
?EXCEPTION(Class, Reason, Stacktrace) ->
eRum:pr_stacktrace(?GET_STACK(Stacktrace), {Class, Reason})
end,
Want = "pr_stacktrace_test:pr_stacktrace_throw_test/0 line 26\n pr_stacktrace_test:make_throw/0 line 16\nthrow:{test,exception}",
?assertNotEqual(nomatch, string:find(Got, Want)).
Got = try
make_throw()
catch
?EXCEPTION(Class, Reason, Stacktrace) ->
eRum:pr_stacktrace(?GET_STACK(Stacktrace), {Class, Reason})
end,
Want = "pr_stacktrace_test:pr_stacktrace_throw_test/0 line 26\n pr_stacktrace_test:make_throw/0 line 16\nthrow:{test,exception}",
?assertNotEqual(nomatch, string:find(Got, Want)).
pr_stacktrace_bad_arg_test() ->
Got = try
bad_arg()
catch
?EXCEPTION(Class, Reason, Stacktrace) ->
eRum:pr_stacktrace(?GET_STACK(Stacktrace), {Class, Reason})
end,
Want = "pr_stacktrace_test:pr_stacktrace_bad_arg_test/0 line 36\n pr_stacktrace_test:bad_arg/0 line 22\nerror:badarg",
?assertNotEqual(nomatch, string:find(Got, Want)).
Got = try
bad_arg()
catch
?EXCEPTION(Class, Reason, Stacktrace) ->
eRum:pr_stacktrace(?GET_STACK(Stacktrace), {Class, Reason})
end,
Want = "pr_stacktrace_test:pr_stacktrace_bad_arg_test/0 line 36\n pr_stacktrace_test:bad_arg/0 line 22\nerror:badarg",
?assertNotEqual(nomatch, string:find(Got, Want)).
pr_stacktrace_bad_arity_test() ->
Got = try
bad_arity()
catch
?EXCEPTION(Class, Reason, Stacktrace) ->
eRum:pr_stacktrace(?GET_STACK(Stacktrace), {Class, Reason})
end,
Want = "pr_stacktrace_test:pr_stacktrace_bad_arity_test/0 line 46\n lists:concat([], [])\nerror:undef",
?assertNotEqual(nomatch, string:find(Got, Want)).
Got = try
bad_arity()
catch
?EXCEPTION(Class, Reason, Stacktrace) ->
eRum:pr_stacktrace(?GET_STACK(Stacktrace), {Class, Reason})
end,
Want = "pr_stacktrace_test:pr_stacktrace_bad_arity_test/0 line 46\n lists:concat([], [])\nerror:undef",
?assertNotEqual(nomatch, string:find(Got, Want)).
pr_stacktrace_no_reverse_test() ->
application:set_env(lager, reverse_pretty_stacktrace, false),
Got = try
bad_arity()
catch
?EXCEPTION(Class, Reason, Stacktrace) ->
eRum:pr_stacktrace(?GET_STACK(Stacktrace), {Class, Reason})
end,
Want = "error:undef\n lists:concat([], [])\n pr_stacktrace_test:pr_stacktrace_bad_arity_test/0 line 57",
?assertEqual(nomatch, string:find(Got, Want)).
application:set_env(lager, reverse_pretty_stacktrace, false),
Got = try
bad_arity()
catch
?EXCEPTION(Class, Reason, Stacktrace) ->
eRum:pr_stacktrace(?GET_STACK(Stacktrace), {Class, Reason})
end,
Want = "error:undef\n lists:concat([], [])\n pr_stacktrace_test:pr_stacktrace_bad_arity_test/0 line 57",
?assertEqual(nomatch, string:find(Got, Want)).

+ 25
- 25
test/special_process.erl Voir le fichier

@ -2,35 +2,35 @@
-export([start/0, init/1]).
start() ->
proc_lib:start_link(?MODULE, init, [self()]).
proc_lib:start_link(?MODULE, init, [self()]).
init(Parent) ->
proc_lib:init_ack(Parent, {ok, self()}),
loop().
proc_lib:init_ack(Parent, {ok, self()}),
loop().
loop() ->
receive
function_clause ->
foo(bar),
loop();
exit ->
exit(byebye),
loop();
error ->
erlang:error(mybad),
loop();
{case_clause, X} ->
case X of
notgonnamatch ->
ok;
notthiseither ->
error
end,
loop();
_ ->
loop()
end.
receive
function_clause ->
foo(bar),
loop();
exit ->
exit(byebye),
loop();
error ->
erlang:error(mybad),
loop();
{case_clause, X} ->
case X of
notgonnamatch ->
ok;
notthiseither ->
error
end,
loop();
_ ->
loop()
end.
foo(baz) ->
ok.
ok.

+ 33
- 33
test/sync_error_logger.erl Voir le fichier

@ -24,66 +24,66 @@
%% convienience, via the process dictionary value `warning_map'.
-export([
info_msg/1, info_msg/2,
warning_msg/1, warning_msg/2,
error_msg/1,error_msg/2
]).
info_msg/1, info_msg/2,
warning_msg/1, warning_msg/2,
error_msg/1, error_msg/2
]).
-export([
info_report/1, info_report/2,
warning_report/1, warning_report/2,
error_report/1, error_report/2
]).
info_report/1, info_report/2,
warning_report/1, warning_report/2,
error_report/1, error_report/2
]).
info_msg(Format) ->
info_msg(Format, []).
info_msg(Format, []).
info_msg(Format, Args) ->
gen_event:sync_notify(error_logger, {info_msg, group_leader(), {self(), Format, Args}}).
gen_event:sync_notify(error_logger, {info_msg, group_leader(), {self(), Format, Args}}).
warning_msg(Format) ->
warning_msg(Format, []).
warning_msg(Format, []).
warning_msg(Format, Args) ->
gen_event:sync_notify(error_logger, {warning_msg_tag(), group_leader(), {self(), Format, Args}}).
gen_event:sync_notify(error_logger, {warning_msg_tag(), group_leader(), {self(), Format, Args}}).
error_msg(Format) ->
error_msg(Format, []).
error_msg(Format, []).
error_msg(Format, Args) ->
gen_event:sync_notify(error_logger, {error, group_leader(), {self(), Format, Args}}).
gen_event:sync_notify(error_logger, {error, group_leader(), {self(), Format, Args}}).
info_report(Report) ->
info_report(std_info, Report).
info_report(std_info, Report).
info_report(Type, Report) ->
gen_event:sync_notify(error_logger, {info_report, group_leader(), {self(), Type, Report}}).
gen_event:sync_notify(error_logger, {info_report, group_leader(), {self(), Type, Report}}).
warning_report(Report) ->
warning_report(std_warning, Report).
warning_report(std_warning, Report).
warning_report(Type, Report) ->
{Tag, NType} = warning_report_tag(Type),
gen_event:sync_notify(error_logger, {Tag, group_leader(), {self(), NType, Report}}).
{Tag, NType} = warning_report_tag(Type),
gen_event:sync_notify(error_logger, {Tag, group_leader(), {self(), NType, Report}}).
error_report(Report) ->
error_report(std_error, Report).
error_report(std_error, Report).
error_report(Type, Report) ->
gen_event:sync_notify(error_logger, {error_report, group_leader(), {self(), Type, Report}}).
gen_event:sync_notify(error_logger, {error_report, group_leader(), {self(), Type, Report}}).
warning_msg_tag() ->
case get(warning_map) of
warning -> warning_msg;
info -> info_msg;
_ -> error
end.
case get(warning_map) of
warning -> warning_msg;
info -> info_msg;
_ -> error
end.
warning_report_tag(Type) ->
case {get(warning_map), Type == std_warning} of
{warning, _} -> {warning_report, Type};
{info, true} -> {info_report, std_info};
{info, false} -> {info_report, Type};
{_, true} -> {error_report, std_error};
{_, false} -> {error_report, Type}
end.
case {get(warning_map), Type == std_warning} of
{warning, _} -> {warning_report, Type};
{info, true} -> {info_report, std_info};
{info, false} -> {info_report, Type};
{_, true} -> {error_report, std_error};
{_, false} -> {error_report, Type}
end.

+ 141
- 141
test/trunc_io_eqc.erl Voir le fichier

@ -29,185 +29,185 @@
-include_lib("eunit/include/eunit.hrl").
-define(QC_OUT(P),
eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)).
eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)).
%%====================================================================
%% eunit test
%%====================================================================
eqc_test_() ->
{timeout, 60,
{spawn,
[
{timeout, 30, ?_assertEqual(true, eqc:quickcheck(eqc:testing_time(14, ?QC_OUT(prop_format()))))},
{timeout, 30, ?_assertEqual(true, eqc:quickcheck(eqc:testing_time(14, ?QC_OUT(prop_equivalence()))))}
]
}}.
{timeout, 60,
{spawn,
[
{timeout, 30, ?_assertEqual(true, eqc:quickcheck(eqc:testing_time(14, ?QC_OUT(prop_format()))))},
{timeout, 30, ?_assertEqual(true, eqc:quickcheck(eqc:testing_time(14, ?QC_OUT(prop_equivalence()))))}
]
}}.
%%====================================================================
%% Shell helpers
%%====================================================================
test() ->
test(100).
test(100).
test(N) ->
quickcheck(numtests(N, prop_format())).
quickcheck(numtests(N, prop_format())).
check() ->
check(prop_format(), current_counterexample()).
check(prop_format(), current_counterexample()).
%%====================================================================
%% Generators
%%====================================================================
gen_fmt_args() ->
list(oneof([gen_print_str(),
"~~",
{"~10000000.p", gen_any(5)},
{"~w", gen_any(5)},
{"~s", oneof([gen_print_str(), gen_atom(), gen_quoted_atom(), gen_print_bin(), gen_iolist(5)])},
{"~1000000.P", gen_any(5), 4},
{"~W", gen_any(5), 4},
{"~i", gen_any(5)},
{"~B", nat()},
{"~b", nat()},
{"~X", nat(), "0x"},
{"~x", nat(), "0x"},
{"~.10#", nat()},
{"~.10+", nat()},
{"~.36B", nat()},
{"~1000000.62P", gen_any(5), 4},
{"~c", gen_char()},
{"~tc", gen_char()},
{"~f", real()},
{"~10.f", real()},
{"~g", real()},
{"~10.g", real()},
{"~e", real()},
{"~10.e", real()}
])).
list(oneof([gen_print_str(),
"~~",
{"~10000000.p", gen_any(5)},
{"~w", gen_any(5)},
{"~s", oneof([gen_print_str(), gen_atom(), gen_quoted_atom(), gen_print_bin(), gen_iolist(5)])},
{"~1000000.P", gen_any(5), 4},
{"~W", gen_any(5), 4},
{"~i", gen_any(5)},
{"~B", nat()},
{"~b", nat()},
{"~X", nat(), "0x"},
{"~x", nat(), "0x"},
{"~.10#", nat()},
{"~.10+", nat()},
{"~.36B", nat()},
{"~1000000.62P", gen_any(5), 4},
{"~c", gen_char()},
{"~tc", gen_char()},
{"~f", real()},
{"~10.f", real()},
{"~g", real()},
{"~10.g", real()},
{"~e", real()},
{"~10.e", real()}
])).
%% Generates a printable string
gen_print_str() ->
?LET(Xs, list(char()), [X || X <- Xs, io_lib:printable_list([X]), X /= $~, X < 256]).
?LET(Xs, list(char()), [X || X <- Xs, io_lib:printable_list([X]), X /= $~, X < 256]).
gen_print_bin() ->
?LET(Xs, gen_print_str(), list_to_binary(Xs)).
?LET(Xs, gen_print_str(), list_to_binary(Xs)).
gen_any(MaxDepth) ->
oneof([largeint(),
gen_atom(),
gen_quoted_atom(),
nat(),
%real(),
binary(),
gen_bitstring(),
gen_pid(),
gen_port(),
gen_ref(),
gen_fun()] ++
[?LAZY(list(gen_any(MaxDepth - 1))) || MaxDepth /= 0] ++
[?LAZY(gen_tuple(gen_any(MaxDepth - 1))) || MaxDepth /= 0]).
oneof([largeint(),
gen_atom(),
gen_quoted_atom(),
nat(),
%real(),
binary(),
gen_bitstring(),
gen_pid(),
gen_port(),
gen_ref(),
gen_fun()] ++
[?LAZY(list(gen_any(MaxDepth - 1))) || MaxDepth /= 0] ++
[?LAZY(gen_tuple(gen_any(MaxDepth - 1))) || MaxDepth /= 0]).
gen_iolist(0) ->
[];
[];
gen_iolist(Depth) ->
list(oneof([gen_char(), gen_print_str(), gen_print_bin(), gen_iolist(Depth-1)])).
list(oneof([gen_char(), gen_print_str(), gen_print_bin(), gen_iolist(Depth - 1)])).
gen_atom() ->
elements([abc, def, ghi]).
elements([abc, def, ghi]).
gen_quoted_atom() ->
elements(['abc@bar', '@bar', '10gen']).
elements(['abc@bar', '@bar', '10gen']).
gen_bitstring() ->
?LET(XS, binary(), <<XS/binary, 1:7>>).
?LET(XS, binary(), <<XS/binary, 1:7>>).
gen_tuple(Gen) ->
?LET(Xs, list(Gen), list_to_tuple(Xs)).
?LET(Xs, list(Gen), list_to_tuple(Xs)).
gen_max_len() -> %% Generate length from 3 to whatever. Needs space for ... in output
?LET(Xs, int(), 3 + abs(Xs)).
?LET(Xs, int(), 3 + abs(Xs)).
gen_pid() ->
?LAZY(spawn(fun() -> ok end)).
?LAZY(spawn(fun() -> ok end)).
gen_port() ->
?LAZY(begin
Port = erlang:open_port({spawn, "true"}, []),
catch(erlang:port_close(Port)),
Port
end).
?LAZY(begin
Port = erlang:open_port({spawn, "true"}, []),
catch (erlang:port_close(Port)),
Port
end).
gen_ref() ->
?LAZY(make_ref()).
?LAZY(make_ref()).
gen_fun() ->
?LAZY(fun() -> ok end).
?LAZY(fun() -> ok end).
gen_char() ->
oneof(lists:seq($A, $z)).
oneof(lists:seq($A, $z)).
%%====================================================================
%% Property
%%====================================================================
%% Checks that trunc_io:format produces output less than or equal to MaxLen
prop_format() ->
?FORALL({FmtArgs, MaxLen}, {gen_fmt_args(), gen_max_len()},
begin
%% Because trunc_io will print '...' when its running out of
%% space, even if the remaining space is less than 3, it
%% doesn't *exactly* stick to the specified limit.
%% Also, since we don't truncate terms not printed with
%% ~p/~P/~w/~W/~s, we also need to calculate the wiggle room
%% for those. Hence the fudge factor calculated below.
FudgeLen = calculate_fudge(FmtArgs, 50),
{FmtStr, Args} = build_fmt_args(FmtArgs),
try
Str = lists:flatten(lager_trunc_io:format(FmtStr, Args, MaxLen)),
?WHENFAIL(begin
io:format(user, "FmtStr: ~p\n", [FmtStr]),
io:format(user, "Args: ~p\n", [Args]),
io:format(user, "FudgeLen: ~p\n", [FudgeLen]),
io:format(user, "MaxLen: ~p\n", [MaxLen]),
io:format(user, "ActLen: ~p\n", [length(Str)]),
io:format(user, "Str: ~p\n", [Str])
end,
%% Make sure the result is a printable list
%% and if the format string is less than the length,
%% the result string is less than the length.
conjunction([{printable, Str == "" orelse
io_lib:printable_list(Str)},
{length, length(FmtStr) > MaxLen orelse
length(Str) =< MaxLen + FudgeLen}]))
catch
_:Err ->
io:format(user, "\nException: ~p\n", [Err]),
io:format(user, "FmtStr: ~p\n", [FmtStr]),
io:format(user, "Args: ~p\n", [Args]),
false
end
end).
?FORALL({FmtArgs, MaxLen}, {gen_fmt_args(), gen_max_len()},
begin
%% Because trunc_io will print '...' when its running out of
%% space, even if the remaining space is less than 3, it
%% doesn't *exactly* stick to the specified limit.
%% Also, since we don't truncate terms not printed with
%% ~p/~P/~w/~W/~s, we also need to calculate the wiggle room
%% for those. Hence the fudge factor calculated below.
FudgeLen = calculate_fudge(FmtArgs, 50),
{FmtStr, Args} = build_fmt_args(FmtArgs),
try
Str = lists:flatten(lager_trunc_io:format(FmtStr, Args, MaxLen)),
?WHENFAIL(begin
io:format(user, "FmtStr: ~p\n", [FmtStr]),
io:format(user, "Args: ~p\n", [Args]),
io:format(user, "FudgeLen: ~p\n", [FudgeLen]),
io:format(user, "MaxLen: ~p\n", [MaxLen]),
io:format(user, "ActLen: ~p\n", [length(Str)]),
io:format(user, "Str: ~p\n", [Str])
end,
%% Make sure the result is a printable list
%% and if the format string is less than the length,
%% the result string is less than the length.
conjunction([{printable, Str == "" orelse
io_lib:printable_list(Str)},
{length, length(FmtStr) > MaxLen orelse
length(Str) =< MaxLen + FudgeLen}]))
catch
_:Err ->
io:format(user, "\nException: ~p\n", [Err]),
io:format(user, "FmtStr: ~p\n", [FmtStr]),
io:format(user, "Args: ~p\n", [Args]),
false
end
end).
%% Checks for equivalent formatting to io_lib
prop_equivalence() ->
?FORALL(FmtArgs, gen_fmt_args(),
begin
{FmtStr, Args} = build_fmt_args(FmtArgs),
Expected = lists:flatten(io_lib:format(FmtStr, Args)),
Actual = lists:flatten(lager_trunc_io:format(FmtStr, Args, 10485760)),
?WHENFAIL(begin
io:format(user, "FmtStr: ~p\n", [FmtStr]),
io:format(user, "Args: ~p\n", [Args]),
io:format(user, "Expected: ~p\n", [Expected]),
io:format(user, "Actual: ~p\n", [Actual])
end,
Expected == Actual)
end).
?FORALL(FmtArgs, gen_fmt_args(),
begin
{FmtStr, Args} = build_fmt_args(FmtArgs),
Expected = lists:flatten(io_lib:format(FmtStr, Args)),
Actual = lists:flatten(lager_trunc_io:format(FmtStr, Args, 10485760)),
?WHENFAIL(begin
io:format(user, "FmtStr: ~p\n", [FmtStr]),
io:format(user, "Args: ~p\n", [Args]),
io:format(user, "Expected: ~p\n", [Expected]),
io:format(user, "Actual: ~p\n", [Actual])
end,
Expected == Actual)
end).
%%====================================================================
@ -216,29 +216,29 @@ prop_equivalence() ->
%% Build a tuple of {Fmt, Args} from a gen_fmt_args() return
build_fmt_args(FmtArgs) ->
F = fun({Fmt, Arg}, {FmtStr0, Args0}) ->
{FmtStr0 ++ Fmt, Args0 ++ [Arg]};
({Fmt, Arg1, Arg2}, {FmtStr0, Args0}) ->
{FmtStr0 ++ Fmt, Args0 ++ [Arg1, Arg2]};
(Str, {FmtStr0, Args0}) ->
{FmtStr0 ++ Str, Args0}
end,
lists:foldl(F, {"", []}, FmtArgs).
F = fun({Fmt, Arg}, {FmtStr0, Args0}) ->
{FmtStr0 ++ Fmt, Args0 ++ [Arg]};
({Fmt, Arg1, Arg2}, {FmtStr0, Args0}) ->
{FmtStr0 ++ Fmt, Args0 ++ [Arg1, Arg2]};
(Str, {FmtStr0, Args0}) ->
{FmtStr0 ++ Str, Args0}
end,
lists:foldl(F, {"", []}, FmtArgs).
calculate_fudge([], Acc) ->
Acc;
calculate_fudge([{"~62P", _Arg, _Depth}|T], Acc) ->
calculate_fudge(T, Acc+62);
calculate_fudge([{Fmt, Arg}|T], Acc) when
Fmt == "~f"; Fmt == "~10.f";
Fmt == "~g"; Fmt == "~10.g";
Fmt == "~e"; Fmt == "~10.e";
Fmt == "~x"; Fmt == "~X";
Fmt == "~B"; Fmt == "~b"; Fmt == "~36B";
Fmt == "~.10#"; Fmt == "~10+" ->
calculate_fudge(T, Acc + length(lists:flatten(io_lib:format(Fmt, [Arg]))));
calculate_fudge([_|T], Acc) ->
calculate_fudge(T, Acc).
Acc;
calculate_fudge([{"~62P", _Arg, _Depth} | T], Acc) ->
calculate_fudge(T, Acc + 62);
calculate_fudge([{Fmt, Arg} | T], Acc) when
Fmt == "~f"; Fmt == "~10.f";
Fmt == "~g"; Fmt == "~10.g";
Fmt == "~e"; Fmt == "~10.e";
Fmt == "~x"; Fmt == "~X";
Fmt == "~B"; Fmt == "~b"; Fmt == "~36B";
Fmt == "~.10#"; Fmt == "~10+" ->
calculate_fudge(T, Acc + length(lists:flatten(io_lib:format(Fmt, [Arg]))));
calculate_fudge([_ | T], Acc) ->
calculate_fudge(T, Acc).
-endif. % (EQC).
-endif. % (TEST).

+ 21
- 21
test/zzzz_gh280_crash.erl Voir le fichier

@ -9,27 +9,27 @@
-include_lib("eunit/include/eunit.hrl").
gh280_crash_test() ->
{timeout, 30, fun() -> gh280_impl() end}.
{timeout, 30, fun() -> gh280_impl() end}.
gh280_impl() ->
application:stop(lager),
application:stop(goldrush),
application:stop(lager),
application:stop(goldrush),
error_logger:tty(false),
%% see https://github.com/erlang/otp/blob/maint/lib/stdlib/src/log_mf_h.erl#L81
%% for an explanation of the init arguments to log_mf_h
ok = gen_event:add_sup_handler(error_logger, log_mf_h, log_mf_h:init("/tmp", 10000, 5)),
eRum:start(),
Result = receive
{gen_event_EXIT,log_mf_h,normal} ->
true;
{gen_event_EXIT,Handler,Reason} ->
{Handler,Reason};
X ->
X
after 10000 ->
timeout
end,
?assert(Result),
application:stop(lager),
application:stop(goldrush).
error_logger:tty(false),
%% see https://github.com/erlang/otp/blob/maint/lib/stdlib/src/log_mf_h.erl#L81
%% for an explanation of the init arguments to log_mf_h
ok = gen_event:add_sup_handler(error_logger, log_mf_h, log_mf_h:init("/tmp", 10000, 5)),
eRum:start(),
Result = receive
{gen_event_EXIT, log_mf_h, normal} ->
true;
{gen_event_EXIT, Handler, Reason} ->
{Handler, Reason};
X ->
X
after 10000 ->
timeout
end,
?assert(Result),
application:stop(lager),
application:stop(goldrush).

Chargement…
Annuler
Enregistrer