@ -0,0 +1,178 @@ | |||
Apache License | |||
Version 2.0, January 2004 | |||
http://www.apache.org/licenses/ | |||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
1. Definitions. | |||
"License" shall mean the terms and conditions for use, reproduction, | |||
and distribution as defined by Sections 1 through 9 of this document. | |||
"Licensor" shall mean the copyright owner or entity authorized by | |||
the copyright owner that is granting the License. | |||
"Legal Entity" shall mean the union of the acting entity and all | |||
other entities that control, are controlled by, or are under common | |||
control with that entity. For the purposes of this definition, | |||
"control" means (i) the power, direct or indirect, to cause the | |||
direction or management of such entity, whether by contract or | |||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
outstanding shares, or (iii) beneficial ownership of such entity. | |||
"You" (or "Your") shall mean an individual or Legal Entity | |||
exercising permissions granted by this License. | |||
"Source" form shall mean the preferred form for making modifications, | |||
including but not limited to software source code, documentation | |||
source, and configuration files. | |||
"Object" form shall mean any form resulting from mechanical | |||
transformation or translation of a Source form, including but | |||
not limited to compiled object code, generated documentation, | |||
and conversions to other media types. | |||
"Work" shall mean the work of authorship, whether in Source or | |||
Object form, made available under the License, as indicated by a | |||
copyright notice that is included in or attached to the work | |||
(an example is provided in the Appendix below). | |||
"Derivative Works" shall mean any work, whether in Source or Object | |||
form, that is based on (or derived from) the Work and for which the | |||
editorial revisions, annotations, elaborations, or other modifications | |||
represent, as a whole, an original work of authorship. For the purposes | |||
of this License, Derivative Works shall not include works that remain | |||
separable from, or merely link (or bind by name) to the interfaces of, | |||
the Work and Derivative Works thereof. | |||
"Contribution" shall mean any work of authorship, including | |||
the original version of the Work and any modifications or additions | |||
to that Work or Derivative Works thereof, that is intentionally | |||
submitted to Licensor for inclusion in the Work by the copyright owner | |||
or by an individual or Legal Entity authorized to submit on behalf of | |||
the copyright owner. For the purposes of this definition, "submitted" | |||
means any form of electronic, verbal, or written communication sent | |||
to the Licensor or its representatives, including but not limited to | |||
communication on electronic mailing lists, source code control systems, | |||
and issue tracking systems that are managed by, or on behalf of, the | |||
Licensor for the purpose of discussing and improving the Work, but | |||
excluding communication that is conspicuously marked or otherwise | |||
designated in writing by the copyright owner as "Not a Contribution." | |||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||
on behalf of whom a Contribution has been received by Licensor and | |||
subsequently incorporated within the Work. | |||
2. Grant of Copyright License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
copyright license to reproduce, prepare Derivative Works of, | |||
publicly display, publicly perform, sublicense, and distribute the | |||
Work and such Derivative Works in Source or Object form. | |||
3. Grant of Patent License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
(except as stated in this section) patent license to make, have made, | |||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||
where such license applies only to those patent claims licensable | |||
by such Contributor that are necessarily infringed by their | |||
Contribution(s) alone or by combination of their Contribution(s) | |||
with the Work to which such Contribution(s) was submitted. If You | |||
institute patent litigation against any entity (including a | |||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
or a Contribution incorporated within the Work constitutes direct | |||
or contributory patent infringement, then any patent licenses | |||
granted to You under this License for that Work shall terminate | |||
as of the date such litigation is filed. | |||
4. Redistribution. You may reproduce and distribute copies of the | |||
Work or Derivative Works thereof in any medium, with or without | |||
modifications, and in Source or Object form, provided that You | |||
meet the following conditions: | |||
(a) You must give any other recipients of the Work or | |||
Derivative Works a copy of this License; and | |||
(b) You must cause any modified files to carry prominent notices | |||
stating that You changed the files; and | |||
(c) You must retain, in the Source form of any Derivative Works | |||
that You distribute, all copyright, patent, trademark, and | |||
attribution notices from the Source form of the Work, | |||
excluding those notices that do not pertain to any part of | |||
the Derivative Works; and | |||
(d) If the Work includes a "NOTICE" text file as part of its | |||
distribution, then any Derivative Works that You distribute must | |||
include a readable copy of the attribution notices contained | |||
within such NOTICE file, excluding those notices that do not | |||
pertain to any part of the Derivative Works, in at least one | |||
of the following places: within a NOTICE text file distributed | |||
as part of the Derivative Works; within the Source form or | |||
documentation, if provided along with the Derivative Works; or, | |||
within a display generated by the Derivative Works, if and | |||
wherever such third-party notices normally appear. The contents | |||
of the NOTICE file are for informational purposes only and | |||
do not modify the License. You may add Your own attribution | |||
notices within Derivative Works that You distribute, alongside | |||
or as an addendum to the NOTICE text from the Work, provided | |||
that such additional attribution notices cannot be construed | |||
as modifying the License. | |||
You may add Your own copyright statement to Your modifications and | |||
may provide additional or different license terms and conditions | |||
for use, reproduction, or distribution of Your modifications, or | |||
for any such Derivative Works as a whole, provided Your use, | |||
reproduction, and distribution of the Work otherwise complies with | |||
the conditions stated in this License. | |||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||
any Contribution intentionally submitted for inclusion in the Work | |||
by You to the Licensor shall be under the terms and conditions of | |||
this License, without any additional terms or conditions. | |||
Notwithstanding the above, nothing herein shall supersede or modify | |||
the terms of any separate license agreement you may have executed | |||
with Licensor regarding such Contributions. | |||
6. Trademarks. This License does not grant permission to use the trade | |||
names, trademarks, service marks, or product names of the Licensor, | |||
except as required for reasonable and customary use in describing the | |||
origin of the Work and reproducing the content of the NOTICE file. | |||
7. Disclaimer of Warranty. Unless required by applicable law or | |||
agreed to in writing, Licensor provides the Work (and each | |||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
implied, including, without limitation, any warranties or conditions | |||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||
appropriateness of using or redistributing the Work and assume any | |||
risks associated with Your exercise of permissions under this License. | |||
8. Limitation of Liability. In no event and under no legal theory, | |||
whether in tort (including negligence), contract, or otherwise, | |||
unless required by applicable law (such as deliberate and grossly | |||
negligent acts) or agreed to in writing, shall any Contributor be | |||
liable to You for damages, including any direct, indirect, special, | |||
incidental, or consequential damages of any character arising as a | |||
result of this License or out of the use or inability to use the | |||
Work (including but not limited to damages for loss of goodwill, | |||
work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses), even if such Contributor | |||
has been advised of the possibility of such damages. | |||
9. Accepting Warranty or Additional Liability. While redistributing | |||
the Work or Derivative Works thereof, You may choose to offer, | |||
and charge a fee for, acceptance of support, warranty, indemnity, | |||
or other liability obligations and/or rights consistent with this | |||
License. However, in accepting such obligations, You may act only | |||
on Your own behalf and on Your sole responsibility, not on behalf | |||
of any other Contributor, and only if You agree to indemnify, | |||
defend, and hold each Contributor harmless for any liability | |||
incurred by, or claims asserted against, such Contributor by reason | |||
of your accepting any such warranty or additional liability. | |||
END OF TERMS AND CONDITIONS | |||
@ -0,0 +1,8 @@ | |||
all: | |||
./rebar compile | |||
clean: | |||
./rebar clean | |||
test: all | |||
./rebar eunit |
@ -0,0 +1,41 @@ | |||
* Overview | |||
Lager (pronounced lAAger) is a logging framework for Erlang. Its purpose is | |||
to provide a more traditional way to perform logging in an erlang application | |||
that plays nicely with traditional UNIX logging tools like logrotate and | |||
syslog. | |||
Features | |||
- Finer grained log levels (debug, info, notice, warning, error, critical, | |||
alert, emergency) | |||
- Logger calls are transformed using a parse transform to allow capturing | |||
Module/Function/Line/Pid information | |||
- When no handler is consuming a log level (eg. debug) no event is even sent | |||
to the log handler | |||
- Supports multiple backends, including console, file and syslog. | |||
* Usage | |||
To use lager in your application, you need to define it as a rebar dep or have | |||
some other way of including it in erlang's path. You can then add the | |||
following option to the erlang compiler flags | |||
{parse_transform, lager_transform} | |||
Alternately, you can add it to the module you which to compile with logging | |||
enabled: | |||
-compile([{parse_transform, lager_transform}]). | |||
Once you have built your code with lager, you can then generate log messages | |||
by doing the following: | |||
lager:error("Some message") | |||
Or: | |||
lager:warning("Some message with a term: ~p", [Term]) | |||
The general form is lager:Severity() where Severity is one of the log levels | |||
mentioned above. | |||
* Configuration | |||
TODO |
@ -0,0 +1,2 @@ | |||
{erl_opts, [debug_info, fail_on_warning]}. |
@ -0,0 +1,18 @@ | |||
%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*- | |||
%% ex: ts=4 sw=4 et | |||
{application, lager, | |||
[ | |||
{description, "Custom error handler"}, | |||
{vsn, "1.0.0"}, | |||
{modules, [ | |||
]}, | |||
{applications, [ | |||
kernel, | |||
stdlib, | |||
sasl | |||
]}, | |||
{registered, []}, | |||
{mod, {riak_err_app, []}}, | |||
{env, [ | |||
]} | |||
]}. |
@ -0,0 +1,109 @@ | |||
%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. | |||
%% | |||
%% This file is provided to you under the Apache License, | |||
%% Version 2.0 (the "License"); you may not use this file | |||
%% except in compliance with the License. You may obtain | |||
%% a copy of the License at | |||
%% | |||
%% http://www.apache.org/licenses/LICENSE-2.0 | |||
%% | |||
%% Unless required by applicable law or agreed to in writing, | |||
%% software distributed under the License is distributed on an | |||
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||
%% KIND, either express or implied. See the License for the | |||
%% specific language governing permissions and limitations | |||
%% under the License. | |||
-module(lager). | |||
-behaviour(gen_server). | |||
-export([start_link/0,start/0,sasl_log/3, log/7, log/8]). | |||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, | |||
code_change/3]). | |||
-record(state, {event_pid, handler_loglevels}). | |||
%% API | |||
start_link() -> | |||
%application:start(sasl), | |||
%application:set_env(riak_err, print_fun, {lager, sasl_log}), | |||
%application:start(riak_err), | |||
Handlers = case application:get_env(lager, handlers) of | |||
undefined -> | |||
[{lager_console_backend, [info]}]; | |||
Val -> | |||
Val | |||
end, | |||
gen_server:start_link({local, ?MODULE}, ?MODULE, [Handlers], []). | |||
start() -> | |||
Handlers = case application:get_env(lager, handlers) of | |||
undefined -> | |||
[{lager_console_backend, [info]}, | |||
{lager_file_backend, [{"error.log", error}, {"console.log", info}]}]; | |||
Val -> | |||
Val | |||
end, | |||
gen_server:start({local, ?MODULE}, ?MODULE, [Handlers], []). | |||
sasl_log(ReportStr, Pid, Message) -> | |||
%Level = reportstr_to_level(ReportStr), | |||
{{Y, M, D}, {H, Mi, S}} = riak_err_stdlib:maybe_utc(erlang:localtime()), | |||
Msg = re:replace(binary:replace(Message, [<<"\r">>, <<"\n">>], <<>>, | |||
[global]), " +", " ", [global, {return, binary}]), | |||
io:format("~b-~b-~b ~b:~b:~b ~p ~s ~s~n", [Y, M, | |||
D, H, Mi, S, Pid, ReportStr, Msg]). | |||
log(Level, Module, Function, Line, Pid, {{Y, M, D}, {H, Mi, S}}, Message) -> | |||
Time = io_lib:format("~b-~b-~b ~b:~b:~b", [Y, M, D, H, Mi, S]), | |||
Msg = io_lib:format("[~p] ~p@~p:~p:~p ~s", [Level, Pid, Module, | |||
Function, Line, Message]), | |||
gen_event:notify(lager_event, {log, lager_util:level_to_num(Level), Time, Msg}). | |||
log(Level, Module, Function, Line, Pid, {{Y, M, D}, {H, Mi, S}}, Format, Args) -> | |||
Time = io_lib:format("~b-~b-~b ~b:~b:~b", [Y, M, D, H, Mi, S]), | |||
Msg = io_lib:format("[~p] ~p@~p:~p:~p ~s", [Level, Pid, Module, | |||
Function, Line, io_lib:format(Format, Args)]), | |||
gen_event:notify(lager_event, {log, lager_util:level_to_num(Level), Time, Msg}). | |||
%% gen_server callbacks | |||
init([Handlers]) -> | |||
%% start a gen_event linked to this process | |||
gen_event:start_link({local, lager_event}), | |||
%% spin up all the defined handlers | |||
[gen_event:add_sup_handler(lager_event, Module, Args) || {Module, Args} <- Handlers], | |||
MinLog = minimum_log_level(get_log_levels()), | |||
lager_mochiglobal:put(loglevel, MinLog), | |||
{ok, #state{}}. | |||
handle_call(_Request, _From, State) -> | |||
{reply, ok, State}. | |||
handle_cast(_Request, State) -> | |||
{noreply, State}. | |||
handle_info(Info, State) -> | |||
io:format("got info ~p~n", [Info]), | |||
{noreply, State}. | |||
terminate(_Reason, _State) -> | |||
ok. | |||
code_change(_OldVsn, State, _Extra) -> | |||
{ok, State}. | |||
%% internal functions | |||
get_log_levels() -> | |||
[gen_event:call(lager_event, Handler, get_loglevel) || | |||
Handler <- gen_event:which_handlers(lager_event)]. | |||
minimum_log_level([]) -> | |||
9; %% higher than any log level, logging off | |||
minimum_log_level(Levels) -> | |||
erlang:hd(lists:sort(lists:map(fun(Level) -> lager_util:level_to_num(Level) end, Levels))). |
@ -0,0 +1,47 @@ | |||
%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. | |||
%% | |||
%% This file is provided to you under the Apache License, | |||
%% Version 2.0 (the "License"); you may not use this file | |||
%% except in compliance with the License. You may obtain | |||
%% a copy of the License at | |||
%% | |||
%% http://www.apache.org/licenses/LICENSE-2.0 | |||
%% | |||
%% Unless required by applicable law or agreed to in writing, | |||
%% software distributed under the License is distributed on an | |||
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||
%% KIND, either express or implied. See the License for the | |||
%% specific language governing permissions and limitations | |||
%% under the License. | |||
-module(lager_console_backend). | |||
-behaviour(gen_event). | |||
-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, | |||
code_change/3]). | |||
-record(state, {level, nlevel}). | |||
init([Level]) -> | |||
{ok, #state{level=Level, nlevel=lager_util:level_to_num(Level)}}. | |||
handle_call(get_loglevel, #state{level=Level} = State) -> | |||
{ok, Level, State}; | |||
handle_call(_Request, State) -> | |||
{ok, ok, State}. | |||
handle_event({log, Level, Time, Message}, #state{nlevel=LogLevel} = State) when Level >= LogLevel -> | |||
io:put_chars([Time, " ", Message, "\n"]), | |||
{ok, State}; | |||
handle_event(_Event, State) -> | |||
{ok, State}. | |||
handle_info(_Info, State) -> | |||
{ok, State}. | |||
terminate(_Reason, _State) -> | |||
ok. | |||
code_change(_OldVsn, State, _Extra) -> | |||
{ok, State}. |
@ -0,0 +1,165 @@ | |||
%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. | |||
%% | |||
%% This file is provided to you under the Apache License, | |||
%% Version 2.0 (the "License"); you may not use this file | |||
%% except in compliance with the License. You may obtain | |||
%% a copy of the License at | |||
%% | |||
%% http://www.apache.org/licenses/LICENSE-2.0 | |||
%% | |||
%% Unless required by applicable law or agreed to in writing, | |||
%% software distributed under the License is distributed on an | |||
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||
%% KIND, either express or implied. See the License for the | |||
%% specific language governing permissions and limitations | |||
%% under the License. | |||
-module(lager_file_backend). | |||
-behaviour(gen_event). | |||
-ifdef(TEST). | |||
-include_lib("eunit/include/eunit.hrl"). | |||
-endif. | |||
-include_lib("kernel/include/file.hrl"). | |||
-compile([{parse_transform, lager_transform}]). | |||
-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, | |||
code_change/3]). | |||
-record(state, {files}). | |||
init(LogFiles) -> | |||
Files = [begin | |||
case open(Name) of | |||
{ok, {FD, Inode}} -> | |||
{Name, lager_util:level_to_num(Level), FD, Inode}; | |||
Error -> | |||
lager:error("Failed to open log file ~s with error ~p", | |||
[Name, Error]), | |||
undefined | |||
end | |||
end || | |||
{Name, Level} <- LogFiles], | |||
{ok, #state{files=Files}}. | |||
handle_call(get_loglevel, #state{files=Files} = State) -> | |||
Result = lists:foldl(fun({_, Level, _, _}, L) -> erlang:min(Level, L); | |||
(_, L) -> L end, 9, | |||
Files), | |||
{ok, Result, State}; | |||
handle_call(_Request, State) -> | |||
{ok, ok, State}. | |||
handle_event({log, Level, Time, Message}, #state{files=Files} = State) -> | |||
NewFiles = lists:map( | |||
fun({_, L, _, _} = File) when Level >= L -> | |||
write(File, Level, [Time, " ", Message, "\n"]); | |||
(File) -> | |||
File | |||
end, Files), | |||
{ok, State#state{files=NewFiles}}; | |||
handle_event(_Event, State) -> | |||
{ok, State}. | |||
handle_info(_Info, State) -> | |||
{ok, State}. | |||
terminate(_Reason, _State) -> | |||
ok. | |||
code_change(_OldVsn, State, _Extra) -> | |||
{ok, State}. | |||
open(Name) -> | |||
case file:open(Name, [append, delayed_write, raw]) of | |||
{ok, FD} -> | |||
case file:read_file_info(Name) of | |||
{ok, FInfo} -> | |||
Inode = FInfo#file_info.inode, | |||
{ok, {FD, Inode}}; | |||
X -> X | |||
end; | |||
Y -> Y | |||
end. | |||
write({Name, L, FD, Inode}, Level, Msg) -> | |||
Result = case file:read_file_info(Name) of | |||
{ok, FInfo} -> | |||
Inode2 = FInfo#file_info.inode, | |||
case Inode == Inode2 of | |||
true -> | |||
{FD, Inode}; | |||
false -> | |||
case open(Name) of | |||
{ok, {FD2, Inode3}} -> | |||
%% inode changed, file was probably moved and | |||
%% recreated | |||
{FD2, Inode3}; | |||
Error -> | |||
lager:error("Failed to reopen file ~s with error ~p", | |||
[Name, Error]), | |||
undefined | |||
end | |||
end; | |||
_ -> | |||
case open(Name) of | |||
{ok, {FD2, Inode3}} -> | |||
%% file was removed | |||
{FD2, Inode3}; | |||
Error -> | |||
lager:error("Failed to reopen file ~s with error ~p", | |||
[Name, Error]), | |||
undefined | |||
end | |||
end, | |||
case Result of | |||
{NewFD, NewInode} -> | |||
file:write(NewFD, Msg), | |||
case Level of | |||
_ when Level >= 4 -> | |||
%% force a sync on any message at error severity or above | |||
file:datasync(NewFD); | |||
_ -> ok | |||
end, | |||
{Name, L, NewFD, NewInode}; | |||
_ -> | |||
undefined | |||
end. | |||
-ifdef(TEST). | |||
get_loglevel_test() -> | |||
{ok, Level, _} = handle_call(get_loglevel, | |||
#state{files=[ | |||
{"foo", lager_util:level_to_num(warning), 0, 0}, | |||
{"bar", lager_util:level_to_num(info), 0, 0}]}), | |||
?assertEqual(Level, lager_util:level_to_num(info)), | |||
{ok, Level2, _} = handle_call(get_loglevel, | |||
#state{files=[ | |||
{"foo", lager_util:level_to_num(warning), 0, 0}, | |||
{"foo", lager_util:level_to_num(critical), 0, 0}, | |||
{"bar", lager_util:level_to_num(error), 0, 0}]}), | |||
?assertEqual(Level2, lager_util:level_to_num(warning)). | |||
rotation_test() -> | |||
{ok, {FD, Inode}} = open("test.log"), | |||
?assertEqual({"test.log", 0, FD, Inode}, | |||
write({"test.log", 0, FD, Inode}, 0, "hello world")), | |||
file:delete("test.log"), | |||
Result = write({"test.log", 0, FD, Inode}, 0, "hello world"), | |||
%% assert file has changed | |||
?assert({"test.log", 0, FD, Inode} =/= Result), | |||
?assertMatch({"test.log", 0, _, _}, Result), | |||
file:rename("test.log", "test.log.1"), | |||
Result2 = write(Result, 0, "hello world"), | |||
%% assert file has changed | |||
?assert(Result =/= Result2), | |||
?assertMatch({"test.log", 0, _, _}, Result2), | |||
ok. | |||
-endif. | |||
@ -0,0 +1,107 @@ | |||
%% @author Bob Ippolito <bob@mochimedia.com> | |||
%% @copyright 2010 Mochi Media, Inc. | |||
%% @doc Abuse module constant pools as a "read-only shared heap" (since erts 5.6) | |||
%% <a href="http://www.erlang.org/pipermail/erlang-questions/2009-March/042503.html">[1]</a>. | |||
-module(lager_mochiglobal). | |||
-author("Bob Ippolito <bob@mochimedia.com>"). | |||
-export([get/1, get/2, put/2, delete/1]). | |||
-spec get(atom()) -> any() | undefined. | |||
%% @equiv get(K, undefined) | |||
get(K) -> | |||
get(K, undefined). | |||
-spec get(atom(), T) -> any() | T. | |||
%% @doc Get the term for K or return Default. | |||
get(K, Default) -> | |||
get(K, Default, key_to_module(K)). | |||
get(_K, Default, Mod) -> | |||
try Mod:term() | |||
catch error:undef -> | |||
Default | |||
end. | |||
-spec put(atom(), any()) -> ok. | |||
%% @doc Store term V at K, replaces an existing term if present. | |||
put(K, V) -> | |||
put(K, V, key_to_module(K)). | |||
put(_K, V, Mod) -> | |||
Bin = compile(Mod, V), | |||
code:purge(Mod), | |||
{module, Mod} = code:load_binary(Mod, atom_to_list(Mod) ++ ".erl", Bin), | |||
ok. | |||
-spec delete(atom()) -> boolean(). | |||
%% @doc Delete term stored at K, no-op if non-existent. | |||
delete(K) -> | |||
delete(K, key_to_module(K)). | |||
delete(_K, Mod) -> | |||
code:purge(Mod), | |||
code:delete(Mod). | |||
-spec key_to_module(atom()) -> atom(). | |||
key_to_module(K) -> | |||
list_to_atom("mochiglobal:" ++ atom_to_list(K)). | |||
-spec compile(atom(), any()) -> binary(). | |||
compile(Module, T) -> | |||
{ok, Module, Bin} = compile:forms(forms(Module, T), | |||
[verbose, report_errors]), | |||
Bin. | |||
-spec forms(atom(), any()) -> [erl_syntax:syntaxTree()]. | |||
forms(Module, T) -> | |||
[erl_syntax:revert(X) || X <- term_to_abstract(Module, term, T)]. | |||
-spec term_to_abstract(atom(), atom(), any()) -> [erl_syntax:syntaxTree()]. | |||
term_to_abstract(Module, Getter, T) -> | |||
[%% -module(Module). | |||
erl_syntax:attribute( | |||
erl_syntax:atom(module), | |||
[erl_syntax:atom(Module)]), | |||
%% -export([Getter/0]). | |||
erl_syntax:attribute( | |||
erl_syntax:atom(export), | |||
[erl_syntax:list( | |||
[erl_syntax:arity_qualifier( | |||
erl_syntax:atom(Getter), | |||
erl_syntax:integer(0))])]), | |||
%% Getter() -> T. | |||
erl_syntax:function( | |||
erl_syntax:atom(Getter), | |||
[erl_syntax:clause([], none, [erl_syntax:abstract(T)])])]. | |||
%% | |||
%% Tests | |||
%% | |||
-ifdef(TEST). | |||
-include_lib("eunit/include/eunit.hrl"). | |||
get_put_delete_test() -> | |||
K = '$$test$$mochiglobal', | |||
delete(K), | |||
?assertEqual( | |||
bar, | |||
get(K, bar)), | |||
try | |||
?MODULE:put(K, baz), | |||
?assertEqual( | |||
baz, | |||
get(K, bar)), | |||
?MODULE:put(K, wibble), | |||
?assertEqual( | |||
wibble, | |||
?MODULE:get(K)) | |||
after | |||
delete(K) | |||
end, | |||
?assertEqual( | |||
bar, | |||
get(K, bar)), | |||
?assertEqual( | |||
undefined, | |||
?MODULE:get(K)), | |||
ok. | |||
-endif. |
@ -0,0 +1,120 @@ | |||
%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. | |||
%% | |||
%% This file is provided to you under the Apache License, | |||
%% Version 2.0 (the "License"); you may not use this file | |||
%% except in compliance with the License. You may obtain | |||
%% a copy of the License at | |||
%% | |||
%% http://www.apache.org/licenses/LICENSE-2.0 | |||
%% | |||
%% Unless required by applicable law or agreed to in writing, | |||
%% software distributed under the License is distributed on an | |||
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||
%% KIND, either express or implied. See the License for the | |||
%% specific language governing permissions and limitations | |||
%% under the License. | |||
-module(lager_transform). | |||
-export([parse_transform/2]). | |||
-define(LEVELS, [debug, info, notice, warning, error, critical, alert, | |||
emergency]). | |||
%% This parse transform rewrites functions calls to lager:Severity/1,2 into | |||
%% a more complicated function that captures module, function, line, pid and | |||
%% time as well. The entire function call is then wrapped in a case that | |||
%$ checks the mochiglobal 'loglevel' value, so the code isn't executed if | |||
%% nothing wishes to consume the message. | |||
parse_transform(AST, _Options) -> | |||
%io:format("~n~p~n", [AST]), | |||
walk_ast([], AST). | |||
walk_ast(Acc, []) -> | |||
lists:reverse(Acc); | |||
walk_ast(Acc, [{attribute, _, module, Module}=H|T]) -> | |||
put(module, Module), | |||
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, [H|T]) -> | |||
walk_ast([H|Acc], T). | |||
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). | |||
walk_body(Acc, []) -> | |||
lists:reverse(Acc); | |||
walk_body(Acc, [H|T]) -> | |||
walk_body([transform_statement(H)|Acc], T). | |||
transform_statement({call, Line, {remote, Line1, {atom, Line2, lager}, | |||
{atom, Line3, Severity}}, Arguments} = Stmt) -> | |||
case lists:member(Severity, ?LEVELS) of | |||
true -> | |||
%io:format("call to lager ~p on line ~p in function ~p in module ~p~n", | |||
%[Severity, Line, get(function), get(module)]), | |||
%% a case to check the mochiglobal 'loglevel' key against the | |||
%% message we're trying to log | |||
{'case',Line, | |||
{op,Line,'=<', | |||
{call,Line, | |||
{remote,Line,{atom,Line,lager_util},{atom,Line,level_to_num}}, | |||
[{call,Line, | |||
{remote,Line,{atom,Line,lager_mochiglobal},{atom,Line,get}}, | |||
[{atom,Line,loglevel}]}]}, | |||
{call,Line, | |||
{remote,Line,{atom,Line,lager_util},{atom,Line,level_to_num}}, | |||
[{atom,Line,Severity}]}}, | |||
[{clause,Line, | |||
[{atom,Line,true}], %% yes, we log! | |||
[], | |||
[{call, Line, {remote, Line1, {atom, Line2, lager}, | |||
{atom, Line3, log}}, [ | |||
{atom, Line3, Severity}, | |||
{atom, Line3, get(module)}, | |||
{atom, Line3, get(function)}, | |||
{integer, Line3, Line}, | |||
{call, Line3, {atom, Line3 ,self}, []}, | |||
{call, Line3, {remote, Line3, | |||
{atom, Line3 ,riak_err_stdlib}, | |||
{atom,Line3,maybe_utc}}, | |||
[{call,Line3,{remote,Line3, | |||
{atom,Line3,erlang}, | |||
{atom,Line3,localtime}},[]}]} | |||
| Arguments | |||
]}]}, | |||
%% No, don't log | |||
{clause,Line3,[{var,Line3,'_'}],[],[{atom,Line3,ok}]}]}; | |||
false -> | |||
%io:format("skipping non-log lager call ~p~n", [Severity]), | |||
Stmt | |||
end; | |||
transform_statement({call, Line, {remote, Line1, {atom, Line2, boston_lager}, | |||
{atom, Line3, Severity}}, Arguments}) -> | |||
NewArgs = case Arguments of | |||
[{string, L, Msg}] -> [{string, L, re:replace(Msg, "r", "h", [{return, list}, global])}]; | |||
[{string, L, Format}, Args] -> [{string, L, re:replace(Format, "r", "h", [{return, list}, global])}, Args]; | |||
Other -> Other | |||
end, | |||
transform_statement({call, Line, {remote, Line1, {atom, Line2, lager}, | |||
{atom, Line3, Severity}}, NewArgs}); | |||
transform_statement({'case', Line, Expr, Clauses}) -> | |||
{'case', Line, Expr, walk_clauses([], Clauses)}; | |||
transform_statement({'if', Line, Clauses}) -> | |||
{'if', Line, walk_clauses([], Clauses)}; | |||
transform_statement({block, Line, Body}) -> | |||
{block, Line, walk_body([], Body)}; | |||
transform_statement({lc, Line, Expression, Generator}) -> | |||
{lc, Line, transform_statement(Expression), Generator}; | |||
transform_statement({match, Line, Var, Expression}) -> | |||
{match, Line, Var, transform_statement(Expression)}; | |||
transform_statement(Stmt) -> | |||
Stmt. | |||
@ -0,0 +1,41 @@ | |||
%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. | |||
%% | |||
%% This file is provided to you under the Apache License, | |||
%% Version 2.0 (the "License"); you may not use this file | |||
%% except in compliance with the License. You may obtain | |||
%% a copy of the License at | |||
%% | |||
%% http://www.apache.org/licenses/LICENSE-2.0 | |||
%% | |||
%% Unless required by applicable law or agreed to in writing, | |||
%% software distributed under the License is distributed on an | |||
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||
%% KIND, either express or implied. See the License for the | |||
%% specific language governing permissions and limitations | |||
%% under the License. | |||
-module(lager_util). | |||
-export([levels/0, level_to_num/1]). | |||
levels() -> | |||
[debug, info, notice, warning, error, critical, alert, emergency]. | |||
level_to_num(debug) -> | |||
0; | |||
level_to_num(info) -> | |||
1; | |||
level_to_num(notice) -> | |||
2; | |||
level_to_num(warning) -> | |||
3; | |||
level_to_num(error) -> | |||
4; | |||
level_to_num(critical) -> | |||
5; | |||
level_to_num(alert) -> | |||
6; | |||
level_to_num(emergency) -> | |||
7; | |||
level_to_num(_) -> | |||
0. |