chore: add logging
parent
7c30116fc1
commit
4df3f8a036
|
|
@ -1,6 +1,7 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local defaults = {
|
local defaults = {
|
||||||
|
log_level = "ERROR", -- One of "TRACE", "DEBUG", "ERROR"
|
||||||
save_dir = vim.fn.expand(vim.fn.stdpath("data") .. "/sessions/"), -- directory where session files are saved
|
save_dir = vim.fn.expand(vim.fn.stdpath("data") .. "/sessions/"), -- directory where session files are saved
|
||||||
silent = false, -- silent nvim message when sourcing session file
|
silent = false, -- silent nvim message when sourcing session file
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
local utils = require("persisted.utils")
|
|
||||||
local config = require("persisted.config")
|
local config = require("persisted.config")
|
||||||
|
local log = require("persisted.log")
|
||||||
|
local utils = require("persisted.utils")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
|
@ -143,14 +144,8 @@ function M.get_branch(dir)
|
||||||
if vim.fn.filereadable(branch_session) ~= 0 then
|
if vim.fn.filereadable(branch_session) ~= 0 then
|
||||||
return branch
|
return branch
|
||||||
else
|
else
|
||||||
vim.notify(
|
log:debug("Trying to load a session for branch: %s", config.options.default_branch)
|
||||||
string.format("[Persisted.nvim]: Trying to load a session for branch %s", config.options.default_branch),
|
log:error("Could not load a session for branch: %s", git_branch[1])
|
||||||
vim.log.levels.INFO
|
|
||||||
)
|
|
||||||
vim.notify(
|
|
||||||
string.format("[Persisted.nvim]: Could not load a session for branch %s.", git_branch[1]),
|
|
||||||
vim.log.levels.WARN
|
|
||||||
)
|
|
||||||
vim.g.persisted_branch_session = branch_session
|
vim.g.persisted_branch_session = branch_session
|
||||||
return config.options.branch_separator .. config.options.default_branch
|
return config.options.branch_separator .. config.options.default_branch
|
||||||
end
|
end
|
||||||
|
|
@ -183,6 +178,21 @@ end
|
||||||
---@return nil
|
---@return nil
|
||||||
function M.setup(opts)
|
function M.setup(opts)
|
||||||
config.setup(opts)
|
config.setup(opts)
|
||||||
|
|
||||||
|
log.set_root(log.new({
|
||||||
|
handlers = {
|
||||||
|
{
|
||||||
|
type = "echo",
|
||||||
|
level = vim.log.levels.WARN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type = "file",
|
||||||
|
filename = "persisted.log",
|
||||||
|
level = vim.log.levels[config.options.log_level],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
local dir = session_dir()
|
local dir = session_dir()
|
||||||
local branch = get_branchname()
|
local branch = get_branchname()
|
||||||
|
|
||||||
|
|
@ -192,6 +202,7 @@ function M.setup(opts)
|
||||||
and not ignore_branch(branch)
|
and not ignore_branch(branch)
|
||||||
and args_check()
|
and args_check()
|
||||||
then
|
then
|
||||||
|
log:trace("Starting session")
|
||||||
M.start()
|
M.start()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -210,19 +221,24 @@ function M.load(opt, dir)
|
||||||
session = opt.session
|
session = opt.session
|
||||||
local session_data = utils.make_session_data(session)
|
local session_data = utils.make_session_data(session)
|
||||||
branch = session_data and session_data.branch or ""
|
branch = session_data and session_data.branch or ""
|
||||||
|
log:trace("Session branch %s", branch)
|
||||||
|
log:trace("Session data %s", session_data)
|
||||||
if not branch then
|
if not branch then
|
||||||
vim.notify(string.format("[Persisted.nvim]: Invalid session file %s", session), vim.log.levels.WARN)
|
vim.notify(string.format("[Persisted.nvim]: Invalid session file %s", session), vim.log.levels.WARN)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
branch = get_branchname()
|
branch = get_branchname()
|
||||||
session = opt.last and get_last() or get_current(dir)
|
session = opt.last and get_last() or get_current(dir)
|
||||||
|
log:trace("Session branch: %s", branch)
|
||||||
end
|
end
|
||||||
|
|
||||||
if session then
|
if session then
|
||||||
if vim.fn.filereadable(session) ~= 0 then
|
if vim.fn.filereadable(session) ~= 0 then
|
||||||
vim.g.persisting_session = not config.options.follow_cwd and session or nil
|
vim.g.persisting_session = not config.options.follow_cwd and session or nil
|
||||||
|
log:trace("Session load: %s", session)
|
||||||
utils.load_session(session, config.options.silent)
|
utils.load_session(session, config.options.silent)
|
||||||
elseif type(config.options.on_autoload_no_session) == "function" then
|
elseif type(config.options.on_autoload_no_session) == "function" then
|
||||||
|
log:trace("No session to load")
|
||||||
config.options.on_autoload_no_session()
|
config.options.on_autoload_no_session()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -241,6 +257,7 @@ function M.autoload()
|
||||||
|
|
||||||
if config.options.autoload and args_check() then
|
if config.options.autoload and args_check() then
|
||||||
if allow_dir(dir) and not ignore_dir(dir) and not ignore_branch(branch) then
|
if allow_dir(dir) and not ignore_dir(dir) and not ignore_branch(branch) then
|
||||||
|
log:trace("Autoloading from %s", dir)
|
||||||
M.load({}, dir)
|
M.load({}, dir)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -268,6 +285,7 @@ local function write(session)
|
||||||
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedSavePre" })
|
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedSavePre" })
|
||||||
vim.cmd("mks! " .. e(session))
|
vim.cmd("mks! " .. e(session))
|
||||||
vim.g.persisting = true
|
vim.g.persisting = true
|
||||||
|
log:trace("Session saved")
|
||||||
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedSavePost" })
|
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedSavePost" })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -317,6 +335,7 @@ function M.delete(dir)
|
||||||
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedDeletePre", data = session_data })
|
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedDeletePre", data = session_data })
|
||||||
|
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
|
log:trace("Deleting session %s", session)
|
||||||
M.stop()
|
M.stop()
|
||||||
vim.fn.system("rm " .. e(session))
|
vim.fn.system("rm " .. e(session))
|
||||||
end)
|
end)
|
||||||
|
|
@ -334,13 +353,16 @@ function M.toggle(dir)
|
||||||
dir = dir or session_dir()
|
dir = dir or session_dir()
|
||||||
|
|
||||||
if vim.g.persisting == nil then
|
if vim.g.persisting == nil then
|
||||||
|
log:trace("Toggling load")
|
||||||
return M.load({}, dir)
|
return M.load({}, dir)
|
||||||
end
|
end
|
||||||
|
|
||||||
if vim.g.persisting then
|
if vim.g.persisting then
|
||||||
|
log:trace("Toggling stop")
|
||||||
return M.stop()
|
return M.stop()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
log:trace("Toggling start")
|
||||||
return M.start()
|
return M.start()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,280 @@
|
||||||
|
---@type boolean
|
||||||
|
local is_windows = vim.loop.os_uname().version:match("Windows")
|
||||||
|
|
||||||
|
---@type string
|
||||||
|
local sep = is_windows and "\\" or "/"
|
||||||
|
|
||||||
|
---@return string
|
||||||
|
local join = function(...)
|
||||||
|
local joined = table.concat({ ... }, sep)
|
||||||
|
if is_windows then
|
||||||
|
joined = joined:gsub("\\\\+", "\\")
|
||||||
|
else
|
||||||
|
joined = joined:gsub("//+", "/")
|
||||||
|
end
|
||||||
|
return joined
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class LogHandler
|
||||||
|
---@field type string
|
||||||
|
---@field level integer
|
||||||
|
---@field formatter? fun(level: integer, msg: string, ...: any)
|
||||||
|
---@field handle? fun(level: integer, text: string)
|
||||||
|
local LogHandler = {}
|
||||||
|
|
||||||
|
local levels_reverse = {}
|
||||||
|
for k, v in pairs(vim.log.levels) do
|
||||||
|
levels_reverse[v] = k
|
||||||
|
end
|
||||||
|
|
||||||
|
function LogHandler.new(opts)
|
||||||
|
vim.validate({
|
||||||
|
type = { opts.type, "s" },
|
||||||
|
handle = { opts.handle, "f" },
|
||||||
|
formatter = { opts.formatter, "f" },
|
||||||
|
level = { opts.level, "n", true },
|
||||||
|
})
|
||||||
|
return setmetatable({
|
||||||
|
type = opts.type,
|
||||||
|
handle = opts.handle,
|
||||||
|
formatter = opts.formatter,
|
||||||
|
level = opts.level or vim.log.levels.INFO,
|
||||||
|
}, { __index = LogHandler })
|
||||||
|
end
|
||||||
|
|
||||||
|
function LogHandler:log(level, msg, ...)
|
||||||
|
if self.level <= level then
|
||||||
|
local text = self.formatter(level, msg, ...)
|
||||||
|
self.handle(level, text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function default_formatter(level, msg, ...)
|
||||||
|
local args = vim.F.pack_len(...)
|
||||||
|
for i = 1, args.n do
|
||||||
|
local v = args[i]
|
||||||
|
if type(v) == "table" then
|
||||||
|
args[i] = vim.inspect(v)
|
||||||
|
elseif v == nil then
|
||||||
|
args[i] = "nil"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local ok, text = pcall(string.format, msg, vim.F.unpack_len(args))
|
||||||
|
if ok then
|
||||||
|
local str_level = levels_reverse[level]
|
||||||
|
return string.format("[%s] %s\n%s", str_level, os.date("%Y-%m-%d %H:%M:%S"), text)
|
||||||
|
else
|
||||||
|
return string.format("[ERROR] error formatting log line: '%s' args %s", msg, vim.inspect(args))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param opts table
|
||||||
|
---@return LogHandler
|
||||||
|
local function create_file_handler(opts)
|
||||||
|
vim.validate({
|
||||||
|
filename = { opts.filename, "s" },
|
||||||
|
})
|
||||||
|
local ok, stdpath = pcall(vim.fn.stdpath, "log")
|
||||||
|
if not ok then
|
||||||
|
stdpath = vim.fn.stdpath("cache")
|
||||||
|
end
|
||||||
|
local filepath = join(stdpath, opts.filename)
|
||||||
|
local logfile, openerr = io.open(filepath, "a+")
|
||||||
|
if not logfile then
|
||||||
|
local err_msg = string.format("Failed to open the CodeCompanion log file: %s", openerr)
|
||||||
|
vim.notify(err_msg, vim.log.levels.ERROR)
|
||||||
|
opts.handle = function() end
|
||||||
|
else
|
||||||
|
opts.handle = function(level, text)
|
||||||
|
logfile:write(text)
|
||||||
|
logfile:write("\n")
|
||||||
|
logfile:flush()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return LogHandler.new(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param opts table
|
||||||
|
---@return LogHandler
|
||||||
|
local function create_notify_handler(opts)
|
||||||
|
opts.handle = function(level, text)
|
||||||
|
vim.notify(text, level)
|
||||||
|
end
|
||||||
|
return LogHandler.new(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param opts table
|
||||||
|
---@return LogHandler
|
||||||
|
local function create_echo_handler(opts)
|
||||||
|
opts.handle = function(level, text)
|
||||||
|
local hl = "Normal"
|
||||||
|
if level == vim.log.levels.ERROR then
|
||||||
|
hl = "DiagnosticError"
|
||||||
|
elseif level == vim.log.levels.WARN then
|
||||||
|
hl = "DiagnosticWarn"
|
||||||
|
end
|
||||||
|
vim.api.nvim_echo({ { text, hl } }, true, {})
|
||||||
|
end
|
||||||
|
return LogHandler.new(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return LogHandler
|
||||||
|
local function create_null_handler()
|
||||||
|
return LogHandler.new({
|
||||||
|
formatter = function() end,
|
||||||
|
handle = function() end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param opts table
|
||||||
|
---@return LogHandler
|
||||||
|
local function create_handler(opts)
|
||||||
|
vim.validate({
|
||||||
|
type = { opts.type, "s" },
|
||||||
|
})
|
||||||
|
if not opts.formatter then
|
||||||
|
opts.formatter = default_formatter
|
||||||
|
end
|
||||||
|
if opts.type == "file" then
|
||||||
|
return create_file_handler(opts)
|
||||||
|
elseif opts.type == "notify" then
|
||||||
|
return create_notify_handler(opts)
|
||||||
|
elseif opts.type == "echo" then
|
||||||
|
return create_echo_handler(opts)
|
||||||
|
else
|
||||||
|
vim.notify(string.format("Unknown log handler %s", opts.type), vim.log.levels.ERROR)
|
||||||
|
return create_null_handler()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class Logger
|
||||||
|
---@field handlers LogHandler[]
|
||||||
|
local Logger = {}
|
||||||
|
|
||||||
|
---@class LoggerArgs
|
||||||
|
---@field handlers LogHandler[]
|
||||||
|
---@field level nil|integer
|
||||||
|
|
||||||
|
---@param opts LoggerArgs
|
||||||
|
function Logger.new(opts)
|
||||||
|
vim.validate({
|
||||||
|
handlers = { opts.handlers, "t" },
|
||||||
|
level = { opts.level, "n", true },
|
||||||
|
})
|
||||||
|
local handlers = {}
|
||||||
|
for _, defn in ipairs(opts.handlers) do
|
||||||
|
table.insert(handlers, create_handler(defn))
|
||||||
|
end
|
||||||
|
local log = setmetatable({
|
||||||
|
handlers = handlers,
|
||||||
|
}, { __index = Logger })
|
||||||
|
if opts.level then
|
||||||
|
log:set_level(opts.level)
|
||||||
|
end
|
||||||
|
return log
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param level integer
|
||||||
|
function Logger:set_level(level)
|
||||||
|
for _, handler in ipairs(self.handlers) do
|
||||||
|
handler.level = level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return LogHandler[]
|
||||||
|
function Logger:get_handlers()
|
||||||
|
return self.handlers
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param level integer
|
||||||
|
---@param msg string
|
||||||
|
---@param ... any[]
|
||||||
|
function Logger:log(level, msg, ...)
|
||||||
|
for _, handler in ipairs(self.handlers) do
|
||||||
|
handler:log(level, msg, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param msg string
|
||||||
|
---@param ... any
|
||||||
|
function Logger:trace(msg, ...)
|
||||||
|
self:log(vim.log.levels.TRACE, msg, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param msg string
|
||||||
|
---@param ... any
|
||||||
|
function Logger:debug(msg, ...)
|
||||||
|
self:log(vim.log.levels.DEBUG, msg, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param msg string
|
||||||
|
---@param ... any
|
||||||
|
function Logger:info(msg, ...)
|
||||||
|
self:log(vim.log.levels.INFO, msg, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param msg string
|
||||||
|
---@param ... any
|
||||||
|
function Logger:warn(msg, ...)
|
||||||
|
self:log(vim.log.levels.WARN, msg, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param msg string
|
||||||
|
---@param ... any
|
||||||
|
function Logger:error(msg, ...)
|
||||||
|
self:log(vim.log.levels.ERROR, msg, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@generic T : any
|
||||||
|
---@param cb T
|
||||||
|
---@param message nil|string
|
||||||
|
---@return T
|
||||||
|
function Logger:wrap_cb(cb, message)
|
||||||
|
return function(err, ...)
|
||||||
|
if err then
|
||||||
|
self:error(message or "Error: %s", err)
|
||||||
|
end
|
||||||
|
return cb(err, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local root = Logger.new({
|
||||||
|
handlers = {
|
||||||
|
{
|
||||||
|
type = "echo",
|
||||||
|
level = vim.log.levels.WARN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
---@class Logger
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.new = Logger.new
|
||||||
|
|
||||||
|
M.get_logfile = function()
|
||||||
|
local ok, stdpath = pcall(vim.fn.stdpath, "log")
|
||||||
|
if not ok then
|
||||||
|
stdpath = vim.fn.stdpath("cache")
|
||||||
|
end
|
||||||
|
|
||||||
|
return join(stdpath, "persisted.log")
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param logger Logger
|
||||||
|
M.set_root = function(logger)
|
||||||
|
root = logger
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return Logger
|
||||||
|
M.get_root = function()
|
||||||
|
return root
|
||||||
|
end
|
||||||
|
|
||||||
|
setmetatable(M, {
|
||||||
|
__index = function(_, key)
|
||||||
|
return root[key]
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
return M
|
||||||
Loading…
Reference in New Issue