iam new in erlang and OTP i have two questions :
iam trying to read some erlang code from ejabberd source, it's very complex and iam surprised with two start/0 functions and two start/1 functions too in ejabberd_logger.erl so
1-what's this ? which function code will be executed when calling start/1?
2-should i read and understand all ejabberd source code to modify and customize it like i want ? i see that it's a huge code but is this a must for good handling ?
-module(ejabberd_logger).
-compile({no_auto_import, [get/0]}).
%% API
-export([start/0, get/0, set/1, get_log_path/0, flush/0]).
-export([convert_loglevel/1, loglevels/0]).
-ifndef(LAGER).
-export([progress_filter/2]).
-endif.
%% Deprecated functions
-export([restart/0, reopen_log/0, rotate_log/0]).
-deprecated([{restart, 0},
{reopen_log, 0},
{rotate_log, 0}]).
-type loglevel() :: none | emergency | alert | critical |
error | warning | notice | info | debug.
-define(is_loglevel(L),
((L == none) or (L == emergency) or (L == alert)
or (L == critical) or (L == error) or (L == warning)
or (L == notice) or (L == info) or (L == debug))).
-export_type([loglevel/0]).
%%%===================================================================
%%% API
%%%===================================================================
-spec get_log_path() -> string().
get_log_path() ->
case ejabberd_config:env_binary_to_list(ejabberd, log_path) of
{ok, Path} ->
Path;
undefined ->
case os:getenv("EJABBERD_LOG_PATH") of
false ->
"ejabberd.log";
Path ->
Path
end
end.
-spec loglevels() -> [loglevel(), ...].
loglevels() ->
[none, emergency, alert, critical, error, warning, notice, info, debug].
-spec convert_loglevel(0..5) -> loglevel().
convert_loglevel(0) -> none;
convert_loglevel(1) -> critical;
convert_loglevel(2) -> error;
convert_loglevel(3) -> warning;
convert_loglevel(4) -> info;
convert_loglevel(5) -> debug.
quiet_mode() ->
case application:get_env(ejabberd, quiet) of
{ok, true} -> true;
_ -> false
end.
-spec get_integer_env(atom(), T) -> T.
get_integer_env(Name, Default) ->
case application:get_env(ejabberd, Name) of
{ok, I} when is_integer(I), I>=0 ->
I;
{ok, infinity} ->
infinity;
undefined ->
Default;
{ok, Junk} ->
error_logger:error_msg("wrong value for ~ts: ~p; "
"using ~p as a fallback~n",
[Name, Junk, Default]),
Default
end.
-ifdef(LAGER).
-spec get_string_env(atom(), T) -> T.
get_string_env(Name, Default) ->
case application:get_env(ejabberd, Name) of
{ok, L} when is_list(L) ->
L;
undefined ->
Default;
{ok, Junk} ->
error_logger:error_msg("wrong value for ~ts: ~p; "
"using ~p as a fallback~n",
[Name, Junk, Default]),
Default
end.
-spec start() -> ok.
start() ->
start(info).
start(Level) ->
StartedApps = application:which_applications(5000),
case lists:keyfind(logger, 1, StartedApps) of
%% Elixir logger is started. We assume everything is in place
%% to use lager to Elixir logger bridge.
{logger, _, _} ->
error_logger:info_msg("Ignoring ejabberd logger options, using Elixir Logger.", []),
%% Do not start lager, we rely on Elixir Logger
do_start_for_logger(Level);
_ ->
do_start(Level)
end.
do_start_for_logger(Level) ->
application:load(sasl),
application:set_env(sasl, sasl_error_logger, false),
application:load(lager),
application:set_env(lager, error_logger_redirect, false),
application:set_env(lager, error_logger_whitelist, ['Elixir.Logger.ErrorHandler']),
application:set_env(lager, crash_log, false),
application:set_env(lager, handlers, [{elixir_logger_backend, [{level, Level}]}]),
ejabberd:start_app(lager),
ok.
do_start(Level) ->
application:load(sasl),
application:set_env(sasl, sasl_error_logger, false),
application:load(lager),
ConsoleLog = get_log_path(),
Dir = filename:dirname(ConsoleLog),
ErrorLog = filename:join([Dir, "error.log"]),
CrashLog = filename:join([Dir, "crash.log"]),
LogRotateDate = get_string_env(log_rotate_date, ""),
LogRotateSize = case get_integer_env(log_rotate_size, 10*1024*1024) of
infinity -> 0;
V -> V
end,
LogRotateCount = get_integer_env(log_rotate_count, 1),
LogRateLimit = get_integer_env(log_rate_limit, 100),
ConsoleLevel0 = case quiet_mode() of
true -> critical;
_ -> Level
end,
ConsoleLevel = case get_lager_version() >= "3.6.0" of
true -> [{level, ConsoleLevel0}];
false -> ConsoleLevel0
end,
application:set_env(lager, error_logger_hwm, LogRateLimit),
application:set_env(
lager, handlers,
[{lager_console_backend, ConsoleLevel},
{lager_file_backend, [{file, ConsoleLog}, {level, Level}, {date, LogRotateDate},
{count, LogRotateCount}, {size, LogRotateSize}]},
{lager_file_backend, [{file, ErrorLog}, {level, error}, {date, LogRotateDate},
{count, LogRotateCount}, {size, LogRotateSize}]}]),
application:set_env(lager, crash_log, CrashLog),
application:set_env(lager, crash_log_date, LogRotateDate),
application:set_env(lager, crash_log_size, LogRotateSize),
application:set_env(lager, crash_log_count, LogRotateCount),
ejabberd:start_app(lager),
lists:foreach(fun(Handler) ->
lager:set_loghwm(Handler, LogRateLimit)
end, gen_event:which_handlers(lager_event)).
-spec restart() -> ok.
restart() ->
Level = ejabberd_option:loglevel(),
application:stop(lager),
start(Level).
-spec reopen_log() -> ok.
reopen_log() ->
ok.
-spec rotate_log() -> ok.
rotate_log() ->
catch lager_crash_log ! rotate,
lists:foreach(
fun({lager_file_backend, File}) ->
whereis(lager_event) ! {rotate, File};
(_) ->
ok
end, gen_event:which_handlers(lager_event)).
-spec get() -> loglevel().
get() ->
Handlers = get_lager_handlers(),
lists:foldl(fun(lager_console_backend, _Acc) ->
lager:get_loglevel(lager_console_backend);
(elixir_logger_backend, _Acc) ->
lager:get_loglevel(elixir_logger_backend);
(_, Acc) ->
Acc
end,
none, Handlers).
-spec set(0..5 | loglevel()) -> ok.
set(N) when is_integer(N), N>=0, N=<5 ->
set(convert_loglevel(N));
set(Level) when ?is_loglevel(Level) ->
case get() of
Level ->
ok;
_ ->
ConsoleLog = get_log_path(),
QuietMode = quiet_mode(),
lists:foreach(
fun({lager_file_backend, File} = H) when File == ConsoleLog ->
lager:set_loglevel(H, Level);
(lager_console_backend = H) when not QuietMode ->
lager:set_loglevel(H, Level);
(elixir_logger_backend = H) ->
lager:set_loglevel(H, Level);
(_) ->
ok
end, get_lager_handlers())
end,
case Level of
debug -> xmpp:set_config([{debug, true}]);
_ -> xmpp:set_config([{debug, false}])
end.
get_lager_handlers() ->
case catch gen_event:which_handlers(lager_event) of
{'EXIT',noproc} ->
[];
Result ->
Result
end.
-spec get_lager_version() -> string().
get_lager_version() ->
Apps = application:loaded_applications(),
case lists:keyfind(lager, 1, Apps) of
{_, _, Vsn} -> Vsn;
false -> "0.0.0"
end.
-spec flush() -> ok.
flush() ->
application:stop(lager),
application:stop(sasl).
-else.
-include_lib("kernel/include/logger.hrl").
-spec start() -> ok | {error, term()}.
start() ->
start(info).
start(Level) ->
EjabberdLog = get_log_path(),
Dir = filename:dirname(EjabberdLog),
ErrorLog = filename:join([Dir, "error.log"]),
LogRotateSize = get_integer_env(log_rotate_size, 10*1024*1024),
LogRotateCount = get_integer_env(log_rotate_count, 1),
Config = #{max_no_bytes => LogRotateSize,
max_no_files => LogRotateCount,
filesync_repeat_interval => no_repeat,
file_check => 1000,
sync_mode_qlen => 1000,
drop_mode_qlen => 1000,
flush_qlen => 5000},
FmtConfig = #{legacy_header => false,
time_designator => $ ,
max_size => 100*1024,
single_line => false},
FileFmtConfig = FmtConfig#{template => file_template()},
ConsoleFmtConfig = FmtConfig#{template => console_template()},
try
ok = logger:set_primary_config(level, Level),
DefaultHandlerId = get_default_handlerid(),
ok = logger:update_formatter_config(DefaultHandlerId, ConsoleFmtConfig),
case quiet_mode() of
true ->
ok = logger:set_handler_config(DefaultHandlerId, level, critical);
_ ->
ok
end,
case logger:add_primary_filter(progress_report,
{fun ?MODULE:progress_filter/2, stop}) of
ok -> ok;
{error, {already_exist, _}} -> ok
end,
case logger:add_handler(ejabberd_log, logger_std_h,
#{level => all,
config => Config#{file => EjabberdLog},
formatter => {logger_formatter, FileFmtConfig}}) of
ok -> ok;
{error, {already_exist, _}} -> ok
end,
case logger:add_handler(error_log, logger_std_h,
#{level => error,
config => Config#{file => ErrorLog},
formatter => {logger_formatter, FileFmtConfig}}) of
ok -> ok;
{error, {already_exist, _}} -> ok
end
catch _:{Tag, Err} when Tag == badmatch; Tag == case_clause ->
?LOG_CRITICAL("Failed to set logging: ~p", [Err]),
Err
end.
get_default_handlerid() ->
Ids = logger:get_handler_ids(),
case lists:member(default, Ids) of
true -> default;
false -> hd(Ids)
end.
-spec restart() -> ok.
restart() ->
ok.
progress_filter(#{level:=info,msg:={report,#{label:={_,progress}}}} = Event, _) ->
case get() of
debug ->
logger_filters:progress(Event#{level => debug}, log);
_ ->
stop
end;
progress_filter(Event, _) ->
Event.
console_template() ->
[time, " [", level, "] " | msg()].
file_template() ->
[time, " [", level, "] ", pid,
{mfa, ["#", mfa, {line, [":", line], []}], []}, " " | msg()].
msg() ->
[{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []},
msg, io_lib:nl()].
-spec reopen_log() -> ok.
reopen_log() ->
ok.
-spec rotate_log() -> ok.
rotate_log() ->
ok.
-spec get() -> loglevel().
get() ->
#{level := Level} = logger:get_primary_config(),
Level.
-spec set(0..5 | loglevel()) -> ok.
set(N) when is_integer(N), N>=0, N=<5 ->
set(convert_loglevel(N));
set(Level) when ?is_loglevel(Level) ->
case get() of
Level -> ok;
PrevLevel ->
?LOG_NOTICE("Changing loglevel from '~s' to '~s'",
[PrevLevel, Level]),
logger:set_primary_config(level, Level),
case Level of
debug -> xmpp:set_config([{debug, true}]);
_ -> xmpp:set_config([{debug, false}])
end
end.
-spec flush() -> ok.
flush() ->
lists:foreach(
fun(#{id := HandlerId, module := logger_std_h}) ->
logger_std_h:filesync(HandlerId);
(#{id := HandlerId, module := logger_disk_log_h}) ->
logger_disk_log_h:filesync(HandlerId);
(_) ->
ok
end, logger:get_handler_config()).
-endif.
Here, the first version is protected by -ifdef(LAGER). and thus gets used if the macro LAGER is defined, and the second version comes after '-else.', and gets used if LAGER is not defined. (The first version uses the Lager library for logging, and the second version uses the newer built-in logger library.)
This question already has answers here:
How to efficiently read thousand of lines from STDIN in Erlang?
(2 answers)
Closed 2 years ago.
A common programming problem which I've done in Python, Java, etc. with no problem. The following in Erlang (which I'm just learning) runs very slowly (~44s user time for 10^5 operations), and I am lost as to why.
As written on HackerRank, the program takes one line from stdin with an integer representing the number of operations that follow. Each subsequent line should be 1 X (enqueue X), 2 (dequeue and discard), or 3 (peek and print the next value on the queue).
Am I using lists:reverse/1 incorrectly?
-module(two_stacks).
%% API exports
-export([main/1]).
enqueue(Num, F, B) ->
{[Num | F], B}.
dequeue(F, []) ->
[_|B] = lists:reverse(F),
{[], B};
dequeue(F, [_|B]) ->
{F, B}.
peek(F, []) ->
[H|T] = lists:reverse(F),
io:format(H),
{[], [H|T]};
peek(F, [H|T]) ->
io:format(H),
{F, [H|T]}.
dispatchOperation(_, {F, B}) ->
[Code|Line] = io:get_line(""),
case Code of
49 ->
[_|Num] = Line,
enqueue(Num, F, B);
50 -> dequeue(F, B);
51 -> peek(F, B)
end.
main(_) ->
{Count, _} = string:to_integer(io:get_line("")),
_ = lists:foldl(fun dispatchOperation/2, {[], []}, lists:seq(1, Count)),
erlang:halt(0).
https://www.hackerrank.com/challenges/queue-using-two-stacks/problem
Are you running an escript? If that's the case, you should add a -mode(compile). there, because otherwise it runs the script in interpreted mode.
Also, you can compare the times against using the queue module (which is implemented using two stacks)
The issue is with the way I'm parsing input. See 46493207 for discussion.
Since all the inputs are integers I was able to make use of the same technique used there. The completed code is:
-module(solution).
-export([main/0]).
enqueue(Num, F, B) ->
{[Num | F], B}.
dequeue(F, []) ->
[_|B] = lists:reverse(F),
{[], B};
dequeue(F, [_|B]) ->
{F, B}.
peek(F, []) ->
[H|T] = lists:reverse(F),
io:format("~B~n", [H]),
{[], [H|T]};
peek(F, [H|T]) ->
io:format("~B~n", [H]),
{F, [H|T]}.
run(_, {F, B}) ->
Line = io:get_line(""),
[X| Y] = binary:split(Line, [<<$\s>>, <<$\n>>], [global]),
Code = binary_to_integer(X),
case Code of
1 ->
Num = binary_to_integer(lists:nth(1, Y)),
enqueue(Num, F, B);
2 -> dequeue(F, B);
3 -> peek(F, B)
end.
main() ->
ok = io:setopts(standard_io, [binary]),
{Count, _} = string:to_integer(io:get_line("")),
_ = lists:foldl(fun run/2, {[], []}, lists:seq(1, Count)),
erlang:halt(0).
I really could'n formulate the question better, but here's my problem:
I want to use this code to convert infix expression to postfix expression in Erlang, but it only writes to the console output. The problem is, I need a list or a string returned, so I can use it as an argument in an other function.
-module(foo).
-compile(export_all).
parse(Str) ->
{ok, Tokens, _} = erl_scan:string(Str ++ "."),
{ok, [E]} = erl_parse:parse_exprs(Tokens),
E.
rpn({op, _, What, LS, RS}) ->
rpn(LS),
rpn(RS),
io:format(" ~s ", [atom_to_list(What)]);
rpn({integer, _, N}) ->
io:format(" ~B ", [N]).
p(Str) ->
Tree = parse(Str),
rpn(Tree),
io:format("~n").
For example, I want someting like this:
Str = "2 * (3 + 4)".
module:p(Str) =:= "2 3 4 + *".
module:anotherFunction(p(Str)).
You just need to io_lib:format/2 instead of io:format/2 and lists:flatten/1 in the end.
-module(foo).
-compile(export_all).
parse(Str) ->
{ok, Tokens, _} = erl_scan:string(Str ++ "."),
{ok, [E]} = erl_parse:parse_exprs(Tokens),
E.
rpn({op, _, What, LS, RS}) ->
io_lib:format("~s ~s ~s", [rpn(LS), rpn(RS), atom_to_list(What)]);
rpn({integer, _, N}) ->
io_lib:format("~b", [N]).
p(Str) ->
Tree = parse(Str),
lists:flatten(rpn(Tree)).
I have a list of values "Z0010", "Z0011", "Z0012", "Z0013" "Z0014", "Z0015", "Z0016", "Z0017", "Z0018", "Z0019"
I want to develop a function that takes a value in parameter
and I want to do a test in my function if the value passed as a parameter is equal to a value in the list in this cases it will show "existe" if not it displays "not existe"
I try with :
test(Token)->
case get_user_formid_by_token(Token) of
{ok, H} ->
FormId=string:substr(H, 2, length(H)),
Form=string:to_integer(FormId),
case verify (0019,1,Form) of
{ok}->io:format("existe");
{notok}->io:format("not existe")
end;
{error, notfound}-> io:format("qq")
end.
verify(VariableLength,VariableIncrement,FormId)->
lists:map(fun(I) -> if I =:= FormId ->
{ok};
I =/= FormId ->
{notok}
end
end,
lists:seq(0010, VariableLength, VariableIncrement)).
but when I execute this code it displays :
1> model:test("21137900").
** exception error: no case clause matching [{notok},
{notok},
{notok},
{notok},
{notok},
{notok},
{notok},
{notok},
{notok},
{notok}]
in function model:test/1
I try now with this solution :
get_user_formid_by_token(Token) ->
Q = qlc:q([{X#person.formid} || X <- mnesia:table(person),
X#person.token =:= Token]),
case do(Q) of
[{H}] ->
{ok, H};
[] ->
{error, notfound}
end.
test(Token)->
case get_user_formid_by_token(Token) of
{ok, H} ->
io:format("~s~n",[H]),
FormId=string:substr(H, 5, length(H)),
io:format("~s~n",[FormId]),
Form=string:to_integer(FormId),
io:format("~p~n",[Form]),
lists:member(Form, lists:seq(313, 320, 1));
{error, notfound}-> io:format("qq")
end.
but when I test I have this message in the console:
1> model:test("21137900").
Z000313
313
{313,[]}
false
the result should be true and not false
I think that Form=string:to_integer(FormId), it not return in this case 313
and another thing I want to add in my code
for example if H equal "Z000010" FormId=string:substr(H, 2, length(H)),
it return "000010"
Now I want to eliminate the first zero before the first integer not null so extarct 0000
before 1
lists:map/2 takes one list and creates a new list with the same number of values, so your list of 10 values is transformed into a list of 10 {ok} or {notok} tuples.
You probably want lists:member/2 instead.
5> lists:member(0, lists:seq(1, 3, 1)).
false
6> lists:member(3, lists:seq(1, 3, 1)).
true
7> lists:map(fun(X) -> ok end, lists:seq(1, 3, 1)).
[ok,ok,ok]
Have a look at the documentation (http://www.erlang.org/doc/man/string.html#to_integer-1):
to_integer(String) -> {Int, Rest} | {error, Reason}
Types:
String = string()
Int = integer()
Rest = string()
Reason = no_integer | not_a_list
So to_integer returns a tuple containing the number that was consumed from the string and the rest of the string. You can even tell from your test output where it says {313,[]}. In order to get the value of the number bound to your Formvariable, you need to decompose the tuple, which is typically done by pattern matching:
{Form,_Rest}=string:to_integer(FormId)
Now your Form will contain only the number 313.
The string:to_integerfunction will also happily eat the leading zeroes:
1> {Form, _} = string:to_integer("000010"), Form.
10
What is the most efficient way from the time consumed to read a text file into a list of binary strings in erlang ? The obvious solution
-module(test).
-export([run/1]).
open_file(FileName, Mode) ->
{ok, Device} = file:open(FileName, [Mode, binary]),
Device.
close_file(Device) ->
ok = file:close(Device).
read_lines(Device, L) ->
case io:get_line(Device, L) of
eof ->
lists:reverse(L);
String ->
read_lines(Device, [String | L])
end.
run(InputFileName) ->
Device = open_file(InputFileName, read),
Data = read_lines(Device, []),
close_file(Device),
io:format("Read ~p lines~n", [length(Data)]).
becomes too slow when the file contains more than 100000 lines.
{ok, Bin} = file:read_file(Filename).
or if you need the contents line by line
read(File) ->
case file:read_line(File) of
{ok, Data} -> [Data | read(File)];
eof -> []
end.
read the entire file in into a binary. Convert to a list and rip out the lines.
This is far more efficient than any other method. If you don't believe me time
it.
file2lines(File) ->
{ok, Bin} = file:read_file(File),
string2lines(binary_to_list(bin), []).
string2lines("\n" ++ Str, Acc) -> [reverse([$\n|Acc]) | string2lines(Str,[])];
string2lines([H|T], Acc) -> string2lines(T, [H|Acc]);
string2lines([], Acc) -> [reverse(Acc)].