I wrote a simple logger for a server I'm writing. Wondering if anyone has any suggestions to improve it. # doom_logger.erl -module(doom_logger). -author('Andrew McRobb <[email protected]>'). -include("include/log_types.hrl"). -export([start/1, start/0, terminate/1, log/4, log/3]). -record(logger_state, { file_pointer = null, write_to_stdout = true, write_to_file = true, file_path = "doom.log", output_level = ?LOG_TYPE_DEBUG }). % start/0 Simple shortcut when no arguments are provided. start()-> start([]). % start/1 Initalizes the logger with key/value tuples. Simply ignores key's that aren't valid. % Please use #logger_state as reference of key names. start(Arguments)-> WriteToStdOutState = update_logger_state(#logger_state{}, Arguments, write_to_stdout), WriteToFileState = update_logger_state(WriteToStdOutState, Arguments, write_to_file), FilePathState = update_logger_state(WriteToFileState, Arguments, file_path), OutputLevelState = update_logger_state(FilePathState, Arguments, output_level), if OutputLevelState#logger_state.write_to_file -> case file:open(OutputLevelState#logger_state.file_path, [append, {encoding, utf8}, {delayed_write, 1, 1}]) of {ok, Device} -> {ok, OutputLevelState#logger_state{file_pointer=Device}}; Error -> Error end; true-> {ok, OutputLevelState} end. % terminate/1 terminates our file pointer of our log file. terminate(#logger_state{file_pointer=null})-> ok; terminate(#logger_state{file_pointer=File})-> file:close(File). % log/3 writes to the console or output file. If output_level is set higher than the type of log, % the function simply returns `ok`. log(#logger_state{output_level=OutputLevel}, Type, _, _) when Type < OutputLevel -> ok; log(State, Type, Message, FormatList) when ?IS_LOG_TYPE(Type) -> % Format our output log message for our log file or console. FormatedMessage = binary_to_list(erlang:iolist_to_binary(io_lib:format(Message, FormatList))), OutputMessage = lists:concat(["[", convert_type_int_to_string(Type), "] ", get_local_time_string(), " ", FormatedMessage, "\n"]), write_to_file(State, OutputMessage), write_to_console(State, Type, OutputMessage). % log/2 writes to the console or output file. If output_level is set higher than the type of log, % the function simply returns `ok`. This function simply doesn't support string formating for custom variables. log(State, Type, Message)-> ?MODULE:log(State, Type, Message, []). % write_to_file/2 Writes to our log file when 'file_pointer' is valid and 'write_to_file' is true. write_to_file(#logger_state{file_pointer=null}, _)-> ok; write_to_file(#logger_state{write_to_file=false}, _)-> ok; write_to_file(#logger_state{file_pointer=FilePointer}, Message)-> file:write(FilePointer, Message), ok. % write_to_console/3 Writes to our stdout/stderr when 'write_to_stdout' is true. write_to_console(#logger_state{write_to_stdout=false}, _, _) -> ok; write_to_console(_, Type, Message) -> case Type of ?LOG_TYPE_WARNING -> io:format(standard_error, Message, []); ?LOG_TYPE_ERROR -> io:format(standard_error, Message, []); _-> io:fwrite(Message) end, ok. % convert_type_int_to_string/1 Converts our enum int to a string for log messages. convert_type_int_to_string(Type) -> case Type of ?LOG_TYPE_DEBUG-> "DEBUG"; ?LOG_TYPE_MESSAGE-> "MESSAGE"; ?LOG_TYPE_WARNING-> "WARNING"; ?LOG_TYPE_ERROR-> "ERROR"; _-> "UNKOWN" end. % update_logger_state/3 Updates our #logger_state variable into a new state with the provided key/value tuples list and key (atom) we want. update_logger_state(State, Arguments, Key) -> case proplists:get_value(Key, Arguments) of undefined -> State; Value -> case Key of write_to_stdout-> if erlang:is_boolean(Value) -> State#logger_state{write_to_stdout=Value}; true-> State end; write_to_file-> if erlang:is_boolean(Value) -> State#logger_state{write_to_file=Value}; true-> State end; file_path-> if erlang:is_list(Value) -> State#logger_state{file_path=Value}; true-> State end; output_level-> if erlang:is_integer(Value) -> State#logger_state{output_level=Value}; true-> State end; _-> State end end. % get_local_time_string/0 Returns formated string of "Y-M-D HH:MM:SS" get_local_time_string() -> {{Year, Month, Day}, {Hours, Minutes, Seconds}} = calendar:local_time(), lists:concat([Year, "-", Month, "-", Day, " ", Hours, ":", Minutes, ":", Seconds]). # log_types.hrl -define(LOG_TYPE_DEBUG, 1). -define(LOG_TYPE_MESSAGE, 2). -define(LOG_TYPE_WARNING, 3). -define(LOG_TYPE_ERROR, 4). -define(IS_LOG_TYPE(V), ( V =:= ?LOG_TYPE_DEBUG orelse V =:= ?LOG_TYPE_MESSAGE orelse V =:= ?LOG_TYPE_WARNING orelse V =:= ?LOG_TYPE_ERROR )). # example start()-> {ok, L} = doom_logger:start([{write_to_file, false}, {output_level, ?LOG_TYPE_MESSAGE}]), doom_logger:log(L, ?LOG_TYPE_DEBUG, "I won't print output level set too high!"), doom_logger:log(L, ?LOG_TYPE_MESSAGE, "I sure will ~p!", ["print"]), doom_logger:terminate(). My only real concern is how I'm building the `#logger_state` with `update_logger_state/3`. I don't know if there really is a better way.