aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp/log.lua
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim/lsp/log.lua')
-rw-r--r--runtime/lua/vim/lsp/log.lua214
1 files changed, 110 insertions, 104 deletions
diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua
index 6d2e0bc292..9f2bd71158 100644
--- a/runtime/lua/vim/lsp/log.lua
+++ b/runtime/lua/vim/lsp/log.lua
@@ -2,138 +2,144 @@
local log = {}
+local log_levels = vim.log.levels
+
--- Log level dictionary with reverse lookup as well.
---
--- Can be used to lookup the number from the name or the name from the number.
--- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF"
--- Level numbers begin with "TRACE" at 0
+--- @type table<string|integer, string|integer>
--- @nodoc
-log.levels = vim.deepcopy(vim.log.levels)
+log.levels = vim.deepcopy(log_levels)
-- Default log level is warn.
-local current_log_level = log.levels.WARN
+local current_log_level = log_levels.WARN
+
local log_date_format = '%F %H:%M:%S'
-local format_func = function(arg)
+
+local function format_func(arg)
return vim.inspect(arg, { newline = '' })
end
-do
- local function notify(msg, level)
- if vim.in_fast_event() then
- vim.schedule(function()
- vim.notify(msg, level)
- end)
- else
+local function notify(msg, level)
+ if vim.in_fast_event() then
+ vim.schedule(function()
vim.notify(msg, level)
- end
+ end)
+ else
+ vim.notify(msg, level)
end
+end
+
+local logfilename = vim.fs.joinpath(vim.fn.stdpath('log'), 'lsp.log')
+
+-- TODO: Ideally the directory should be created in open_logfile(), right
+-- before opening the log file, but open_logfile() can be called from libuv
+-- callbacks, where using fn.mkdir() is not allowed.
+vim.fn.mkdir(vim.fn.stdpath('log'), 'p')
+
+--- Returns the log filename.
+---@return string log filename
+function log.get_filename()
+ return logfilename
+end
- local path_sep = vim.uv.os_uname().version:match('Windows') and '\\' or '/'
- local function path_join(...)
- return table.concat(vim.tbl_flatten({ ... }), path_sep)
+--- @type file*?, string?
+local logfile, openerr
+
+--- Opens log file. Returns true if file is open, false on error
+local function open_logfile()
+ -- Try to open file only once
+ if logfile then
+ return true
+ end
+ if openerr then
+ return false
end
- local logfilename = path_join(vim.fn.stdpath('log'), 'lsp.log')
- -- TODO: Ideally the directory should be created in open_logfile(), right
- -- before opening the log file, but open_logfile() can be called from libuv
- -- callbacks, where using fn.mkdir() is not allowed.
- vim.fn.mkdir(vim.fn.stdpath('log'), 'p')
+ logfile, openerr = io.open(logfilename, 'a+')
+ if not logfile then
+ local err_msg = string.format('Failed to open LSP client log file: %s', openerr)
+ notify(err_msg, log_levels.ERROR)
+ return false
+ end
- --- Returns the log filename.
- ---@return string log filename
- function log.get_filename()
- return logfilename
+ local log_info = vim.uv.fs_stat(logfilename)
+ if log_info and log_info.size > 1e9 then
+ local warn_msg = string.format(
+ 'LSP client log is large (%d MB): %s',
+ log_info.size / (1000 * 1000),
+ logfilename
+ )
+ notify(warn_msg)
end
- local logfile, openerr
- --- Opens log file. Returns true if file is open, false on error
- local function open_logfile()
- -- Try to open file only once
- if logfile then
+ -- Start message for logging
+ logfile:write(string.format('[START][%s] LSP logging initiated\n', os.date(log_date_format)))
+ return true
+end
+
+for level, levelnr in pairs(log_levels) do
+ -- Also export the log level on the root object.
+ log[level] = levelnr
+
+ -- Add a reverse lookup.
+ log.levels[levelnr] = level
+end
+
+--- @param level string
+--- @param levelnr integer
+--- @return fun(...:any): boolean?
+local function create_logger(level, levelnr)
+ return function(...)
+ if levelnr < current_log_level then
+ return false
+ end
+ local argc = select('#', ...)
+ if argc == 0 then
return true
end
- if openerr then
+ if not open_logfile() then
return false
end
-
- logfile, openerr = io.open(logfilename, 'a+')
- if not logfile then
- local err_msg = string.format('Failed to open LSP client log file: %s', openerr)
- notify(err_msg, vim.log.levels.ERROR)
- return false
+ local info = debug.getinfo(2, 'Sl')
+ local header = string.format(
+ '[%s][%s] ...%s:%s',
+ level,
+ os.date(log_date_format),
+ info.short_src:sub(-16),
+ info.currentline
+ )
+ local parts = { header }
+ for i = 1, argc do
+ local arg = select(i, ...)
+ table.insert(parts, arg == nil and 'nil' or format_func(arg))
end
+ assert(logfile)
+ logfile:write(table.concat(parts, '\t'), '\n')
+ logfile:flush()
+ end
+end
- local log_info = vim.uv.fs_stat(logfilename)
- if log_info and log_info.size > 1e9 then
- local warn_msg = string.format(
- 'LSP client log is large (%d MB): %s',
- log_info.size / (1000 * 1000),
- logfilename
- )
- notify(warn_msg)
- end
+-- If called without arguments, it will check whether the log level is
+-- greater than or equal to this one. When called with arguments, it will
+-- log at that level (if applicable, it is checked either way).
- -- Start message for logging
- logfile:write(string.format('[START][%s] LSP logging initiated\n', os.date(log_date_format)))
- return true
- end
+--- @nodoc
+log.debug = create_logger('DEBUG', log_levels.DEBUG)
- for level, levelnr in pairs(log.levels) do
- -- Also export the log level on the root object.
- log[level] = levelnr
- -- FIXME: DOC
- -- Should be exposed in the vim docs.
- --
- -- Set the lowercase name as the main use function.
- -- If called without arguments, it will check whether the log level is
- -- greater than or equal to this one. When called with arguments, it will
- -- log at that level (if applicable, it is checked either way).
- --
- -- Recommended usage:
- -- ```
- -- local _ = log.warn() and log.warn("123")
- -- ```
- --
- -- This way you can avoid string allocations if the log level isn't high enough.
- if level ~= 'OFF' then
- log[level:lower()] = function(...)
- local argc = select('#', ...)
- if levelnr < current_log_level then
- return false
- end
- if argc == 0 then
- return true
- end
- if not open_logfile() then
- return false
- end
- local info = debug.getinfo(2, 'Sl')
- local header = string.format(
- '[%s][%s] ...%s:%s',
- level,
- os.date(log_date_format),
- string.sub(info.short_src, #info.short_src - 15),
- info.currentline
- )
- local parts = { header }
- for i = 1, argc do
- local arg = select(i, ...)
- if arg == nil then
- table.insert(parts, 'nil')
- else
- table.insert(parts, format_func(arg))
- end
- end
- logfile:write(table.concat(parts, '\t'), '\n')
- logfile:flush()
- end
- end
- end
-end
+--- @nodoc
+log.error = create_logger('ERROR', log_levels.ERROR)
--- This is put here on purpose after the loop above so that it doesn't
--- interfere with iterating the levels
-vim.tbl_add_reverse_lookup(log.levels)
+--- @nodoc
+log.info = create_logger('INFO', log_levels.INFO)
+
+--- @nodoc
+log.trace = create_logger('TRACE', log_levels.TRACE)
+
+--- @nodoc
+log.warn = create_logger('WARN', log_levels.WARN)
--- Sets the current log level.
---@param level (string|integer) One of `vim.lsp.log.levels`
@@ -163,7 +169,7 @@ end
--- Checks whether the level is sufficient for logging.
---@param level integer log level
----@returns (bool) true if would log, false if not
+---@return bool : true if would log, false if not
function log.should_log(level)
return level >= current_log_level
end