-module(flash). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([parse_level/1, level_to_string/1, new/2, with_attrs/2, with_attr/2, with_group/2, enabled/2, log/3, debug/2, info/2, warn/2, error/2, json_writer/3, text_writer/3]). -export_type([attr/0, level/0, logger/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type attr() :: {bool_attr, binary(), boolean()} | {float_attr, binary(), float()} | {group_attr, binary(), list(attr())} | {int_attr, binary(), integer()} | {string_attr, binary(), binary()}. -type level() :: debug_level | info_level | warn_level | error_level. -type logger() :: {logger, level(), fun((level(), binary(), list(attr())) -> nil), gleam@option:option(logger()), binary(), list(attr())}. -file("src/flash.gleam", 39). ?DOC(" Parses a string representation of a level to the equivalent level variant.\n"). -spec parse_level(binary()) -> {ok, level()} | {error, nil}. parse_level(Level) -> case string:lowercase(Level) of <<"debug"/utf8>> -> {ok, debug_level}; <<"info"/utf8>> -> {ok, info_level}; <<"warn"/utf8>> -> {ok, warn_level}; <<"error"/utf8>> -> {ok, error_level}; _ -> {error, nil} end. -file("src/flash.gleam", 50). ?DOC(" Converts a level variant to an equivalent string representation.\n"). -spec level_to_string(level()) -> binary(). level_to_string(Level) -> case Level of debug_level -> <<"debug"/utf8>>; info_level -> <<"info"/utf8>>; warn_level -> <<"warn"/utf8>>; error_level -> <<"error"/utf8>> end. -file("src/flash.gleam", 71). ?DOC( " Creates a logger that logs for levels greater than or equal to\n" " the given level and writes using the given writer.\n" ). -spec new(level(), fun((level(), binary(), list(attr())) -> nil)) -> logger(). new(Level, Writer) -> {logger, Level, Writer, none, <<""/utf8>>, []}. -file("src/flash.gleam", 81). ?DOC(" Adds the list of attributes to the logger.\n"). -spec with_attrs(logger(), list(attr())) -> logger(). with_attrs(Logger, Attrs) -> _record = Logger, {logger, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), erlang:element(5, _record), lists:append(erlang:element(6, Logger), Attrs)}. -file("src/flash.gleam", 76). ?DOC(" Adds the given attribute to the logger.\n"). -spec with_attr(logger(), attr()) -> logger(). with_attr(Logger, Attr) -> with_attrs(Logger, [Attr]). -file("src/flash.gleam", 86). ?DOC(" Creates a child group from the given logger with the given group name.\n"). -spec with_group(logger(), binary()) -> logger(). with_group(Logger, Group) -> _record = Logger, {logger, erlang:element(2, _record), erlang:element(3, _record), {some, Logger}, Group, []}. -file("src/flash.gleam", 213). -spec level_to_int(level()) -> integer(). level_to_int(Level) -> case Level of debug_level -> 0; info_level -> 1; warn_level -> 2; error_level -> 3 end. -file("src/flash.gleam", 91). ?DOC(" Determines if the logger is enabled for the given log level.\n"). -spec enabled(logger(), level()) -> boolean(). enabled(Logger, Level) -> level_to_int(Level) >= level_to_int(erlang:element(2, Logger)). -file("src/flash.gleam", 222). -spec attr_compare(attr(), attr()) -> gleam@order:order(). attr_compare(A, B) -> A_is_group = case A of {group_attr, _, _} -> true; _ -> false end, B_is_group = case B of {group_attr, _, _} -> true; _ -> false end, case {A_is_group, B_is_group} of {true, true} -> gleam@string:compare(erlang:element(2, A), erlang:element(2, B)); {false, false} -> gleam@string:compare(erlang:element(2, A), erlang:element(2, B)); {true, false} -> gt; {_, _} -> lt end. -file("src/flash.gleam", 259). -spec logger_to_attrs(logger()) -> list(attr()). logger_to_attrs(Logger) -> case erlang:element(4, Logger) of {some, Parent} -> logger_to_attrs( {logger, erlang:element(2, Parent), erlang:element(3, Parent), erlang:element(4, Parent), erlang:element(5, Parent), [{group_attr, erlang:element(5, Logger), erlang:element(6, Logger)} | erlang:element(6, Parent)]} ); none -> erlang:element(6, Logger) end. -file("src/flash.gleam", 97). ?DOC( " Logs the message and any attributes if the logger is enabled to\n" " log at the given log level.\n" ). -spec log(logger(), level(), binary()) -> nil. log(Logger, Level, Message) -> case enabled(Logger, Level) of true -> (erlang:element(3, Logger))(Level, Message, logger_to_attrs(Logger)); false -> nil end. -file("src/flash.gleam", 105). ?DOC(" Logs the message and any attributes at the debug level.\n"). -spec debug(logger(), binary()) -> nil. debug(Logger, Message) -> log(Logger, debug_level, Message). -file("src/flash.gleam", 110). ?DOC(" Logs the message and any attributes at the info level.\n"). -spec info(logger(), binary()) -> nil. info(Logger, Message) -> log(Logger, info_level, Message). -file("src/flash.gleam", 115). ?DOC(" Logs the message and any attributes at the warn level.\n"). -spec warn(logger(), binary()) -> nil. warn(Logger, Message) -> log(Logger, warn_level, Message). -file("src/flash.gleam", 120). ?DOC(" Logs the message and any attributes at the error level.\n"). -spec error(logger(), binary()) -> nil. error(Logger, Message) -> log(Logger, error_level, Message). -file("src/flash.gleam", 272). -spec attr_to_json_value(attr()) -> gleam@json:json(). attr_to_json_value(Attr) -> case Attr of {bool_attr, _, Value} -> gleam@json:bool(Value); {float_attr, _, Value@1} -> gleam@json:float(Value@1); {group_attr, _, Value@2} -> _pipe = Value@2, _pipe@1 = gleam@list:map( _pipe, fun(Attr@1) -> {erlang:element(2, Attr@1), attr_to_json_value(Attr@1)} end ), gleam@json:object(_pipe@1); {int_attr, _, Value@3} -> gleam@json:int(Value@3); {string_attr, _, Value@4} -> gleam@json:string(Value@4) end. -file("src/flash.gleam", 285). -spec attr_to_text(attr()) -> gleam@string_tree:string_tree(). attr_to_text(Attr) -> From_strings = fun gleam_stdlib:identity/1, case Attr of {bool_attr, Key, Value} -> From_strings([Key, <<"="/utf8>>, gleam@bool:to_string(Value)]); {float_attr, Key@1, Value@1} -> From_strings( [Key@1, <<"="/utf8>>, gleam_stdlib:float_to_string(Value@1)] ); {group_attr, Key@2, Value@2} -> _pipe = Value@2, _pipe@1 = gleam@list:map( _pipe, fun(Attr@1) -> Key@3 = gleam@string:join( [Key@2, erlang:element(2, Attr@1)], <<"."/utf8>> ), attr_to_text(case Attr@1 of {bool_attr, _, Value@3} -> {bool_attr, Key@3, Value@3}; {float_attr, _, Value@4} -> {float_attr, Key@3, Value@4}; {group_attr, _, Value@5} -> {group_attr, Key@3, Value@5}; {int_attr, _, Value@6} -> {int_attr, Key@3, Value@6}; {string_attr, _, Value@7} -> {string_attr, Key@3, Value@7} end) end ), gleam@string_tree:join(_pipe@1, <<" "/utf8>>); {int_attr, Key@4, Value@8} -> From_strings( [Key@4, <<"="/utf8>>, erlang:integer_to_binary(Value@8)] ); {string_attr, Key@5, Value@9} -> From_strings([Key@5, <<"="/utf8>>, Value@9]) end. -file("src/flash.gleam", 310). -spec unique_by(list(FQY), fun((FQY, FQY) -> boolean())) -> list(FQY). unique_by(List, Predicate) -> case List of [] -> []; [First | Rest] -> [First | unique_by( gleam@list:filter( Rest, fun(Next) -> Predicate(First, Next) end ), Predicate )] end. -file("src/flash.gleam", 240). -spec prepare_attrs(list(attr())) -> list(attr()). prepare_attrs(Attrs) -> _pipe = Attrs, _pipe@1 = gleam@list:filter(_pipe, fun(Attr) -> case Attr of {group_attr, _, Value} -> Value /= []; _ -> true end end), _pipe@2 = lists:reverse(_pipe@1), _pipe@3 = unique_by( _pipe@2, fun(A, B) -> erlang:element(2, A) /= erlang:element(2, B) end ), _pipe@4 = gleam@list:sort(_pipe@3, fun attr_compare/2), gleam@list:map(_pipe@4, fun(Attr@1) -> case Attr@1 of {group_attr, Key, Value@1} -> {group_attr, Key, prepare_attrs(Value@1)}; _ -> Attr@1 end end). -file("src/flash.gleam", 140). ?DOC( " A writer that writes to standard out using a JSON representation.\n" " Here's some example code and the associated JSON output:\n" "\n" " ```\n" " let logger = with_attr(logger, StringAttr(\"request_id\", \"foobar\"))\n" " info(logger, \"/health\")\n" " ```\n" "\n" " ```\n" " {\"level\":\"info\",\"time\":\"2024-03-09T12:52:43.657-05:00\",\"message\":\"/health\",\"request_id\":\"foobar\"}\n" " ```\n" "\n" " The `level`, `time`, and `message` attributes are added automatically and will be\n" " ordered before other attributes. Other attributes are sorted lexicographically with\n" " groups being sorted after non group attributes. If multiple attributes in the same\n" " group share a key, the last attribute with the key is chosen.\n" ). -spec json_writer(level(), binary(), list(attr())) -> nil. json_writer(Level, Message, Attrs) -> Attrs@1 = begin _pipe = Attrs, _pipe@1 = prepare_attrs(_pipe), gleam@list:map( _pipe@1, fun(Attr) -> {erlang:element(2, Attr), attr_to_json_value(Attr)} end ) end, _pipe@2 = gleam@json:object( [{<<"level"/utf8>>, gleam@json:string(level_to_string(Level))}, {<<"time"/utf8>>, gleam@json:string( gleam@time@timestamp:to_rfc3339( gleam@time@timestamp:system_time(), gleam@time@calendar:local_offset() ) )}, {<<"message"/utf8>>, gleam@json:string(Message)} | Attrs@1] ), _pipe@3 = gleam@json:to_string(_pipe@2), gleam_stdlib:println(_pipe@3). -file("src/flash.gleam", 177). ?DOC( " A writer that writes to standard out using a text representation.\n" " Here's some example code and the associated text output:\n" "\n" " ```\n" " let logger = with_attr(logger, StringAttr(\"request_id\", \"foobar\"))\n" " info(logger, \"/health\")\n" " ```\n" "\n" " ```\n" " 12:59:37 INFO /health request_id=foobar\n" " ```\n" "\n" " Attributes are sorted lexicographically with groups being sorted after non\n" " group attributes. If multiple attributes in the same group share a key, the\n" " last attribute with the key is chosen.\n" ). -spec text_writer(level(), binary(), list(attr())) -> nil. text_writer(Level, Message, Attrs) -> Message@1 = gleam@string:pad_end(Message, 45, <<" "/utf8>>), Level@1 = begin _pipe = level_to_string(Level), _pipe@1 = string:uppercase(_pipe), gleam@string:pad_end(_pipe@1, 5, <<" "/utf8>>) end, {_, Now} = gleam@time@timestamp:to_calendar( gleam@time@timestamp:system_time(), gleam@time@calendar:local_offset() ), Time_tree = gleam_stdlib:identity( [gleam@string:pad_start( erlang:integer_to_binary(erlang:element(2, Now)), 2, <<"0"/utf8>> ), <<":"/utf8>>, gleam@string:pad_start( erlang:integer_to_binary(erlang:element(3, Now)), 2, <<"0"/utf8>> ), <<":"/utf8>>, gleam@string:pad_start( erlang:integer_to_binary(erlang:element(4, Now)), 2, <<"0"/utf8>> )] ), Attrs@1 = begin _pipe@2 = Attrs, _pipe@3 = prepare_attrs(_pipe@2), gleam@list:map(_pipe@3, fun attr_to_text/1) end, _pipe@4 = gleam@string_tree:join( [Time_tree, gleam_stdlib:identity(Level@1), gleam_stdlib:identity(Message@1) | Attrs@1], <<" "/utf8>> ), _pipe@5 = unicode:characters_to_binary(_pipe@4), gleam_stdlib:println(_pipe@5).