summaryrefslogtreecommitdiff
path: root/deps/twig/src
diff options
context:
space:
mode:
Diffstat (limited to 'deps/twig/src')
-rw-r--r--deps/twig/src/trunc_io.erl215
-rw-r--r--deps/twig/src/twig.app.src8
-rw-r--r--deps/twig/src/twig.erl55
-rw-r--r--deps/twig/src/twig_app.erl23
-rw-r--r--deps/twig/src/twig_event_handler.erl164
-rw-r--r--deps/twig/src/twig_int.hrl26
-rw-r--r--deps/twig/src/twig_monitor.erl48
-rw-r--r--deps/twig/src/twig_sup.erl26
-rw-r--r--deps/twig/src/twig_util.erl82
9 files changed, 647 insertions, 0 deletions
diff --git a/deps/twig/src/trunc_io.erl b/deps/twig/src/trunc_io.erl
new file mode 100644
index 00000000..cfa6c972
--- /dev/null
+++ b/deps/twig/src/trunc_io.erl
@@ -0,0 +1,215 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with your Erlang distribution. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Corelatus AB.
+%% Portions created by Corelatus are Copyright 2003, Corelatus
+%% AB. All Rights Reserved.''
+%%
+%% Module to print out terms for logging. Limits by length rather than depth.
+%%
+%% The resulting string may be slightly larger than the limit; the intention
+%% is to provide predictable CPU and memory consumption for formatting
+%% terms, not produce precise string lengths.
+%%
+%% Typical use:
+%%
+%% trunc_io:print(Term, 500).
+%%
+-module(trunc_io).
+-author('matthias@corelatus.se').
+%% And thanks to Chris Newcombe for a bug fix
+-export([print/2, fprint/2, safe/2]). % interface functions
+-export([perf/0, perf/3, perf1/0, test/0, test/2]). % testing functions
+-version("$Id: trunc_io.erl,v 1.11 2009-02-23 12:01:06 matthias Exp $").
+
+
+%% Returns an flattened list containing the ASCII representation of the given
+%% term.
+fprint(T, Max) ->
+ {L, _} = print(T, Max),
+ lists:flatten(L).
+
+%% Same as print, but never crashes.
+%%
+%% This is a tradeoff. Print might conceivably crash if it's asked to
+%% print something it doesn't understand, for example some new data
+%% type in a future version of Erlang. If print crashes, we fall back
+%% to io_lib to format the term, but then the formatting is
+%% depth-limited instead of length limited, so you might run out
+%% memory printing it. Out of the frying pan and into the fire.
+%%
+safe(What, Len) ->
+ case catch print(What, Len) of
+ {L, Used} when is_list(L) -> {L, Used};
+ _ -> {"unable to print" ++ io_lib:write(What, 99)}
+ end.
+
+%% Returns {List, Length}
+print(_, Max) when Max < 0 -> {"...", 3};
+print(Tuple, Max) when is_tuple(Tuple) ->
+ {TC, Len} = tuple_contents(Tuple, Max-2),
+ {[${, TC, $}], Len + 2};
+
+%% We assume atoms, floats, funs, integers, PIDs, ports and refs never need
+%% to be truncated. This isn't strictly true, someone could make an
+%% arbitrarily long bignum. Let's assume that won't happen unless someone
+%% is being malicious.
+%%
+print(Atom, _Max) when is_atom(Atom) ->
+ L = atom_to_list(Atom),
+ {L, length(L)};
+
+print(<<>>, _Max) ->
+ {"<<>>", 4};
+
+print(Binary, Max) when is_binary(Binary) ->
+ B = binary_to_list(Binary, 1, lists:min([Max, size(Binary)])),
+ {L, Len} = alist_start(B, Max-4),
+ {["<<", L, ">>"], Len};
+
+print(Float, _Max) when is_float(Float) ->
+ L = float_to_list(Float),
+ {L, length(L)};
+
+print(Fun, _Max) when is_function(Fun) ->
+ L = erlang:fun_to_list(Fun),
+ {L, length(L)};
+
+print(Integer, _Max) when is_integer(Integer) ->
+ L = integer_to_list(Integer),
+ {L, length(L)};
+
+print(Pid, _Max) when is_pid(Pid) ->
+ L = pid_to_list(Pid),
+ {L, length(L)};
+
+print(Ref, _Max) when is_reference(Ref) ->
+ L = erlang:ref_to_list(Ref),
+ {L, length(L)};
+
+print(Port, _Max) when is_port(Port) ->
+ L = erlang:port_to_list(Port),
+ {L, length(L)};
+
+print(List, Max) when is_list(List) ->
+ alist_start(List, Max).
+
+%% Returns {List, Length}
+tuple_contents(Tuple, Max) ->
+ L = tuple_to_list(Tuple),
+ list_body(L, Max).
+
+%% Format the inside of a list, i.e. do not add a leading [ or trailing ].
+%% Returns {List, Length}
+list_body([], _) -> {[], 0};
+list_body(_, Max) when Max < 4 -> {"...", 3};
+list_body([H|T], Max) ->
+ {List, Len} = print(H, Max),
+ {Final, FLen} = list_bodyc(T, Max - Len),
+ {[List|Final], FLen + Len};
+list_body(X, Max) -> %% improper list
+ {List, Len} = print(X, Max - 1),
+ {[$|,List], Len + 1}.
+
+list_bodyc([], _) -> {[], 0};
+list_bodyc(_, Max) when Max < 4 -> {"...", 3};
+list_bodyc([H|T], Max) ->
+ {List, Len} = print(H, Max),
+ {Final, FLen} = list_bodyc(T, Max - Len - 1),
+ {[$,, List|Final], FLen + Len + 1};
+list_bodyc(X,Max) -> %% improper list
+ {List, Len} = print(X, Max - 1),
+ {[$|,List], Len + 1}.
+
+%% The head of a list we hope is ascii. Examples:
+%%
+%% [65,66,67] -> "ABC"
+%% [65,0,67] -> "A"[0,67]
+%% [0,65,66] -> [0,65,66]
+%% [65,b,66] -> "A"[b,66]
+%%
+alist_start([], _) -> {"[]", 2};
+alist_start(_, Max) when Max < 4 -> {"...", 3};
+alist_start([H|T], Max) when H >= 16#20, H =< 16#7e -> % definitely printable
+ {L, Len} = alist([H|T], Max-1),
+ {[$\"|L], Len + 1};
+alist_start([H|T], Max) when H == 9; H == 10; H == 13 -> % show as space
+ {L, Len} = alist(T, Max-1),
+ {[$ |L], Len + 1};
+alist_start(L, Max) ->
+ {R, Len} = list_body(L, Max-2),
+ {[$[, R, $]], Len + 2}.
+
+alist([], _) -> {"\"", 1};
+alist(_, Max) when Max < 5 -> {"...\"", 4};
+alist([H|T], Max) when H >= 16#20, H =< 16#7e -> % definitely printable
+ {L, Len} = alist(T, Max-1),
+ {[H|L], Len + 1};
+alist([H|T], Max) when H == 9; H == 10; H == 13 -> % show as space
+ {L, Len} = alist(T, Max-1),
+ {[$ |L], Len + 1};
+alist(L, Max) ->
+ {R, Len} = list_body(L, Max-3),
+ {[$\", $[, R, $]], Len + 3}.
+
+
+%%--------------------
+%% The start of a test suite. So far, it only checks for not crashing.
+test() ->
+ test(trunc_io, print).
+
+test(Mod, Func) ->
+ Simple_items = [atom, 1234, 1234.0, {tuple}, [], [list], "string", self(),
+ <<1,2,3>>, make_ref(), fun() -> ok end],
+ F = fun(A) ->
+ Mod:Func(A, 100),
+ Mod:Func(A, 2),
+ Mod:Func(A, 20)
+ end,
+
+ G = fun(A) ->
+ case catch F(A) of
+ {'EXIT', _} -> exit({failed, A});
+ _ -> ok
+ end
+ end,
+
+ lists:foreach(G, Simple_items),
+
+ Tuples = [ {1,2,3,a,b,c}, {"abc", def, 1234},
+ {{{{a},b,c,{d},e}},f}],
+
+ Lists = [ [1,2,3,4,5,6,7], lists:seq(1,1000),
+ [{a}, {a,b}, {a, [b,c]}, "def"], [a|b], [$a|$b] ],
+
+
+ lists:foreach(G, Tuples),
+ lists:foreach(G, Lists).
+
+perf() ->
+ {New, _} = timer:tc(trunc_io, perf, [trunc_io, print, 1000]),
+ {Old, _} = timer:tc(trunc_io, perf, [io_lib, write, 1000]),
+ io:fwrite("New code took ~p us, old code ~p\n", [New, Old]).
+
+perf(M, F, Reps) when Reps > 0 ->
+ test(M,F),
+ perf(M,F,Reps-1);
+perf(_,_,_) ->
+ done.
+
+%% Performance test. Needs a particularly large term I saved as a binary...
+perf1() ->
+ {ok, Bin} = file:read_file("bin"),
+ A = binary_to_term(Bin),
+ {N, _} = timer:tc(trunc_io, print, [A, 1500]),
+ {M, _} = timer:tc(io_lib, write, [A]),
+ {N, M}.
+
diff --git a/deps/twig/src/twig.app.src b/deps/twig/src/twig.app.src
new file mode 100644
index 00000000..7e1da747
--- /dev/null
+++ b/deps/twig/src/twig.app.src
@@ -0,0 +1,8 @@
+{application, twig, [
+ {description, "Logger"},
+ {vsn, git},
+ {registered, []},
+ {applications, [kernel, stdlib]},
+ {mod, {twig_app, []}},
+ {env, []}
+]}.
diff --git a/deps/twig/src/twig.erl b/deps/twig/src/twig.erl
new file mode 100644
index 00000000..6a24e6bc
--- /dev/null
+++ b/deps/twig/src/twig.erl
@@ -0,0 +1,55 @@
+% Copyright 2011 Cloudant
+%
+% Licensed 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(twig).
+
+-export([log/2, log/3, log/4, set_level/1]).
+
+-include("twig_int.hrl").
+
+set_level(LevelAtom) ->
+ application:set_env(twig, level, twig_util:level(LevelAtom)).
+
+log(LevelAtom, String) ->
+ log(LevelAtom, String, [], []).
+
+log(LevelAtom, Format, Data) ->
+ log(LevelAtom, Format, Data, []).
+
+log(LevelAtom, Format, Data, _Options) ->
+ %% TODO do something useful with options
+ Level = twig_util:level(LevelAtom),
+ case application:get_env(twig, level) of
+ {ok, Threshold} when Level =< Threshold ->
+ send_message(Level, Format, Data);
+ undefined when Level =< ?LEVEL_INFO ->
+ send_message(Level, Format, Data);
+ _ ->
+ ok
+ end.
+
+%% internal
+
+send_message(Level, Format, Data) ->
+ gen_event:sync_notify(error_logger, format(Level, Format, Data)).
+
+format(Level, Format, Data) ->
+ %% TODO truncate large messages
+ #twig{
+ level = Level,
+ msg = iolist_to_binary(twig_util:format(Format, Data)),
+ msgid = erlang:get(nonce),
+ pid = self()
+ }.
+
diff --git a/deps/twig/src/twig_app.erl b/deps/twig/src/twig_app.erl
new file mode 100644
index 00000000..209391cc
--- /dev/null
+++ b/deps/twig/src/twig_app.erl
@@ -0,0 +1,23 @@
+% Copyright 2011 Cloudant
+%
+% Licensed 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(twig_app).
+-behaviour(application).
+-export([start/2, stop/1]).
+
+start(_StartType, _StartArgs) ->
+ twig_sup:start_link().
+
+stop(_State) ->
+ ok.
diff --git a/deps/twig/src/twig_event_handler.erl b/deps/twig/src/twig_event_handler.erl
new file mode 100644
index 00000000..cd61b0d3
--- /dev/null
+++ b/deps/twig/src/twig_event_handler.erl
@@ -0,0 +1,164 @@
+% Copyright 2011 Cloudant
+%
+% Licensed 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(twig_event_handler).
+
+-behaviour(gen_event).
+
+-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
+ code_change/3]).
+
+-import(twig_util, [get_env/2]).
+
+-record(state, {
+ socket,
+ host,
+ port,
+ hostname,
+ os_pid,
+ appid,
+ facility,
+ level
+}).
+
+-include("twig_int.hrl").
+
+init([]) ->
+ {ok, Socket} = gen_udp:open(0),
+ {ok, ok, State} = handle_call(load_config, #state{socket=Socket}),
+ {ok, State}.
+
+handle_event(#twig{level=Level, msgid=MsgId, msg=Msg, pid=Pid}, State) ->
+ write(Level, MsgId, Msg, Pid, State),
+ {ok, State};
+
+% OTP standard events
+handle_event({Class, _GL, {Pid, Format, Args}}, #state{level=Max} = State) ->
+ case otp_event_level(Class, Format) of
+ undefined ->
+ {ok, State};
+ Level when Level > Max ->
+ {ok, State};
+ Level ->
+ {MsgId, Msg} = message(Format, Args),
+ write(Level, MsgId, Msg, Pid, State),
+ {ok, State}
+ end;
+
+handle_event(_Event, State) ->
+ {ok, State}.
+
+handle_call({set_level, Level}, State) ->
+ {ok, ok, State#state{level = Level}};
+
+handle_call(load_config, State) ->
+ Host = case inet:getaddr(get_env(host, undefined), inet) of
+ {ok, Address} ->
+ Address;
+ {error, _} ->
+ undefined
+ end,
+ NewState = State#state{
+ host = Host,
+ port = get_env(port, 514),
+ hostname = net_adm:localhost(),
+ os_pid = os:getpid(),
+ appid = get_env(appid, "twig"),
+ facility = twig_util:facility(get_env(facility, local2)),
+ level = twig_util:level(get_env(level, info))
+ },
+ {ok, ok, NewState};
+
+handle_call(_Call, State) ->
+ {ok, ignored, State}.
+
+handle_info(_Info, State) ->
+ {ok, State}.
+
+terminate(_Reason, State) ->
+ gen_udp:close(State#state.socket).
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+write(Level, undefined, Msg, Pid, State) ->
+ write(Level, "--------", Msg, Pid, State);
+write(Level, MsgId, Msg, Pid, State) when is_list(Msg); is_binary(Msg) ->
+ #state{facility=Facil, appid=App, hostname=Hostname, host=Host, port=Port,
+ socket=Socket} = State,
+ Pre = io_lib:format("<~B>~B ~s ~s ~s ~p ~s - ", [Facil bor Level,
+ ?SYSLOG_VERSION, twig_util:iso8601_timestamp(), Hostname, App, Pid,
+ MsgId]),
+ send(Socket, Host, Port, [Pre, Msg, $\n]).
+
+send(_, undefined, _, Packet) ->
+ io:put_chars(Packet);
+send(Socket, Host, Port, Packet) ->
+ gen_udp:send(Socket, Host, Port, Packet).
+
+message(crash_report, Report) ->
+ Msg = case erts_debug:flat_size(Report) > get_env(max_term_size, 8192) of
+ true ->
+ MaxString = get_env(max_message_size, 16000),
+ ["*Truncated* - ", trunc_io:print(Report, MaxString)];
+ false ->
+ proc_lib:format(Report)
+ end,
+ {crash_report, Msg};
+message(supervisor_report, Report) ->
+ Name = get_value(supervisor, Report),
+ Error = get_value(errorContext, Report),
+ Reason = get_value(reason, Report),
+ Offender = get_value(offender, Report),
+ ChildPid = get_value(pid, Offender),
+ ChildName = get_value(name, Offender),
+ case get_value(mfa, Offender) of
+ undefined ->
+ {M,F,_} = get_value(mfargs, Offender);
+ {M,F,_} ->
+ ok
+ end,
+ {supervisor_report, twig_util:format("~p ~p (~p) child: ~p [~p] ~p:~p",
+ [Name, Error, Reason, ChildName, ChildPid, M, F])};
+message(Type, Report) when Type == std_error;
+ Type == std_info;
+ Type == std_warning;
+ Type == progress_report;
+ Type == progress ->
+ {Type, twig_util:format("~2048.0p", [Report])};
+message(Format, Args) when is_list(Format) ->
+ {msg, twig_util:format(Format, Args)};
+message(Format, Args) ->
+ {unknown, twig_util:format("~2048.0p ~2048.0p", [Format, Args])}.
+
+otp_event_level(_, crash_report) -> ?LEVEL_CRIT;
+otp_event_level(_, supervisor_report) -> ?LEVEL_WARN;
+otp_event_level(_, supervisor) -> ?LEVEL_WARN;
+otp_event_level(_, progress_report) -> ?LEVEL_DEBUG;
+otp_event_level(_, progress) -> ?LEVEL_DEBUG;
+otp_event_level(error, _) -> ?LEVEL_ERR;
+otp_event_level(warning_msg, _) -> ?LEVEL_WARN;
+otp_event_level(info_msg, _) -> ?LEVEL_NOTICE;
+otp_event_level(error_report, _) -> ?LEVEL_ERR;
+otp_event_level(warning_report, _) -> ?LEVEL_WARN;
+otp_event_level(info_report, _) -> ?LEVEL_NOTICE;
+otp_event_level(_, _) -> ?LEVEL_DEBUG.
+
+get_value(Key, Props) ->
+ case lists:keyfind(Key, 1, Props) of
+ {Key, Value} ->
+ Value;
+ false ->
+ undefined
+ end.
diff --git a/deps/twig/src/twig_int.hrl b/deps/twig/src/twig_int.hrl
new file mode 100644
index 00000000..a510d405
--- /dev/null
+++ b/deps/twig/src/twig_int.hrl
@@ -0,0 +1,26 @@
+% Copyright 2011 Cloudant
+%
+% Licensed 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.
+
+-define(SYSLOG_VERSION, 1).
+
+-define(LEVEL_DEBUG, 7).
+-define(LEVEL_INFO, 6).
+-define(LEVEL_NOTICE, 5).
+-define(LEVEL_WARN, 4).
+-define(LEVEL_ERR, 3).
+-define(LEVEL_CRIT, 2).
+-define(LEVEL_ALERT, 1).
+-define(LEVEL_EMERG, 0).
+
+-record(twig, {level, msgid, msg, pid}).
diff --git a/deps/twig/src/twig_monitor.erl b/deps/twig/src/twig_monitor.erl
new file mode 100644
index 00000000..c32a0c69
--- /dev/null
+++ b/deps/twig/src/twig_monitor.erl
@@ -0,0 +1,48 @@
+% Copyright 2011 Cloudant
+%
+% Licensed 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(twig_monitor).
+
+-behaviour(gen_server).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
+ code_change/3]).
+
+-export([start_link/0]).
+
+start_link() ->
+ gen_server:start_link(?MODULE, [], []).
+
+init(_) ->
+ ok = gen_event:add_sup_handler(error_logger, twig_event_handler, []),
+ {ok, nil}.
+
+handle_call(_Call, _From, State) ->
+ {reply, ignored, State}.
+
+handle_cast(_Cast, State) ->
+ {noreply, State}.
+
+handle_info({gen_event_EXIT, twig_event_handler, Reason} = Msg, State) ->
+ io:format("~p~n", [Msg]),
+ {stop, Reason, State};
+
+handle_info(_Msg, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_, State, _) ->
+ {ok, State}.
diff --git a/deps/twig/src/twig_sup.erl b/deps/twig/src/twig_sup.erl
new file mode 100644
index 00000000..0fe73ef4
--- /dev/null
+++ b/deps/twig/src/twig_sup.erl
@@ -0,0 +1,26 @@
+% Copyright 2011 Cloudant
+%
+% Licensed 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(twig_sup).
+-behaviour(supervisor).
+-export([start_link/0, init/1]).
+
+%% Helper macro for declaring children of supervisor
+-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+init([]) ->
+ {ok, { {one_for_one, 5, 10}, [?CHILD(twig_monitor, worker)]} }.
diff --git a/deps/twig/src/twig_util.erl b/deps/twig/src/twig_util.erl
new file mode 100644
index 00000000..b4f830c9
--- /dev/null
+++ b/deps/twig/src/twig_util.erl
@@ -0,0 +1,82 @@
+% Copyright 2011 Cloudant
+%
+% Licensed 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(twig_util).
+
+-export([format/2, get_env/2, level/1, facility/1, iso8601_timestamp/0]).
+
+level(debug) -> 7;
+level(info) -> 6;
+level(notice) -> 5;
+level(warn) -> 4;
+level(warning) -> 4;
+level(err) -> 3;
+level(error) -> 3;
+level(crit) -> 2;
+level(alert) -> 1;
+level(emerg) -> 0;
+level(panic) -> 0;
+
+level(I) when is_integer(I), I >= 0, I =< 7 ->
+ I;
+level(_BadLevel) ->
+ 3.
+
+facility(kern) -> (0 bsl 3) ; % kernel messages
+facility(user) -> (1 bsl 3) ; % random user-level messages
+facility(mail) -> (2 bsl 3) ; % mail system
+facility(daemon) -> (3 bsl 3) ; % system daemons
+facility(auth) -> (4 bsl 3) ; % security/authorization messages
+facility(syslog) -> (5 bsl 3) ; % messages generated internally by syslogd
+facility(lpr) -> (6 bsl 3) ; % line printer subsystem
+facility(news) -> (7 bsl 3) ; % network news subsystem
+facility(uucp) -> (8 bsl 3) ; % UUCP subsystem
+facility(cron) -> (9 bsl 3) ; % clock daemon
+facility(authpriv) -> (10 bsl 3); % security/authorization messages (private)
+facility(ftp) -> (11 bsl 3); % ftp daemon
+
+facility(local0) -> (16 bsl 3);
+facility(local1) -> (17 bsl 3);
+facility(local2) -> (18 bsl 3);
+facility(local3) -> (19 bsl 3);
+facility(local4) -> (20 bsl 3);
+facility(local5) -> (21 bsl 3);
+facility(local6) -> (22 bsl 3);
+facility(local7) -> (23 bsl 3).
+
+
+iso8601_timestamp() ->
+ {_,_,Micro} = Now = os:timestamp(),
+ {{Year,Month,Date},{Hour,Minute,Second}} = calendar:now_to_datetime(Now),
+ Format = "~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0B.~6.10.0BZ",
+ io_lib:format(Format, [Year, Month, Date, Hour, Minute, Second, Micro]).
+
+format(Format, Data) ->
+ MaxTermSize = get_env(max_term_size, 8192),
+ case erts_debug:flat_size(Data) > MaxTermSize of
+ true ->
+ MaxString = get_env(max_message_size, 16000),
+ {Truncated, _} = trunc_io:print(Data, MaxString),
+ ["*Truncated* ", Format, " - ", Truncated];
+ false ->
+ io_lib:format(Format, Data)
+ end.
+
+get_env(Key, Default) ->
+ case application:get_env(twig, Key) of
+ {ok, Value} ->
+ Value;
+ undefined ->
+ Default
+ end.