diff --git a/README.md b/README.md index d6535a1..25803d0 100644 --- a/README.md +++ b/README.md @@ -1153,6 +1153,69 @@ Example Usage: :lager.warning('Some message with a term: ~p', [term]) ``` +Integration with OTP's Logger +----------------------------- + +Now that OTP includes a modern log event pipeline, the Lager project has decided +to work on winding down Lager development in favor of unifying the Erlang +ecosystem behind Logger. To that end, Lager now supports being configured to use +its parse transform to rewrite lager calls into logger calls. To enable this +mode, the following changes are required: + +In sys.config: +``` +[ + {kernel, + [{logger, + [{handler, default, logger_std_h, + #{formatter => {lager_logger_formatter, #{report_cb => fun lager_logger_formatter:report_cb/1}}}}, + ]}]}, +... +{lager, [ + {lager_use_logger, true}, + ... +]} +] +``` + +The logger stanza changes are not needed if you wish to use logger's default +formatter. + +In your applications top level rebar.config (probably requires rebar3): + +``` +{overrides, [{add, [{erl_opts, [{lager_use_logger, true}]}]}]}. +``` + +This will force the parse transform configuration to apply to all the +dependencies as well. Make sure you've defined the lager version in your +toplevel application's rebar.config and that the version is high enough to +support these options. A toplevel dependency will override any lager +dependencies in any of your application's dependencies and thus ensure the parse +transform is the right version. + +To generate a logger configuration from your lager configuration you can do: + +``` +lager:generate_logger_configuration() +``` + +If you wish to simply use your existing lager configuration as is, and have +lager configure logger you can, in your sys.config in the lager stanza: + +``` +{lager, [ + {configure_logger, true}, + ... +]} +``` + +Alternatively you can use: + +``` +lager:configure_logger() +``` + 3.x Changelog ------------- diff --git a/src/lager.erl b/src/lager.erl index 275bebc..c50b513 100644 --- a/src/lager.erl +++ b/src/lager.erl @@ -40,7 +40,8 @@ get_loglevel/1, get_loglevel/2, set_loglevel/2, set_loglevel/3, set_loglevel/4, get_loglevels/1, update_loglevel_config/1, posix_error/1, set_loghwm/2, set_loghwm/3, set_loghwm/4, safe_format/3, safe_format_chop/3, unsafe_format/2, dispatch_log/5, dispatch_log/7, dispatch_log/9, - do_log/9, do_log/10, do_log_unsafe/10, pr/2, pr/3, pr_stacktrace/1, pr_stacktrace/2]). + do_log/9, do_log/10, do_log_unsafe/10, pr/2, pr/3, pr_stacktrace/1, pr_stacktrace/2, + generate_logger_config/0, configure_logger/0]). -type log_level() :: none | debug | info | notice | warning | error | critical | alert | emergency. -type log_level_number() :: 0..7. @@ -695,6 +696,65 @@ rotate_handler(Handler) -> rotate_handler(Handler, Sink) -> gen_event:call(Sink, Handler, rotate, ?ROTATE_TIMEOUT). +generate_logger_config() -> + Handlers = application:get_env(lager, handlers, lager_app:default_handlers()), + {Level, NewHandlers} = generate_logger_handlers(Handlers, {notice, []}), + {kernel, [{logger_level, Level}, {logger, NewHandlers}]}. + +configure_logger() -> + Handlers = application:get_env(lager, handlers, lager_app:default_handlers()), + WhitelistedLoggerHandlers = application:get_env(lager, whitelisted_logger_handlers, []), + [ ok = logger:remove_handler(Id) || #{id := Id} <- logger:get_handler_config(), not lists:member(Id, WhitelistedLoggerHandlers) ], + {Level, NewHandlers} = generate_logger_handlers(Handlers, {notice, []}), + logger:set_primary_config(maps:merge(logger:get_primary_config(), #{level => Level})), + [ ok = logger:add_handler(HandlerId, HandlerModule, HandlerConfig) || {handler, HandlerId, HandlerModule, HandlerConfig} <- NewHandlers ], + ok. + +generate_logger_handlers([], Acc) -> + Acc; +generate_logger_handlers([{lager_console_backend, Config}|Tail], {CurrentLevel, Acc}) -> + Level = proplists:get_value(level, Config, info), + Formatter = proplists:get_value(formatter, Config, lager_default_formatter), + FormatterConfig = proplists:get_value(formatter_config, Config, []), + Handler = {handler, console, logger_std_h, #{level => Level, formatter => + {lager_logger_formatter, #{report_cb => fun lager_logger_formatter:report_cb/1, + formatter => Formatter, + formatter_config => FormatterConfig}}}}, + NewLevel = case lager_util:level_to_num(Level) > lager_util:level_to_num(CurrentLevel) of + true -> + Level; + false -> + CurrentLevel + end, + generate_logger_handlers(Tail, {NewLevel, [Handler|Acc]}); +generate_logger_handlers([{lager_file_backend, Config}|Tail], {CurrentLevel, Acc}) -> + Level = proplists:get_value(level, Config, info), + File = proplists:get_value(file, Config), + LogRoot = application:get_env(lager, log_root, ""), + Size = proplists:get_value(size, Config, 10485760), + Count = proplists:get_value(count, Config, 5), + Formatter = proplists:get_value(formatter, Config, lager_default_formatter), + FormatterConfig = proplists:get_value(formatter_config, Config, []), + %% the standard log handler has a file mode with size based rotation support that is much saner than + %% disk_log's, so use that here + Handler = {handler, list_to_atom(File), logger_std_h, #{level => Level, + config => #{type => file, + file => filename:join(LogRoot, File), + max_no_files => Count, + max_no_bytes => Size}, + formatter => + {lager_logger_formatter, #{report_cb => fun lager_logger_formatter:report_cb/1, + formatter => Formatter, + formatter_config => FormatterConfig}}}}, + NewLevel = case lager_util:level_to_num(Level) > lager_util:level_to_num(CurrentLevel) of + true -> + Level; + false -> + CurrentLevel + end, + generate_logger_handlers(Tail, {NewLevel, [Handler|Acc]}). + + %% @private trace_func(#trace_func_state_v1{pid=Pid, level=Level, format_string=Fmt}=FuncState, Event, ProcState) -> _ = lager:log(Level, Pid, Fmt, [Event, ProcState]), diff --git a/src/lager_app.erl b/src/lager_app.erl index be59289..1abd05c 100644 --- a/src/lager_app.erl +++ b/src/lager_app.erl @@ -30,7 +30,8 @@ start_handler/3, configure_sink/2, stop/1, - boot/1]). + boot/1, + default_handlers/0]). %% The `application:get_env/3` compatibility wrapper was useful %% for other modules in r15 and before @@ -225,11 +226,22 @@ get_env(Application, Key, Default) -> start(_StartType, _StartArgs) -> {ok, Pid} = lager_sup:start_link(), - SavedHandlers = boot(), - _ = boot('__all_extra'), - _ = boot('__traces'), - clean_up_config_checks(), - {ok, Pid, SavedHandlers}. + case application:get_env(lager, lager_use_logger, false) of + false -> + SavedHandlers = boot(), + _ = boot('__all_extra'), + _ = boot('__traces'), + clean_up_config_checks(), + {ok, Pid, SavedHandlers}; + true -> + case application:get_env(lager, configure_logger, false) of + true -> + ok = lager:configure_logger(); + false -> + ok + end, + {ok, Pid} + end. boot() -> %% Handle the default sink. @@ -276,6 +288,9 @@ stop(Handlers) -> end, Handlers), lager_config:cleanup(). +default_handlers() -> + ?DEFAULT_HANDLER_CONF. + expand_handlers([]) -> []; expand_handlers([{lager_file_backend, [{Key, _Value}|_]=Config}|T]) when is_atom(Key) -> diff --git a/src/lager_logger_formatter.erl b/src/lager_logger_formatter.erl new file mode 100644 index 0000000..d8f2e2a --- /dev/null +++ b/src/lager_logger_formatter.erl @@ -0,0 +1,119 @@ +-module(lager_logger_formatter). + +%% convert logger formatter calls into lager formatter ones + +-export([report_cb/1, format/2]).%, check_config/1]). + +report_cb(#{label := {gen_server, terminate}, name := Name, reason := Reason}) -> + Formatted = error_logger_lager_h:format_reason(Reason), + {"gen_server ~w terminated with reason: ~s", [Name, Formatted]}; +report_cb(#{label := {gen_fsm, terminate}, name := Name, state_name := StateName, reason := Reason}) -> + Formatted = error_logger_lager_h:format_reason(Reason), + {"gen_fsm ~w in state ~w terminated with reason: ~s", [Name, StateName, Formatted]}; +report_cb(#{label := {gen_event, terminate}, name := Name, handler := Handler, reason := Reason}) -> + Formatted = error_logger_lager_h:format_reason(Reason), + {"gen_event ~w installed in ~w terminated with reason: ~s", [Handler, Name, Formatted]}; +report_cb(#{label := {gen_statem, terminate}, name := Name, reason := Reason}) -> + Formatted = error_logger_lager_h:format_reason(Reason), + %% XXX I can't find the FSM statename in the error report, maybe it should be added + {"gen_statem ~w terminated with reason: ~s", [Name, Formatted]}; +report_cb(#{msg := {report, #{label := {Behaviour, no_handle_info}, mod := Mod, msg := Msg}}}) -> + {"undefined handle_info for ~p in ~s ~p", [Msg, Behaviour, Mod]}; +report_cb(#{label := {supervisor, progress}, report := Report}) -> + case application:get_env(lager, suppress_supervisor_start_stop, false) of + true -> + {"", []}; + false -> + {supervisor, Name} = lists:keyfind(supervisor, 1, Report), + {started, Started} = lists:keyfind(started, 1, Report), + case lists:keyfind(id, 1, Started) of + false -> + %% supervisor itself starting + {mfa, {Module, Function, Args}} = lists:keyfind(mfa, 1, Started), + {pid, Pid} = lists:keyfind(pid, 1, Started), + {"Supervisor ~w started as ~p at pid ~w", [Name, error_logger_lager_h:format_mfa({Module, Function, Args}), Pid]}; + {id, ChildID} -> + case lists:keyfind(pid, 1, Started) of + {pid, Pid} -> + {"Supervisor ~w started child ~p at pid ~w", [Name, ChildID, Pid]}; + false -> + %% children is a list of pids for some reason? and we only get the count + {nb_children, ChildCount} = lists:keyfind(nb_children, 1, Started), + {"Supervisor ~w started ~b children ~p", [Name, ChildCount, ChildID]} + end + end + end; +report_cb(#{label := {supervisor, _Error}, report := Report}) -> + {supervisor, Name} = lists:keyfind(supervisor, 1, Report), + {reason, Reason} = lists:keyfind(reason, 1, Report), + Formatted = error_logger_lager_h:format_reason(Reason), + {errorContext, ErrorContext} = lists:keyfind(errorContext, 1, Report), + {offender, Offender} = lists:keyfind(offender, 1, Report), + case lists:keyfind(mod, 1, Offender) of + {mod, _Mod} -> + {pid, Pid} = lists:keyfind(pid, 1, Offender), + %% this comes from supervisor_bridge + {"Supervisor ~w had ~p ~p with reason ~s", [Name, Pid, ErrorContext, Formatted]}; + false -> + {id, ChildID} = lists:keyfind(id, 1, Offender), + case lists:keyfind(pid, 1, Offender) of + {pid, Pid} -> + {"Supervisor ~w had ~p ~p ~p with reason ~s", [Name, ChildID, Pid, ErrorContext, Formatted]}; + false -> + {"Supervisor ~w had ~p ~p with reason ~s", [Name, ChildID, ErrorContext, Formatted]} + end + end; +report_cb(#{label := {application_controller, progress}, report := Report}) -> + case application:get_env(lager, suppress_application_start_stop, false) of + true -> {"", []}; + false -> + {application, Name} = lists:keyfind(application, 1, Report), + {started_at, Node} = lists:keyfind(started_at, 1, Report), + {"Application ~w started on node ~w", [Name, Node]} + end; +report_cb(#{label := {application_controller, exit}, report := Report}) -> + {exited, Reason} = lists:keyfind(exited, 1, Report), + case application:get_env(lager, suppress_application_start_stop) of + {ok, true} when Reason == stopped -> + {"", []}; + _ -> + {application, Name} = lists:keyfind(application, 1, Report), + Formatted = error_logger_lager_h:format_reason(Reason), + {"Application ~w exited with reason: ~s", [Name, Formatted]} + end. +%% TODO handle proc_lib crash + +format(#{msg := {report, _Report}, meta := Metadata} = Event, #{report_cb := Fun} = Config) when is_function(Fun, 1); is_function(Fun, 2) -> + format(Event#{meta => Metadata#{report_cb => Fun}}, maps:remove(report_cb, Config)); +format(#{level := _Level, msg := {report, Report}, meta := #{report_cb := Fun}} = Event, Config) when is_function(Fun, 1) -> + case Fun(Report) of + {"", []} -> ""; + {Format, Args} when is_list(Format), is_list(Args) -> + format(Event#{msg => {Format, Args}}, Config) + end; +format(#{level := Level, msg := {string, String}, meta := Metadata}, Config) -> + do_format(Level, String, Metadata, Config); +format(#{level := Level, msg := {FmtStr, FmtArgs}, meta := Metadata}, Config) -> + Msg = lager_format:format(FmtStr, FmtArgs, maps:get(max_size, Config, 1024)), + do_format(Level, Msg, Metadata, Config). + +do_format(Level, Msg, Metadata, Config) -> + FormatModule = maps:get(formatter, Config, lager_default_formatter), + Timestamp = maps:get(time, Metadata), + MegaSecs = Timestamp div 1000000000000, + Secs = (Timestamp rem 1000000000000) div 1000000, + MicroSecs = (Timestamp rem 1000000000000) rem 1000000, + {Colors, End} = case maps:get(colors, Config, false) of + true -> + {application:get_env(lager, colors, []), "\e[0m"}; + false -> + {[], ""} + end, + [FormatModule:format(lager_msg:new(Msg, {MegaSecs, Secs, MicroSecs}, Level, convert_metadata(Metadata), []), maps:get(formatter_config, Config, []), Colors), End]. + +convert_metadata(Metadata) -> + maps:fold(fun(mfa, {Module, Function, Arity}, Acc) -> + [{module, Module}, {function, Function}, {arity, Arity}|Acc]; + (K, V, Acc) -> + [{K, V}|Acc] + end, [], Metadata). diff --git a/src/lager_sup.erl b/src/lager_sup.erl index 97bfd00..890f272 100644 --- a/src/lager_sup.erl +++ b/src/lager_sup.erl @@ -32,24 +32,29 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> - %% set up the config, is safe even during relups - lager_config:new(), - %% TODO: - %% Always start lager_event as the default and make sure that - %% other gen_event stuff can start up as needed - %% - %% Maybe a new API to handle the sink and its policy? - Children = [ - {lager, {gen_event, start_link, [{local, lager_event}]}, - permanent, 5000, worker, dynamic}, - {lager_handler_watcher_sup, {lager_handler_watcher_sup, start_link, []}, - permanent, 5000, supervisor, [lager_handler_watcher_sup]}], - - CrashLog = decide_crash_log(application:get_env(lager, crash_log, false)), - - {ok, {{one_for_one, 10, 60}, - Children ++ CrashLog - }}. + case application:get_env(lager, lager_use_logger, false) of + true -> + {ok, {{one_for_one, 10, 60}, []}}; + false -> + %% set up the config, is safe even during relups + lager_config:new(), + %% TODO: + %% Always start lager_event as the default and make sure that + %% other gen_event stuff can start up as needed + %% + %% Maybe a new API to handle the sink and its policy? + Children = [ + {lager, {gen_event, start_link, [{local, lager_event}]}, + permanent, 5000, worker, dynamic}, + {lager_handler_watcher_sup, {lager_handler_watcher_sup, start_link, []}, + permanent, 5000, supervisor, [lager_handler_watcher_sup]}], + + CrashLog = decide_crash_log(application:get_env(lager, crash_log, false)), + + {ok, {{one_for_one, 10, 60}, + Children ++ CrashLog + }} + end. validate_positive({ok, Val}, _Default) when is_integer(Val) andalso Val >= 0 -> Val; diff --git a/src/lager_transform.erl b/src/lager_transform.erl index 5c2134c..8f5a483 100644 --- a/src/lager_transform.erl +++ b/src/lager_transform.erl @@ -33,10 +33,12 @@ parse_transform(AST, Options) -> Enable = proplists:get_value(lager_print_records_flag, Options, true), Sinks = [lager] ++ proplists:get_value(lager_extra_sinks, Options, []), Functions = proplists:get_value(lager_function_transforms, Options, []), + UseLogger = proplists:get_value(lager_use_logger, Options, false), put(print_records_flag, Enable), put(truncation_size, TruncSize), put(sinks, Sinks), put(functions, lists:keysort(1, Functions)), + put(use_logger, UseLogger), 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)), @@ -63,6 +65,7 @@ walk_ast(Acc, [{attribute, _, lager_function_transforms, FromModule }=H|T]) -> walk_ast([H|Acc], T); walk_ast(Acc, [{function, Line, Name, Arity, Clauses}|T]) -> put(function, Name), + put(arity, Arity), walk_ast([{function, Line, Name, Arity, walk_clauses([], Clauses)}|Acc], T); walk_ast(Acc, [{attribute, _, record, {Name, Fields}}=H|T]) -> @@ -140,24 +143,40 @@ do_transform(Line, SinkName, Severity, Arguments0) -> do_transform(Line, SinkName, Severity, Arguments0, safe). do_transform(Line, SinkName, Severity, Arguments0, Safety) -> - SeverityAsInt=lager_util: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}}}}}}}, + DefaultAttrs0 = case get(use_logger) of + true -> + {cons, Line, {tuple, Line, [ + {atom, Line, pid}, {call, Line, {atom, Line, self}, []}]}, + {cons, Line, {tuple, Line, [ + {atom, Line, gl}, {call, Line, {atom, Line, group_leader}, []}]}, + {cons, Line, {tuple, Line, [ + {atom, Line, time}, {call, Line, {remote, Line, {atom, Line, erlang}, {atom, Line, system_time}}, [{atom, Line, microsecond}]}]}, + {cons, Line, {tuple, Line, [ + {atom, Line, mfa}, {tuple, Line, [{atom, Line, get(module)}, {atom, Line, get(function)}, {atom, Line, get(arity)}]}]}, + {cons, Line, {tuple, Line, [ + {atom, Line, file}, {string, Line, get(filename)}]}, + {cons, Line, {tuple, Line, [ + {atom, Line, line}, {integer, Line, Line}]}, + %% 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}}, []}}}}}}}; + false -> + {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}}, []}}}}}} + end, Functions = get(functions), DefaultAttrs1 = add_function_transforms(Line, DefaultAttrs0, Functions), DefaultAttrs = case erlang:get(application) of @@ -171,67 +190,82 @@ do_transform(Line, SinkName, Severity, Arguments0, Safety) -> {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,?DEFAULT_SINK}]}, - {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}]}]}. + case get(use_logger) of + true -> + case Arguments of + {atom, _, none} -> + %% logger:log(Level, Format, Args, Metadata) + {call,Line,{remote, Line, {atom, Line, logger}, {atom, Line, log}}, + [{atom,Line,Severity}, Message, {call, Line, {remote, Line, {atom, Line, maps}, {atom, Line, from_list}}, [Meta]}]}; + _ -> + %% logger:log(Level, String, Metadata) + {call,Line,{remote, Line, {atom, Line, logger}, {atom, Line, log}}, + [{atom,Line,Severity}, Message, Arguments, {call, Line, {remote, Line, {atom, Line, maps}, {atom, Line, from_list}}, [Meta]}]} + end; + false -> + SeverityAsInt=lager_util:level_to_num(Severity), + %% 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,?DEFAULT_SINK}]}, + {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}]}]} + end. handle_args(DefaultAttrs, Line, [{cons, LineNum, {tuple, _, _}, _} = Attrs]) -> {concat_lists(DefaultAttrs, Attrs), {string, LineNum, ""}, {atom, Line, none}}; @@ -317,6 +351,7 @@ guess_application(Dirname, Attr) when Dirname /= undefined -> ok end; guess_application(undefined, {attribute, _, file, {Filename, _}}) -> + put(filename, Filename), Dir = filename:dirname(Filename), find_app_file(Dir); guess_application(_, _) ->