@ -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. |