log.lua 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. local Log = {}
  2. Log.levels = {
  3. TRACE = 1,
  4. DEBUG = 2,
  5. INFO = 3,
  6. WARN = 4,
  7. ERROR = 5,
  8. }
  9. vim.tbl_add_reverse_lookup(Log.levels)
  10. local notify_opts = {}
  11. local log_notify_as_notification = false
  12. function Log:set_level(level)
  13. if
  14. not pcall(function()
  15. local logger_ok, logger = pcall(function()
  16. return require("structlog").get_logger "lvim"
  17. end)
  18. local log_level = Log.levels[level:upper()]
  19. if logger_ok and logger and log_level then
  20. for _, pipeline in ipairs(logger.pipelines) do
  21. pipeline.level = log_level
  22. end
  23. end
  24. end)
  25. then
  26. vim.notify "structlog version too old, run `:Lazy sync`"
  27. end
  28. end
  29. function Log:init()
  30. local status_ok, structlog = pcall(require, "structlog")
  31. if not status_ok then
  32. return nil
  33. end
  34. local log_level = Log.levels[(lvim.log.level):upper() or "WARN"]
  35. structlog.configure {
  36. lvim = {
  37. pipelines = {
  38. {
  39. level = log_level,
  40. processors = {
  41. structlog.processors.StackWriter({ "line", "file" }, { max_parents = 0, stack_level = 2 }),
  42. structlog.processors.Timestamper "%H:%M:%S",
  43. },
  44. formatter = structlog.formatters.FormatColorizer(
  45. "%s [%-5s] %s: %-30s",
  46. { "timestamp", "level", "logger_name", "msg" },
  47. { level = structlog.formatters.FormatColorizer.color_level() }
  48. ),
  49. sink = structlog.sinks.Console(false), -- async=false
  50. },
  51. {
  52. level = log_level,
  53. processors = {
  54. structlog.processors.StackWriter({ "line", "file" }, { max_parents = 3, stack_level = 2 }),
  55. structlog.processors.Timestamper "%F %H:%M:%S",
  56. },
  57. formatter = structlog.formatters.Format(
  58. "%s [%-5s] %s: %-30s",
  59. { "timestamp", "level", "logger_name", "msg" }
  60. ),
  61. sink = structlog.sinks.File(self:get_path()),
  62. },
  63. },
  64. },
  65. }
  66. local logger = structlog.get_logger "lvim"
  67. -- Overwrite `vim.notify` to use the logger
  68. if lvim.log.override_notify then
  69. vim.notify = function(msg, vim_log_level, opts)
  70. notify_opts = opts or {}
  71. -- vim_log_level can be omitted
  72. if vim_log_level == nil then
  73. vim_log_level = Log.levels["INFO"]
  74. elseif type(vim_log_level) == "string" then
  75. vim_log_level = Log.levels[(vim_log_level):upper()] or Log.levels["INFO"]
  76. else
  77. -- https://github.com/neovim/neovim/blob/685cf398130c61c158401b992a1893c2405cd7d2/runtime/lua/vim/lsp/log.lua#L5
  78. vim_log_level = vim_log_level + 1
  79. end
  80. self:add_entry(vim_log_level, msg)
  81. end
  82. end
  83. return logger
  84. end
  85. --- Configure the sink in charge of logging notifications
  86. ---@param nvim_notify table The nvim-notify instance
  87. function Log:configure_notifications(nvim_notify)
  88. local status_ok, structlog = pcall(require, "structlog")
  89. if not status_ok then
  90. return
  91. end
  92. local function log_writer(log)
  93. local opts = { title = log.logger_name }
  94. opts = vim.tbl_deep_extend("force", opts, notify_opts)
  95. notify_opts = {}
  96. if log_notify_as_notification then
  97. nvim_notify(log.msg, log.level, opts)
  98. end
  99. end
  100. local notif_pipeline = structlog.Pipeline(
  101. structlog.level.INFO,
  102. {},
  103. structlog.formatters.Format("%s", { "msg" }, { blacklist_all = true }),
  104. structlog.sinks.Adapter(log_writer)
  105. )
  106. self.__handle:add_pipeline(notif_pipeline)
  107. end
  108. --- Adds a log entry using Plenary.log
  109. ---@param level integer [same as vim.log.levels]
  110. ---@param msg any
  111. ---@param event any
  112. function Log:add_entry(level, msg, event)
  113. if
  114. not pcall(function()
  115. local logger = self:get_logger()
  116. if not logger then
  117. return
  118. end
  119. logger:log(level, vim.inspect(msg), event)
  120. end)
  121. then
  122. vim.notify "structlog version too old, run `:Lazy sync`"
  123. end
  124. end
  125. ---Retrieves the handle of the logger object
  126. ---@return table|nil logger handle if found
  127. function Log:get_logger()
  128. local logger_ok, logger = pcall(function()
  129. return require("structlog").get_logger "lvim"
  130. end)
  131. if logger_ok and logger then
  132. return logger
  133. end
  134. logger = self:init()
  135. if not logger then
  136. return
  137. end
  138. self.__handle = logger
  139. return logger
  140. end
  141. ---Retrieves the path of the logfile
  142. ---@return string path of the logfile
  143. function Log:get_path()
  144. return string.format("%s/%s.log", get_cache_dir(), "lvim")
  145. end
  146. ---Add a log entry at TRACE level
  147. ---@param msg any
  148. ---@param event any
  149. function Log:trace(msg, event)
  150. self:add_entry(self.levels.TRACE, msg, event)
  151. end
  152. ---Add a log entry at DEBUG level
  153. ---@param msg any
  154. ---@param event any
  155. function Log:debug(msg, event)
  156. self:add_entry(self.levels.DEBUG, msg, event)
  157. end
  158. ---Add a log entry at INFO level
  159. ---@param msg any
  160. ---@param event any
  161. function Log:info(msg, event)
  162. self:add_entry(self.levels.INFO, msg, event)
  163. end
  164. ---Add a log entry at WARN level
  165. ---@param msg any
  166. ---@param event any
  167. function Log:warn(msg, event)
  168. self:add_entry(self.levels.WARN, msg, event)
  169. end
  170. ---Add a log entry at ERROR level
  171. ---@param msg any
  172. ---@param event any
  173. function Log:error(msg, event)
  174. self:add_entry(self.levels.ERROR, msg, event)
  175. end
  176. setmetatable({}, Log)
  177. return Log