Skip to content

Commit 8974a32

Browse files
author
Sungjin Park
committed
New logging mechanism using disk_log.
1 parent a1743cb commit 8974a32

14 files changed

+259
-52
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ client: compile
3535

3636
# Make a textual log snapshot.
3737
log:
38-
priv/script/dump-log.escript $(node)-`date "+%Y%m%dT%H%M%S.log"` priv/log/$(node)@`hostname -s`
38+
priv/script/dump-log.escript $(node)`date "-sasl-+%Y%m%dT%H%M%S.log"` priv/log/$(node)@`hostname -s`
3939

4040
# Perform unit tests.
4141
check: compile
File renamed without changes.

src/fubar.app.src

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
{registered, []},
66
{applications, [kernel, stdlib]},
77
{mod, {fubar_app, []}},
8-
{env, [{fubar, [{log_dir, "priv/log"},
9-
{log_max_bytes, 10485760},
10-
{log_max_files, 10}
11-
]},
12-
{fubar_app, [{acceptors, 4},
8+
{env, [{fubar_app, [{acceptors, 4},
139
{max_connections, infinity}
1410
]},
11+
{fubar_log, [{dir, "priv/log"},
12+
{max_bytes, 10485760},
13+
{max_files, 10},
14+
{classes, [trace, error]}
15+
]},
1516
{mqtt_protocol, [{max_packet_size, 4096},
1617
{dispatch, mqtt_server}
1718
]},

src/fubar.erl

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,21 @@
1616
-endif.
1717

1818
-include("fubar.hrl").
19-
-include("log.hrl").
2019
-include("props_to_record.hrl").
2120

21+
-record(settings, {dir = "priv/log" :: string(),
22+
max_bytes = 10485760 :: integer(),
23+
max_files = 10 :: integer()}).
24+
2225
%%
2326
%% Exported Functions
2427
%%
2528
-export([start/0, stop/0, % application start/stop
2629
settings/1, settings/2, % application environment getter/setter
2730
create/1, set/2, get/2, timestamp/2, % fubar message manipulation
28-
trace/2, % trace logging
2931
apply_all_module_attributes_of/1 % bootstrapping utility
3032
]).
3133

32-
-record(settings, {log_dir = "priv/log" :: string(),
33-
log_max_bytes = 104857600 :: integer(),
34-
log_max_files = 10 :: integer()}).
35-
3634
%%
3735
%% API Functions
3836
%%
@@ -42,11 +40,11 @@
4240
-spec start() -> ok | {error, reason()}.
4341
start() ->
4442
application:load(?MODULE),
45-
Settings = ?PROPS_TO_RECORD(settings(?MODULE), settings),
46-
Path = filename:join(Settings#settings.log_dir, io_lib:format("~s", [node()])),
43+
Settings = ?PROPS_TO_RECORD(settings(fubar_log), settings),
44+
Path = filename:join(Settings#settings.dir, io_lib:format("~s", [node()])),
4745
ok = filelib:ensure_dir(Path++"/"),
4846
error_logger:add_report_handler(
49-
log_mf_h, log_mf_h:init(Path, Settings#settings.log_max_bytes, Settings#settings.log_max_files)),
47+
log_mf_h, log_mf_h:init(Path, Settings#settings.max_bytes, Settings#settings.max_files)),
5048
application:start(?MODULE).
5149

5250
%% @doc Stop application.
@@ -91,12 +89,10 @@ set(Props, Fubar=#fubar{}) ->
9189
Base;
9290
_ ->
9391
Now = now(),
94-
Base#fubar{
95-
origin = stamp(Base#fubar.origin, Now),
92+
Base#fubar{origin = stamp(Base#fubar.origin, Now),
9693
from = stamp(Base#fubar.from, Now),
9794
to = stamp(Base#fubar.to, Now),
98-
via = stamp(Base#fubar.via, Now)
99-
}
95+
via = stamp(Base#fubar.via, Now)}
10096
end.
10197

10298
%% @doc Get a field or fields in a fubar.
@@ -152,19 +148,6 @@ timestamp(via, #fubar{via={_, Value}}) ->
152148
timestamp(_, #fubar{}) ->
153149
undefined.
154150

155-
%% @doc Leave profiling log.
156-
%% @sample fubar:trace(?MODULE, Fubar).
157-
-spec trace(term(), #fubar{}) -> ok.
158-
trace(_, #fubar{id=undefined}) ->
159-
ok;
160-
trace(Tag, #fubar{id=Id, origin={Origin, T1}, from={From, T2}, via={Via, T3}}) ->
161-
Now = now(),
162-
?INFO([{'PROFILE', Tag},
163-
{"id", Id},
164-
{"since origin", Origin, timer:now_diff(Now, T1)/1000},
165-
{"since from", From, timer:now_diff(Now, T2)/1000},
166-
{"since via", Via, timer:now_diff(Now, T3)/1000}]).
167-
168151
%% @doc Get all the attributes of a name in current runtime environment and invoke.
169152
%% The attributes must be a function form, i.e {M, F, A}, {F, A} or F where M must
170153
%% be a module name, F must be a function name and A must be a list of arguments.

src/fubar_app.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
%% Includes
1717
%%
1818
-include("fubar.hrl").
19-
-include("log.hrl").
19+
-include("sasl_log.hrl").
2020
-include("props_to_record.hrl").
2121

2222
%%

src/fubar_log.erl

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
%%% -------------------------------------------------------------------
2+
%%% Author : Sungjin Park <[email protected]>
3+
%%%
4+
%%% Description : Fubar log manager.
5+
%%%
6+
%%% Created : Nov 30, 2012
7+
%%% -------------------------------------------------------------------
8+
-module(fubar_log).
9+
-author("Sungjin Park <[email protected]>").
10+
-behavior(gen_server).
11+
12+
%%
13+
%% Includes
14+
%%
15+
-ifdef(TEST).
16+
-include_lib("eunit/include/eunit.hrl").
17+
-endif.
18+
19+
-include("fubar.hrl").
20+
-include("sasl_log.hrl").
21+
-include("props_to_record.hrl").
22+
23+
%%
24+
%% Macros, records and types
25+
%%
26+
-record(?MODULE, {dir = "priv/log" :: string(),
27+
max_bytes = 10485760 :: integer(),
28+
max_files = 10 :: integer(),
29+
classes = [] :: [{atom(), term(), null | standard_io | pid()}],
30+
interval = 500 :: timeout()}).
31+
32+
%%
33+
%% Exports
34+
%%
35+
-export([start_link/0, log/3, trace/2, dump/2,
36+
open/1, close/1, show/1, hide/1, interval/1, state/0]).
37+
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
38+
39+
%% @doc Start a log manager.
40+
%% The log manager process manages disk_logs and polls them.
41+
-spec start_link() -> {ok, pid()} | {error, reason()}.
42+
start_link() ->
43+
State = ?PROPS_TO_RECORD(fubar:settings(?MODULE), ?MODULE),
44+
Path = filename:join(State#?MODULE.dir, io_lib:format("~s", [node()])),
45+
ok = filelib:ensure_dir(Path++"/"),
46+
gen_server:start({local, ?MODULE}, ?MODULE, State#?MODULE{dir=Path}, []).
47+
48+
%% @doc Leave a log.
49+
%% The log is dropped unless the class is open in advance.
50+
%% @sample fubar_log:log(debug, my_module, Term).
51+
-spec log(atom(), term(), term()) -> ok | {error, reason()}.
52+
log(Class, Tag, Term) ->
53+
Now = now(),
54+
Calendar = calendar:now_to_universal_time(Now),
55+
Timestamp = httpd_util:rfc1123_date(Calendar),
56+
catch disk_log:log(Class, {{Class, Timestamp}, {Tag, self()}, Term}).
57+
58+
%% @doc Leave a special trace type log.
59+
%% @sample fubar_log:trace(my_module, Fubar).
60+
-spec trace(term(), #fubar{}) -> ok.
61+
trace(_, #fubar{id=undefined}) ->
62+
ok;
63+
trace(Tag, #fubar{id=Id, origin={Origin, T1}, from={From, T2}, via={Via, T3}, payload=Payload}) ->
64+
Now = now(),
65+
Calendar = calendar:now_to_universal_time(Now),
66+
Timestamp = httpd_util:rfc1123_date(Calendar),
67+
catch disk_log:log(trace, {{'TRACE', Timestamp}, {Tag, self()},
68+
{fubar, Id},
69+
{since, {Origin, timer:now_diff(Now, T1)/1000},
70+
{From, timer:now_diff(Now, T2)/1000},
71+
{Via, timer:now_diff(Now, T3)/1000}},
72+
{payload, Payload}}).
73+
74+
%% @doc Dump a log class as a text file.
75+
-spec dump(atom(), string()) -> ok.
76+
dump(Class, Path) ->
77+
case state() of
78+
#?MODULE{dir=Dir} ->
79+
case file:open(Path, [write]) of
80+
{ok, File} ->
81+
LogFile = filename:join(Dir, io_lib:format("~s", [Class])),
82+
disk_log:open([{name, Class}, {file, LogFile}]),
83+
consume_log(Class, start, File),
84+
disk_log:close(Class),
85+
file:close(File);
86+
Error1 ->
87+
Error1
88+
end;
89+
Error ->
90+
Error
91+
end.
92+
93+
%% @doc Open a log class.
94+
%% Opening a log class doesn't mean the logs in the class is shown in tty.
95+
%% Need to call show/1 explicitly to do that.
96+
-spec open(atom()) -> ok.
97+
open(Class) ->
98+
gen_server:call(?MODULE, {open, Class}).
99+
100+
%% @doc Close a log class.
101+
%% Closing a log class mean that the logs in the class is no longer stored.
102+
-spec close(atom()) -> ok.
103+
close(Class) ->
104+
gen_server:call(?MODULE, {close, Class}).
105+
106+
%% @doc Print logs in a class to tty.
107+
-spec show(atom()) -> ok.
108+
show(Class) ->
109+
gen_server:call(?MODULE, {show, Class}).
110+
111+
%% @doc Hide logs in a class from tty.
112+
-spec hide(atom()) -> ok.
113+
hide(Class) ->
114+
gen_server:call(?MODULE, {hide, Class}).
115+
116+
%% @doc Set tty refresh interval.
117+
-spec interval(timeout()) -> ok.
118+
interval(T) ->
119+
gen_server:call(?MODULE, {interval, T}).
120+
121+
%% @doc Get the log manager state.
122+
-spec state() -> #?MODULE{}.
123+
state() ->
124+
gen_server:call(?MODULE, state).
125+
126+
%%
127+
%% Callback Functions
128+
%%
129+
init(State=#?MODULE{dir=Dir, max_bytes=L, max_files=N, classes=Classes, interval=T}) ->
130+
?DEBUG([init, State]),
131+
Init = fun(Class) -> open(Class, Dir, L, N) end,
132+
{ok, State#?MODULE{classes=lists:map(Init, Classes)}, T}.
133+
134+
handle_call({open, Class}, _, State=#?MODULE{dir=Dir, max_bytes=L, max_files=N, interval=T}) ->
135+
open(Class, Dir, L, N),
136+
{reply, ok, State, T};
137+
handle_call({close, Class}, _, State=#?MODULE{classes=Classes, interval=T}) ->
138+
Result = disk_log:close(Class),
139+
NewClasses = case lists:keytake(Class, 1, Classes) of
140+
{value, {Class, _, _}, Rest} -> Rest;
141+
false -> Classes
142+
end,
143+
{reply, Result, State#?MODULE{classes=NewClasses}, T};
144+
handle_call({show, Class}, _, State=#?MODULE{classes=Classes, interval=T}) ->
145+
case lists:keytake(Class, 1, Classes) of
146+
{value, {Class, Last, _}, Rest} ->
147+
Current = consume_log(Class, Last, null),
148+
{reply, ok, State#?MODULE{classes=[{Class, Current, standard_io} | Rest]}, T};
149+
false ->
150+
Current = consume_log(Class, start, null),
151+
{reply, ok, State#?MODULE{classes=[{Class, Current, standard_io} | Classes]}, T}
152+
end;
153+
handle_call({hide, Class}, _, State=#?MODULE{classes=Classes, interval=T}) ->
154+
case lists:keytake(Class, 1, Classes) of
155+
{value, {Class, Current, _}, Rest} ->
156+
{reply, ok, State#?MODULE{classes=[{Class, Current, null} | Rest]}, T};
157+
false ->
158+
{reply, ok, State, T}
159+
end;
160+
handle_call({interval, T}, _, State=#?MODULE{interval=_}) ->
161+
{reply, ok, State#?MODULE{interval=T}, T};
162+
handle_call(state, _, State=#?MODULE{interval=T}) ->
163+
{reply, State, State, T};
164+
handle_call(Request, From, State) ->
165+
?WARNING([handle_call, Request, From, State, "dropping unknown"]),
166+
{reply, ok, State}.
167+
168+
handle_cast(Message, State) ->
169+
?WARNING([handle_cast, Message, State, "dropping unknown"]),
170+
{noreply, State}.
171+
172+
handle_info(timeout, State) ->
173+
F = fun({Class, Last, Show}) ->
174+
Current = consume_log(Class, Last, Show),
175+
{Class, Current, Show}
176+
end,
177+
Classes = lists:map(F, State#?MODULE.classes),
178+
{noreply, State#?MODULE{classes=Classes}, State#?MODULE.interval};
179+
handle_info(Info, State) ->
180+
?WARNING([handle_info, Info, State, "dropping unknown"]),
181+
{noreply, State}.
182+
183+
terminate(Reason, State) ->
184+
?DEBUG([terminate, Reason, State]),
185+
Close = fun({Class, _, _}) ->
186+
disk_log:close(Class)
187+
end,
188+
lists:foreach(Close, State#?MODULE.classes),
189+
Reason.
190+
191+
code_change(OldVsn, State, Extra) ->
192+
?WARNING([code_change, OldVsn, State, Extra]),
193+
{ok, State}.
194+
195+
%%
196+
%% Local Functions
197+
%%
198+
open(Class, Dir, L, N) ->
199+
File = filename:join(Dir, io_lib:format("~s", [Class])),
200+
disk_log:open([{name, Class}, {file, File}, {type, wrap}, {size, {L, N}}]),
201+
Current = consume_log(Class, start, null),
202+
{Class, Current, standard_io}.
203+
204+
consume_log(Log, Last, Io) ->
205+
case disk_log:chunk(Log, Last) of
206+
{error, _} ->
207+
Last;
208+
eof ->
209+
Last;
210+
{Current, Terms} ->
211+
case Io of
212+
null ->
213+
start;
214+
_ ->
215+
Print = fun(Term) -> io:format(Io, "~p~n", [Term]) end,
216+
lists:foreach(Print, Terms)
217+
end,
218+
consume_log(Log, Current, Io)
219+
end.
220+
221+
%%
222+
%% Unit Tests
223+
%%
224+
-ifdef(TEST).
225+
-endif.

src/fubar_route.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
-endif.
2323

2424
-include("fubar.hrl").
25-
-include("log.hrl").
25+
-include("sasl_log.hrl").
2626

2727
%% @doc Routing table schema
2828
-record(?MODULE, {name = '_' :: term(),

src/fubar_sup.erl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@
1414
%%
1515
-include("fubar.hrl").
1616

17-
%%
18-
%% Macros
19-
%%
2017
-define(MAX_R, 3).
2118
-define(MAX_T, 5).
2219

@@ -35,4 +32,5 @@ start_link() ->
3532
%% Supervisor callbacks
3633
%%
3734
init(_) ->
38-
{ok, {{one_for_one, ?MAX_R, ?MAX_T}, []}}.
35+
LogManager = {fubar_log, {fubar_log, start_link, []}, permanent, 10, worker, dynamic},
36+
{ok, {{one_for_one, ?MAX_R, ?MAX_T}, [LogManager]}}.

src/mqtt_account.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
-endif.
1717

1818
-include("fubar.hrl").
19-
-include("log.hrl").
19+
-include("sasl_log.hrl").
2020

2121
%%
2222
%% Records

src/mqtt_client.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
-include("fubar.hrl").
1919
-include("mqtt.hrl").
20-
-include("log.hrl").
20+
-include("sasl_log.hrl").
2121
-include("props_to_record.hrl").
2222

2323
%%

src/mqtt_protocol.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
-include("fubar.hrl").
2828
-include("mqtt.hrl").
29-
-include("log.hrl").
29+
-include("sasl_log.hrl").
3030
-include("props_to_record.hrl").
3131

3232
%%

0 commit comments

Comments
 (0)