Current section
Files
Jump to
Current section
Files
src/flash.erl
-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).