From b6a1827490fb96a5078c6636cf37de69ecbbce15 Mon Sep 17 00:00:00 2001 From: Pedram Nimreezi Date: Thu, 13 Dec 2012 07:29:19 -0500 Subject: [PATCH] Add colors to lager, not enabled by default --- .gitignore | 1 + rebar.config | 2 +- src/lager.app.src | 14 ++++++++ src/lager_app.erl | 8 ++--- src/lager_console_backend.erl | 59 ++++++++++++++++++++++----------- src/lager_default_formatter.erl | 32 +++++++++++++----- 6 files changed, 83 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index e04c657..ff8fc4b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ doc *.swp erl_crash.dump .project +log diff --git a/rebar.config b/rebar.config index a9f18d7..97b3169 100644 --- a/rebar.config +++ b/rebar.config @@ -2,4 +2,4 @@ {erl_first_files, ["src/lager_util.erl"]}. {cover_enabled, true}. -{edoc_opts, [{stylesheet_file, "./priv/edoc.css"}]}. \ No newline at end of file +{edoc_opts, [{stylesheet_file, "./priv/edoc.css"}]}. diff --git a/src/lager.app.src b/src/lager.app.src index a9db914..e0d7ef0 100644 --- a/src/lager.app.src +++ b/src/lager.app.src @@ -20,6 +20,20 @@ {"log/console.log", info, 10485760, "$D0", 5} ]} ]}, + %% What colors to use with what log levels + {colored, false}, + {colors, [ + {debug, "\e[0;38m" }, + {info, "\e[1;37m" }, + {notice, "\e[1;36m" }, + {warning, "\e[1;33m" }, + {error, "\e[1;31m" }, + {critical, "\e[1;35m" }, + {alert, "\e[1;44m" }, + {emergency, "\e[1;41m" } + + ]}, + %% Whether to write a crash log, and where. Undefined means no crash logger. {crash_log, "log/crash.log"}, %% Maximum size in bytes of events in the crash log - defaults to 65536 diff --git a/src/lager_app.erl b/src/lager_app.erl index b2a228b..352c4f3 100644 --- a/src/lager_app.erl +++ b/src/lager_app.erl @@ -78,11 +78,11 @@ start(_StartType, _StartArgs) -> {ok, false} -> []; _ -> - case application:get_env(lager, error_logger_whitelist) of + WhiteList = case application:get_env(lager, error_logger_whitelist) of undefined -> - WhiteList = []; - {ok, WhiteList} -> - WhiteList + []; + {ok, WhiteList0} -> + WhiteList0 end, case supervisor:start_child(lager_handler_watcher_sup, [error_logger, error_logger_lager_h, [HighWaterMark]]) of diff --git a/src/lager_console_backend.erl b/src/lager_console_backend.erl index 442204a..8f968e0 100644 --- a/src/lager_console_backend.erl +++ b/src/lager_console_backend.erl @@ -24,7 +24,7 @@ -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). --record(state, {level, formatter,format_config}). +-record(state, {level, formatter,format_config,colors=[]}). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). @@ -32,26 +32,33 @@ -endif. -include("lager.hrl"). - --define(TERSE_FORMAT,[time, " [", severity,"] ", message, "\r\n"]). +-define(TERSE_FORMAT,[time, " ", color, "[", severity,"] ", message]). %% @private init([Level, true]) -> % for backwards compatibility - init([Level,{lager_default_formatter,[{eol, "\r\n"}]}]); + init([Level,{lager_default_formatter,[{eol, eol()}]}]); init([Level,false]) -> % for backwards compatibility - init([Level,{lager_default_formatter,?TERSE_FORMAT}]); + init([Level,{lager_default_formatter,?TERSE_FORMAT ++ [eol()]}]); init([Level,{Formatter,FormatterConfig}]) when is_atom(Formatter) -> - try lager_util:config_to_mask(Level) of + Colors = case application:get_env(lager, colored) of + {ok, true} -> + {ok, LagerColors} = application:get_env(lager, colors), + LagerColors; + _ -> [] + end, + + try lager_util:config_to_mask(Level) of Levels -> {ok, #state{level=Levels, formatter=Formatter, - format_config=FormatterConfig}} + format_config=FormatterConfig, + colors=Colors}} catch _:_ -> {error, bad_log_level} end; init(Level) -> - init([Level,{lager_default_formatter,?TERSE_FORMAT}]). + init([Level,{lager_default_formatter,?TERSE_FORMAT ++ [eol()]}]). %% @private @@ -70,10 +77,10 @@ handle_call(_Request, State) -> %% @private handle_event({log, Message}, - #state{level=L,formatter=Formatter,format_config=FormatConfig} = State) -> + #state{level=L,formatter=Formatter,format_config=FormatConfig,colors=Colors} = State) -> case lager_util:is_loggable(Message, L, ?MODULE) of true -> - io:put_chars(user, Formatter:format(Message,FormatConfig)), + io:put_chars(user, Formatter:format(Message,FormatConfig,Colors)), {ok, State}; false -> {ok, State} @@ -93,6 +100,14 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. +eol() -> + case application:get_env(lager, colored) of + {ok, true} -> + "\e[0m\r\n"; + _ -> + "\r\n" + end. + -ifdef(TEST). console_log_test_() -> %% tiny recursive fun that pretends to be a group leader @@ -131,16 +146,17 @@ console_log_test_() -> {"regular console logging", fun() -> Pid = spawn(F(self())), - gen_event:add_handler(lager_event, lager_console_backend, info), unregister(user), register(user, Pid), erlang:group_leader(Pid, whereis(lager_event)), + gen_event:add_handler(lager_event, lager_console_backend, info), lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}), lager:log(info, self(), "Test message"), receive {io_request, From, ReplyAs, {put_chars, unicode, Msg}} -> From ! {io_reply, ReplyAs, ok}, - ?assertMatch([_, "[info]", "Test message\r\n"], re:split(Msg, " ", [{return, list}, {parts, 3}])) + TestMsg = "Test message" ++ eol(), + ?assertMatch([_, "[info]", TestMsg], re:split(Msg, " ", [{return, list}, {parts, 3}])) after 500 -> ?assert(false) @@ -156,11 +172,11 @@ console_log_test_() -> gen_event:add_handler(lager_event, lager_console_backend, [info, true]), lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}), lager:info("Test message"), - lager:info("Test message"), PidStr = pid_to_list(self()), receive {io_request, _, _, {put_chars, unicode, Msg}} -> - ?assertMatch([_, _, "[info]", PidStr, _,"Test message\r\n"], re:split(Msg, "[ @]", [{return, list}, {parts, 6}])) + TestMsg = "Test message" ++ eol(), + ?assertMatch([_, _, "[info]", PidStr, _, TestMsg], re:split(Msg, "[ @]", [{return, list}, {parts, 6}])) after 500 -> ?assert(false) @@ -183,7 +199,8 @@ console_log_test_() -> ModuleStr = atom_to_list(?MODULE), receive {io_request, _, _, {put_chars, unicode, Msg}} -> - ?assertMatch([_, _, "info", NodeStr, PidStr, ModuleStr, _, _, _, "Test message\r\n"], + TestMsg = "Test message" ++ eol(), + ?assertMatch([_, _, "info", NodeStr, PidStr, ModuleStr, _, _, _, TestMsg], re:split(Msg, "#", [{return, list}, {parts, 10}])) after 500 -> @@ -213,7 +230,8 @@ console_log_test_() -> receive {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} -> From1 ! {io_reply, ReplyAs1, ok}, - ?assertMatch([_, "[debug]", "Test message\r\n"], re:split(Msg1, " ", [{return, list}, {parts, 3}])) + TestMsg = "Test message" ++ eol(), + ?assertMatch([_, "[debug]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}])) after 500 -> ?assert(false) @@ -242,7 +260,8 @@ console_log_test_() -> receive {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} -> From1 ! {io_reply, ReplyAs1, ok}, - ?assertMatch([_, "[error]", "Test message\r\n"], re:split(Msg1, " ", [{return, list}, {parts, 3}])) + TestMsg = "Test message" ++ eol(), + ?assertMatch([_, "[error]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}])) after 1000 -> ?assert(false) @@ -271,7 +290,8 @@ console_log_test_() -> receive {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} -> From1 ! {io_reply, ReplyAs1, ok}, - ?assertMatch([_, "[debug]", "Test message\r\n"], re:split(Msg1, " ", [{return, list}, {parts, 3}])) + TestMsg = "Test message" ++ eol(), + ?assertMatch([_, "[debug]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}])) after 1000 -> ?assert(false) @@ -301,7 +321,8 @@ console_log_test_() -> receive {io_request, From1, ReplyAs1, {put_chars, unicode, Msg1}} -> From1 ! {io_reply, ReplyAs1, ok}, - ?assertMatch([_, "[debug]", "Test message\r\n"], re:split(Msg1, " ", [{return, list}, {parts, 3}])) + TestMsg = "Test message" ++ eol(), + ?assertMatch([_, "[debug]", TestMsg], re:split(Msg1, " ", [{return, list}, {parts, 3}])) after 1000 -> ?assert(false) diff --git a/src/lager_default_formatter.erl b/src/lager_default_formatter.erl index cbe614f..e061b7e 100644 --- a/src/lager_default_formatter.erl +++ b/src/lager_default_formatter.erl @@ -27,7 +27,7 @@ %% %% Exported Functions %% --export([format/2]). +-export([format/2, format/3]). %% %% API Functions @@ -53,22 +53,28 @@ %% %% `[{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()) -> any(). -format(Msg,[]) -> - format(Msg, [{eol, "\n"}]); -format(Msg,[{eol, EOL}]) -> +-spec format(lager_msg:lager_msg(),list(),list()) -> any(). +format(Msg,[], Colors) -> + format(Msg, [{eol, "\n"}], Colors); +format(Msg,[{eol, EOL}], Colors) -> format(Msg, - [date, " ", time, " [", severity, "] ", + [date, " ", time, " ", color, "[", severity, "] ", {pid, ""}, {module, [ {pid, ["@"], ""}, module, {function, [":", function], ""}, {line, [":",line], ""}], ""}, - " ", message, EOL]); -format(Message,Config) -> - [ output(V,Message) || V <- Config ]. + " ", message, EOL], 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(). +format(Msg, Config) -> + format(Msg, Config, []). -spec output(term(),lager_msg:lager_msg()) -> iolist(). output(message,Msg) -> lager_msg:message(Msg); @@ -97,6 +103,14 @@ output({Prop, Present, Absent}, Msg) when is_atom(Prop) -> end; output(Other,_) -> make_printable(Other). +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);