defmodule Argos.Logger.Default do @moduledoc """ Implementación por defecto del logger de Argos. Usa Aurora para colorear el mensaje según el nivel y aplicar efectos. """ @behaviour Argos.Logger.Behaviour alias Aurora.{Color, Effects, Format} alias Aurora.Structs.{ChunkText, EffectInfo} @effects %{ debug: [:dim], info: [], warning: [:bold], error: [:bold], critical: [:bold, :blink], emergency: [:bold, :blink, :reverse], alert: [:bold, :blink] } @impl Argos.Logger.Behaviour def log(level, message, metadata) do effects = Map.get(@effects, level, []) level |> format_message(message, metadata, effects) |> IO.puts() end defp format_message(level, message, metadata, effects) do timestamp = format_timestamp(metadata[:timestamp]) level_str = String.upcase(to_string(level)) module_info = format_module(metadata) chunks = build_log_chunks(timestamp, level, level_str, module_info, message, effects) Format.format(chunks) end defp build_log_chunks(timestamp, level, level_str, module_info, message, effects) do timestamp_chunks = if timestamp != "" do [ %ChunkText{ text: timestamp, color: Color.to_color_info(:secondary), effects: %EffectInfo{dim: true} }, %ChunkText{ text: " ", color: Color.to_color_info(:no_color) } ] else [] end level_chunks = [ %ChunkText{ text: "[#{level_str}]", color: Color.to_color_info(level), effects: Effects.to_effect_info(effects) }, %ChunkText{ text: " ", color: Color.to_color_info(:no_color) } ] module_chunks = if module_info != "" do [ %ChunkText{ text: module_info, color: Color.to_color_info(:info), effects: %EffectInfo{dim: true} } ] else [] end message_chunks = [ %ChunkText{ text: message, color: Color.to_color_info(level), effects: %EffectInfo{} } ] Enum.concat([timestamp_chunks, level_chunks, module_chunks, message_chunks]) end def format_timestamp({{year, month, day}, {hour, min, sec, millisec}}) do "#{year}-#{pad(month)}-#{pad(day)} #{pad(hour)}:#{pad(min)}:#{pad(sec)}.#{pad(millisec, 3)}" end def format_timestamp(_), do: "" defp pad(number, length \\ 2) do number |> Integer.to_string() |> String.pad_leading(length, "0") end def format_module(metadata) do case {metadata[:module], metadata[:function], metadata[:file], metadata[:line]} do {mod, fun, file, line} when not is_nil(mod) and not is_nil(fun) and not is_nil(file) and not is_nil(line) -> "#{inspect(mod)}.#{fun} #{file}:#{line} " _ -> "" end end end