diff options
Diffstat (limited to 'deps/twig/src')
| -rw-r--r-- | deps/twig/src/trunc_io.erl | 215 | ||||
| -rw-r--r-- | deps/twig/src/twig.app.src | 8 | ||||
| -rw-r--r-- | deps/twig/src/twig.erl | 55 | ||||
| -rw-r--r-- | deps/twig/src/twig_app.erl | 23 | ||||
| -rw-r--r-- | deps/twig/src/twig_event_handler.erl | 164 | ||||
| -rw-r--r-- | deps/twig/src/twig_int.hrl | 26 | ||||
| -rw-r--r-- | deps/twig/src/twig_monitor.erl | 48 | ||||
| -rw-r--r-- | deps/twig/src/twig_sup.erl | 26 | ||||
| -rw-r--r-- | deps/twig/src/twig_util.erl | 82 |
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. |
