-module(witness).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/witness.gleam").
-export([bool/2, float/2, int/2, string/2, default_config/0, this/3, level_to_short_string/1, set_config/1, empty_config/0, with_sink/3, with_global_fields/2, set_process_fields/1, add_process_fields/1]).
-export_type([field/0, config/0, format/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.
-opaque field() :: {w_string, binary(), binary()} |
{w_int, binary(), integer()} |
{w_float, binary(), float()} |
{w_bool, binary(), boolean()}.
-opaque config() :: {config,
gleam@dict:dict(format(), list(fun((logging:log_level(), binary()) -> nil))),
list(field())}.
-type format() :: json | text.
-file("src/witness.gleam", 216).
?DOC(
" Create a bool field\n"
"\n"
" # Example\n"
" \n"
" ```gleam\n"
" witness.this(logging.Info, \"Things happened\", [witness.bool(\"really\", True)])\n"
" ```\n"
).
-spec bool(binary(), boolean()) -> field().
bool(Name, Value) ->
{w_bool, Name, Value}.
-file("src/witness.gleam", 204).
?DOC(
" Create a float field\n"
"\n"
" # Example\n"
" \n"
" ```gleam\n"
" witness.this(logging.Info, \"Things happened\", [witness.float(\"how_many\", 3.14159)])\n"
" ```\n"
).
-spec float(binary(), float()) -> field().
float(Name, Value) ->
{w_float, Name, Value}.
-file("src/witness.gleam", 192).
?DOC(
" Create an int field\n"
"\n"
" # Example\n"
" \n"
" ```gleam\n"
" witness.this(logging.Info, \"Things happened\", [witness.int(\"how_many\", 21)])\n"
" ```\n"
).
-spec int(binary(), integer()) -> field().
int(Name, Value) ->
{w_int, Name, Value}.
-file("src/witness.gleam", 155).
-spec fields_to_string(field()) -> binary().
fields_to_string(Field) ->
case Field of
{w_string, Name, Value} ->
<<<<Name/binary, ": "/utf8>>/binary, Value/binary>>;
{w_int, Name@1, Value@1} ->
<<<<Name@1/binary, ": "/utf8>>/binary,
(erlang:integer_to_binary(Value@1))/binary>>;
{w_float, Name@2, Value@2} ->
<<<<Name@2/binary, ": "/utf8>>/binary,
(gleam_stdlib:float_to_string(Value@2))/binary>>;
{w_bool, Name@3, Value@3} ->
<<<<Name@3/binary, ": "/utf8>>/binary,
(gleam@bool:to_string(Value@3))/binary>>
end.
-file("src/witness.gleam", 180).
?DOC(
" Create a string field\n"
"\n"
" # Example\n"
" \n"
" ```gleam\n"
" witness.this(logging.Info, \"Something happened\", [witness.string(\"what\", \"i dont know, honestly\")])\n"
" ```\n"
).
-spec string(binary(), binary()) -> field().
string(Name, Value) ->
{w_string, Name, Value}.
-file("src/witness.gleam", 103).
-spec timestamp_to_short_string(gleam@time@timestamp:timestamp()) -> binary().
timestamp_to_short_string(Timestamp) ->
{_, {time_of_day, Hours, Minutes, Seconds, _}} = gleam@time@timestamp:to_calendar(
Timestamp,
gleam@time@calendar:local_offset()
),
<<<<<<<<(gleam@string:pad_start(
erlang:integer_to_binary(Hours),
2,
<<"0"/utf8>>
))/binary,
":"/utf8>>/binary,
(gleam@string:pad_start(
erlang:integer_to_binary(Minutes),
2,
<<"0"/utf8>>
))/binary>>/binary,
":"/utf8>>/binary,
(gleam@string:pad_start(
erlang:integer_to_binary(Seconds),
2,
<<"0"/utf8>>
))/binary>>.
-file("src/witness.gleam", 146).
-spec field_to_json(field()) -> {binary(), gleam@json:json()}.
field_to_json(Field) ->
case Field of
{w_string, Name, Value} ->
{Name, gleam@json:string(Value)};
{w_int, Name@1, Value@1} ->
{Name@1, gleam@json:int(Value@1)};
{w_float, Name@2, Value@2} ->
{Name@2, gleam@json:float(Value@2)};
{w_bool, Name@3, Value@3} ->
{Name@3, gleam@json:bool(Value@3)}
end.
-file("src/witness.gleam", 114).
-spec level_to_string(logging:log_level()) -> binary().
level_to_string(Level) ->
case Level of
emergency ->
<<"emergency"/utf8>>;
alert ->
<<"alert"/utf8>>;
critical ->
<<"critical"/utf8>>;
error ->
<<"error"/utf8>>;
warning ->
<<"warning"/utf8>>;
notice ->
<<"notice"/utf8>>;
info ->
<<"info"/utf8>>;
debug ->
<<"debug"/utf8>>
end.
-file("src/witness.gleam", 55).
-spec format_message(logging:log_level(), format(), binary(), list(field())) -> binary().
format_message(Level, Format, Message, Fields) ->
case Format of
json ->
_pipe = [string(
<<"timestamp"/utf8>>,
gleam@time@timestamp:to_rfc3339(
gleam@time@timestamp:system_time(),
{duration, 0, 0}
)
),
string(<<"level"/utf8>>, level_to_string(Level)),
string(<<"message"/utf8>>, Message) |
Fields],
_pipe@1 = gleam@list:map(_pipe, fun field_to_json/1),
_pipe@2 = gleam@json:object(_pipe@1),
gleam@json:to_string(_pipe@2);
text ->
Message@1 = <<<<(timestamp_to_short_string(
gleam@time@timestamp:system_time()
))/binary,
" "/utf8>>/binary,
Message/binary>>,
_pipe@3 = Fields,
gleam@list:fold(
_pipe@3,
Message@1,
fun(Acc, Field) ->
<<<<Acc/binary, "\n "/utf8>>/binary,
(fields_to_string(Field))/binary>>
end
)
end.
-file("src/witness.gleam", 230).
-spec upsert_sink(fun((logging:log_level(), binary()) -> nil)) -> fun((gleam@option:option(list(fun((logging:log_level(), binary()) -> nil)))) -> list(fun((logging:log_level(), binary()) -> nil))).
upsert_sink(Sink) ->
fun(X) -> case X of
{some, X@1} ->
[Sink | X@1];
none ->
[Sink]
end end.
-file("src/witness.gleam", 224).
?DOC(" The default config logging to `io.println` with format `witness.Text`\n").
-spec default_config() -> config().
default_config() ->
_pipe = maps:new(),
_pipe@1 = gleam@dict:upsert(
_pipe,
text,
upsert_sink(fun(_, Message) -> gleam_stdlib:println(Message) end)
),
{config, _pipe@1, []}.
-file("src/witness.gleam", 31).
-spec this(logging:log_level(), binary(), list(field())) -> nil.
this(Level, Message, Fields) ->
Config = witness_ffi:get_config(default_config()),
Fields@1 = lists:append(erlang:element(3, Config), Fields),
Fields@2 = lists:append(witness_ffi:get_process_fields([]), Fields@1),
_pipe = erlang:element(2, Config),
gleam@dict:each(
_pipe,
fun(Format, Sinks) ->
Message@1 = format_message(Level, Format, Message, Fields@2),
gleam@list:map(Sinks, fun(Sink) -> Sink(Level, Message@1) end)
end
),
nil.
-file("src/witness.gleam", 90).
?DOC(" Turn a log level into a \"[LEVEL]\" string\n").
-spec level_to_short_string(logging:log_level()) -> binary().
level_to_short_string(Level) ->
case Level of
emergency ->
<<"[EMERG]"/utf8>>;
alert ->
<<"[ALERT]"/utf8>>;
critical ->
<<"[CRIT]"/utf8>>;
error ->
<<"[ERROR]"/utf8>>;
warning ->
<<"[WARN]"/utf8>>;
notice ->
<<"[NOTICE]"/utf8>>;
info ->
<<"[INFO]"/utf8>>;
debug ->
<<"[DEBUG]"/utf8>>
end.
-file("src/witness.gleam", 285).
?DOC(
" Set your config globally\n"
"\n"
" # Example\n"
"\n"
" ```gleam\n"
" witness.empty_config()\n"
" |> with_sink(witness.Json, logging.log)\n"
" |> witness.set_config\n"
" ```\n"
).
-spec set_config(config()) -> nil.
set_config(Config) ->
witness_ffi:set_config(Config).
-file("src/witness.gleam", 297).
?DOC(
" Get a new empty config \n"
"\n"
" # Example\n"
"\n"
" ```gleam\n"
" witness.empty_config()\n"
" |> with_sink(witness.Json, logging.log)\n"
" |> witness.set_config\n"
" ```\n"
).
-spec empty_config() -> config().
empty_config() ->
{config, maps:new(), []}.
-file("src/witness.gleam", 314).
?DOC(
" Add a sink with a specific formatting to a config\n"
"\n"
" # Example\n"
"\n"
" ```gleam\n"
" witness.empty_config()\n"
" |> with_sink(witness.Json, logging.log)\n"
" |> witness.set_config\n"
" ```\n"
).
-spec with_sink(config(), format(), fun((logging:log_level(), binary()) -> nil)) -> config().
with_sink(Config, Format, Sink) ->
Sinks = begin
_pipe = erlang:element(2, Config),
gleam@dict:upsert(_pipe, Format, upsert_sink(Sink))
end,
{config, Sinks, erlang:element(3, Config)}.
-file("src/witness.gleam", 338).
?DOC(
" These fields will be prepended to every message logged through witness.\n"
" Does not replace fields from previous calls.\n"
"\n"
" # Example\n"
"\n"
" ```gleam \n"
"\n"
" witness.empty_config()\n"
" |> with_sink(witness.Json, logging.log)\n"
" |> witness.with_global_fields([\n"
" witness.string(\"application\", \"my_application\"),\n"
" witness.string(\"version\", \"1.0.0\"),\n"
" ])\n"
" |> witness.set_config\n"
" ```\n"
).
-spec with_global_fields(config(), list(field())) -> config().
with_global_fields(Config, Fields) ->
Global_fields = lists:append(erlang:element(3, Config), Fields),
{config, erlang:element(2, Config), Global_fields}.
-file("src/witness.gleam", 358).
?DOC(
" Put per process fields into the process dictionary.\n"
" Overwrites all previously set fields for the process.\n"
"\n"
" These fields will be prepended to every message that process logs.\n"
"\n"
" # Example\n"
"\n"
" ```gleam \n"
" witness.set_process_fields([\n"
" witness.string(\"process_name\", \"my special process\"),\n"
" ])\n"
" ```\n"
).
-spec set_process_fields(list(field())) -> nil.
set_process_fields(Fields) ->
witness_ffi:set_process_fields(Fields).
-file("src/witness.gleam", 373).
?DOC(
" Add per process fields into the process dictionary.\n"
" Does not replace fields from previous calls.\n"
"\n"
" These fields will be prepended to every message that process logs.\n"
"\n"
" # Example\n"
"\n"
" ```gleam \n"
" witness.set_process_fields([\n"
" witness.string(\"process_name\", \"my special process\"),\n"
" ])\n"
" ```\n"
).
-spec add_process_fields(list(field())) -> nil.
add_process_fields(Fields) ->
_pipe = lists:append(witness_ffi:get_process_fields([]), Fields),
witness_ffi:set_process_fields(_pipe).