Skip to content

Commit 558185b

Browse files
committedOct 14, 2013
add a command line programming module to the system
This is a general module that helps output from command line applications written in erlang.
1 parent 79c5436 commit 558185b

File tree

2 files changed

+279
-0
lines changed

2 files changed

+279
-0
lines changed
 

‎include/ec_cmd_log.hrl

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*-
2+
%%% Copyright 2012 Erlware, LLC. All Rights Reserved.
3+
%%%
4+
%%% This file is provided to you under the Apache License,
5+
%%% Version 2.0 (the "License"); you may not use this file
6+
%%% except in compliance with the License. You may obtain
7+
%%% a copy of the License at
8+
%%%
9+
%%% http://www.apache.org/licenses/LICENSE-2.0
10+
%%%
11+
%%% Unless required by applicable law or agreed to in writing,
12+
%%% software distributed under the License is distributed on an
13+
%%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
%%% KIND, either express or implied. See the License for the
15+
%%% specific language governing permissions and limitations
16+
%%% under the License.
17+
%%%---------------------------------------------------------------------------
18+
%%% @author Eric Merritt <ericbmerritt@gmail.com>
19+
%%% @copyright (C) 2012 Erlware, LLC.
20+
21+
-define(EC_ERROR, 0).
22+
-define(EC_WARN, 1).
23+
-define(EC_INFO, 2).
24+
-define(EC_DEBUG, 3).

‎src/ec_cmd_log.erl

+255
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*-
2+
%%% Copyright 2012 Erlware, LLC. All Rights Reserved.
3+
%%%
4+
%%% This file is provided to you under the Apache License,
5+
%%% Version 2.0 (the "License"); you may not use this file
6+
%%% except in compliance with the License. You may obtain
7+
%%% a copy of the License at
8+
%%%
9+
%%% http://www.apache.org/licenses/LICENSE-2.0
10+
%%%
11+
%%% Unless required by applicable law or agreed to in writing,
12+
%%% software distributed under the License is distributed on an
13+
%%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
%%% KIND, either express or implied. See the License for the
15+
%%% specific language governing permissions and limitations
16+
%%% under the License.
17+
%%%---------------------------------------------------------------------------
18+
%%% @author Eric Merritt <ericbmerritt@gmail.com>
19+
%%% @copyright (C) 2012 Erlware, LLC.
20+
%%%
21+
%%% @doc This provides simple output functions for command line apps. You should
22+
%%% use this to talk to the users if you are wrting code for the system
23+
-module(ec_cmd_log).
24+
25+
-export([new/1,
26+
new/2,
27+
log/4,
28+
should/2,
29+
debug/2,
30+
debug/3,
31+
info/2,
32+
info/3,
33+
error/2,
34+
error/3,
35+
warn/2,
36+
warn/3,
37+
log_level/1,
38+
atom_log_level/1,
39+
format/1]).
40+
41+
-include_lib("erlware_commons/include/ec_cmd_log.hrl").
42+
43+
-define(RED, 31).
44+
-define(GREEN, 32).
45+
-define(YELLOW, 33).
46+
-define(BLUE, 34).
47+
-define(MAGENTA, 35).
48+
-define(CYAN, 36).
49+
50+
-define(PREFIX, "===> ").
51+
52+
-record(state_t, {mod=?MODULE :: ec_log,
53+
log_level=0 :: int_log_level(),
54+
caller=api :: api | command_line}).
55+
56+
%%============================================================================
57+
%% types
58+
%%============================================================================
59+
-export_type([t/0,
60+
int_log_level/0,
61+
atom_log_level/0,
62+
log_level/0,
63+
log_fun/0]).
64+
65+
-type log_level() :: int_log_level() | atom_log_level().
66+
67+
-type int_log_level() :: 0..3.
68+
69+
-type atom_log_level() :: error | warn | info | debug.
70+
71+
-type log_fun() :: fun(() -> iolist()).
72+
73+
-type color() :: 31..36.
74+
75+
-opaque t() :: record(state_t).
76+
77+
%%============================================================================
78+
%% API
79+
%%============================================================================
80+
%% @doc Create a new 'log level' for the system
81+
-spec new(log_level()) -> t().
82+
new(LogLevel) ->
83+
new(LogLevel, api).
84+
85+
new(LogLevel, Caller) when LogLevel >= 0, LogLevel =< 3 ->
86+
#state_t{mod=?MODULE, log_level=LogLevel, caller=Caller};
87+
new(AtomLogLevel, Caller)
88+
when AtomLogLevel =:= error;
89+
AtomLogLevel =:= warn;
90+
AtomLogLevel =:= info;
91+
AtomLogLevel =:= debug ->
92+
LogLevel = case AtomLogLevel of
93+
error -> 0;
94+
warn -> 1;
95+
info -> 2;
96+
debug -> 3
97+
end,
98+
new(LogLevel, Caller).
99+
100+
%% @doc log at the debug level given the current log state with a string or
101+
%% function that returns a string
102+
-spec debug(t(), string() | log_fun()) -> ok.
103+
debug(LogState, Fun)
104+
when erlang:is_function(Fun) ->
105+
log(LogState, ?EC_DEBUG, fun() ->
106+
colorize(LogState, ?CYAN, false, Fun())
107+
end);
108+
debug(LogState, String) ->
109+
debug(LogState, "~s~n", [String]).
110+
111+
%% @doc log at the debug level given the current log state with a format string
112+
%% and argements @see io:format/2
113+
-spec debug(t(), string(), [any()]) -> ok.
114+
debug(LogState, FormatString, Args) ->
115+
log(LogState, ?EC_DEBUG, colorize(LogState, ?CYAN, false, FormatString), Args).
116+
117+
%% @doc log at the info level given the current log state with a string or
118+
%% function that returns a string
119+
-spec info(t(), string() | log_fun()) -> ok.
120+
info(LogState, Fun)
121+
when erlang:is_function(Fun) ->
122+
log(LogState, ?EC_INFO, fun() ->
123+
colorize(LogState, ?GREEN, false, Fun())
124+
end);
125+
info(LogState, String) ->
126+
info(LogState, "~s~n", [String]).
127+
128+
%% @doc log at the info level given the current log state with a format string
129+
%% and argements @see io:format/2
130+
-spec info(t(), string(), [any()]) -> ok.
131+
info(LogState, FormatString, Args) ->
132+
log(LogState, ?EC_INFO, colorize(LogState, ?GREEN, false, FormatString), Args).
133+
134+
%% @doc log at the error level given the current log state with a string or
135+
%% format string that returns a function
136+
-spec error(t(), string() | log_fun()) -> ok.
137+
error(LogState, Fun)
138+
when erlang:is_function(Fun) ->
139+
log(LogState, ?EC_ERROR, fun() ->
140+
colorize(LogState, ?RED, false, Fun())
141+
end);
142+
error(LogState, String) ->
143+
error(LogState, "~s~n", [String]).
144+
145+
%% @doc log at the error level given the current log state with a format string
146+
%% and argements @see io:format/2
147+
-spec error(t(), string(), [any()]) -> ok.
148+
error(LogState, FormatString, Args) ->
149+
log(LogState, ?EC_ERROR, colorize(LogState, ?GREEN, false, FormatString), Args).
150+
151+
%% @doc log at the warn level given the current log state with a string or
152+
%% format string that returns a function
153+
-spec warn(t(), string() | log_fun()) -> ok.
154+
warn(LogState, Fun)
155+
when erlang:is_function(Fun) ->
156+
log(LogState, ?EC_WARN, fun() -> colorize(LogState, ?MAGENTA, false, Fun()) end);
157+
warn(LogState, String) ->
158+
warn(LogState, "~s~n", [String]).
159+
160+
%% @doc log at the warn level given the current log state with a format string
161+
%% and argements @see io:format/2
162+
-spec warn(t(), string(), [any()]) -> ok.
163+
warn(LogState, FormatString, Args) ->
164+
log(LogState, ?EC_WARN, colorize(LogState, ?MAGENTA, false, FormatString), Args).
165+
166+
%% @doc Execute the fun passed in if log level is as expected.
167+
-spec log(t(), int_log_level(), log_fun()) -> ok.
168+
log(#state_t{mod=?MODULE, log_level=DetailLogLevel}, LogLevel, Fun)
169+
when DetailLogLevel >= LogLevel ->
170+
io:format("~s~n", [Fun()]);
171+
log(_, _, _) ->
172+
ok.
173+
174+
%% @doc when the module log level is less then or equal to the log level for the
175+
%% call then write the log info out. When its not then ignore the call.
176+
-spec log(t(), int_log_level(), string(), [any()]) -> ok.
177+
log(#state_t{mod=?MODULE, log_level=DetailLogLevel}, LogLevel, FormatString, Args)
178+
when DetailLogLevel >= LogLevel,
179+
erlang:is_list(Args) ->
180+
io:format(FormatString, Args);
181+
log(_, _, _, _) ->
182+
ok.
183+
184+
%% @doc return a boolean indicating if the system should log for the specified
185+
%% levelg
186+
-spec should(t(), int_log_level() | any()) -> boolean().
187+
should(#state_t{mod=?MODULE, log_level=DetailLogLevel}, LogLevel)
188+
when DetailLogLevel >= LogLevel ->
189+
true;
190+
should(_, _) ->
191+
false.
192+
193+
%% @doc get the current log level as an integer
194+
-spec log_level(t()) -> int_log_level().
195+
log_level(#state_t{mod=?MODULE, log_level=DetailLogLevel}) ->
196+
DetailLogLevel.
197+
198+
%% @doc get the current log level as an atom
199+
-spec atom_log_level(t()) -> atom_log_level().
200+
atom_log_level(#state_t{mod=?MODULE, log_level=?EC_ERROR}) ->
201+
error;
202+
atom_log_level(#state_t{mod=?MODULE, log_level=?EC_WARN}) ->
203+
warn;
204+
atom_log_level(#state_t{mod=?MODULE, log_level=?EC_INFO}) ->
205+
info;
206+
atom_log_level(#state_t{mod=?MODULE, log_level=?EC_DEBUG}) ->
207+
debug.
208+
209+
-spec format(t()) -> iolist().
210+
format(Log) ->
211+
[<<"(">>,
212+
ec_cnv:to_binary(log_level(Log)), <<":">>,
213+
ec_cnv:to_binary(atom_log_level(Log)),
214+
<<")">>].
215+
216+
-spec colorize(t(), color(), boolean(), string()) -> string().
217+
colorize(#state_t{caller=command_line}, Color, false, Msg) when is_integer(Color) ->
218+
colorize_(Color, 0, Msg);
219+
colorize(_LogState, _Color, _Bold, Msg) ->
220+
Msg.
221+
222+
-spec colorize_(color(), integer(), string()) -> string().
223+
colorize_(Color, Bold, Msg) when is_integer(Color), is_integer(Bold)->
224+
lists:flatten(io_lib:format("\033[~B;~Bm~s~s\033[0m", [Bold, Color, ?PREFIX, Msg])).
225+
226+
%%%===================================================================
227+
%%% Test Functions
228+
%%%===================================================================
229+
230+
-ifndef(NOTEST).
231+
-include_lib("eunit/include/eunit.hrl").
232+
233+
should_test() ->
234+
ErrorLogState = new(error),
235+
?assertMatch(true, should(ErrorLogState, ?EC_ERROR)),
236+
?assertMatch(true, not should(ErrorLogState, ?EC_INFO)),
237+
?assertMatch(true, not should(ErrorLogState, ?EC_DEBUG)),
238+
?assertEqual(?EC_ERROR, log_level(ErrorLogState)),
239+
?assertEqual(error, atom_log_level(ErrorLogState)),
240+
241+
InfoLogState = new(info),
242+
?assertMatch(true, should(InfoLogState, ?EC_ERROR)),
243+
?assertMatch(true, should(InfoLogState, ?EC_INFO)),
244+
?assertMatch(true, not should(InfoLogState, ?EC_DEBUG)),
245+
?assertEqual(?EC_INFO, log_level(InfoLogState)),
246+
?assertEqual(info, atom_log_level(InfoLogState)),
247+
248+
DebugLogState = new(debug),
249+
?assertMatch(true, should(DebugLogState, ?EC_ERROR)),
250+
?assertMatch(true, should(DebugLogState, ?EC_INFO)),
251+
?assertMatch(true, should(DebugLogState, ?EC_DEBUG)),
252+
?assertEqual(?EC_DEBUG, log_level(DebugLogState)),
253+
?assertEqual(debug, atom_log_level(DebugLogState)).
254+
255+
-endif.

0 commit comments

Comments
 (0)
Please sign in to comment.