local Log = {} local logfile = string.format("%s/%s.log", get_cache_dir(), "lvim") Log.levels = { TRACE = 1, DEBUG = 2, INFO = 3, WARN = 4, ERROR = 5, } vim.tbl_add_reverse_lookup(Log.levels) function Log:init() local status_ok, structlog = pcall(require, "structlog") if not status_ok then return nil end local notify_handler = require "lvim.core.notify" ---Check if notify is available ---@return boolean local is_notify_available = function() local in_headless = #vim.api.nvim_list_uis() == 0 --We can't rely on lvim.builtin.notify.active since this can be used before the config loader local has_notify_plugin = pcall(require, "notify") if not in_headless and has_notify_plugin then return true end return false end local log_level = Log.levels[(lvim.log.level):upper() or "WARN"] local lvim_log = { lvim = { sinks = { structlog.sinks.Console(log_level, { async = false, processors = { structlog.processors.Namer(), structlog.processors.StackWriter({ "line", "file" }, { max_parents = 0, stack_level = 2 }), structlog.processors.Timestamper "%H:%M:%S", }, formatter = structlog.formatters.FormatColorizer( -- "%s [%-5s] %s: %-30s", { "timestamp", "level", "logger_name", "msg" }, { level = structlog.formatters.FormatColorizer.color_level() } ), }), structlog.sinks.File(Log.levels.TRACE, logfile, { processors = { structlog.processors.Namer(), structlog.processors.StackWriter({ "line", "file" }, { max_parents = 3, stack_level = 2 }), structlog.processors.Timestamper "%H:%M:%S", }, formatter = structlog.formatters.Format( -- "%s [%-5s] %s: %-30s", { "timestamp", "level", "logger_name", "msg" } ), }), }, }, } if is_notify_available() then table.insert( lvim_log.lvim.sinks, structlog.sinks.NvimNotify(Log.levels.INFO, { processors = { notify_handler.default_namer, notify_handler.params_injecter, }, formatter = structlog.formatters.Format( -- "%s", { "msg" }, { blacklist_all = true } ), params_map = { icon = "icon", keep = "keep", on_open = "on_open", on_close = "on_close", timeout = "timeout", title = "title", }, }) ) end structlog.configure(lvim_log) local logger = structlog.get_logger "lvim" if lvim.log.override_notify then logger:log(Log.levels.INFO, "Ignoring request to override vim.notify with structlog due to instabilities") end return logger end --- Adds a log entry using Plenary.log ---@fparam msg any ---@param level string [same as vim.log.log_levels] function Log:add_entry(level, msg, event) if self.__handle then self.__handle:log(level, vim.inspect(msg), event) return end local logger = self:init() if not logger then return end self.__handle = logger self.__handle:log(level, vim.inspect(msg), event) end ---Retrieves the path of the logfile ---@return string path of the logfile function Log:get_path() return logfile end ---Add a log entry at TRACE level ---@param msg any ---@param event any function Log:trace(msg, event) self:add_entry(self.levels.TRACE, msg, event) end ---Add a log entry at DEBUG level ---@param msg any ---@param event any function Log:debug(msg, event) self:add_entry(self.levels.DEBUG, msg, event) end ---Add a log entry at INFO level ---@param msg any ---@param event any function Log:info(msg, event) self:add_entry(self.levels.INFO, msg, event) end ---Add a log entry at WARN level ---@param msg any ---@param event any function Log:warn(msg, event) self:add_entry(self.levels.WARN, msg, event) end ---Add a log entry at ERROR level ---@param msg any ---@param event any function Log:error(msg, event) self:add_entry(self.levels.ERROR, msg, event) end setmetatable({}, Log) return Log