log.lua 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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. function Log:set_level(level)
  12. local logger_ok, logger = pcall(function()
  13. return require("structlog").get_logger "lvim"
  14. end)
  15. local log_level = Log.levels[level:upper()]
  16. if logger_ok and logger and log_level then
  17. for _, s in ipairs(logger.sinks) do
  18. s.level = log_level
  19. end
  20. else
  21. vim.notify_once("Unable to set logger's level to " .. level)
  22. end
  23. local packer_ok, _ = xpcall(function()
  24. require("packer.log").cfg { log = { level = level } }
  25. end, debug.traceback)
  26. if not packer_ok then
  27. vim.notify_once("Unable to set packer's log level to " .. level)
  28. end
  29. end
  30. function Log:init()
  31. local status_ok, structlog = pcall(require, "structlog")
  32. if not status_ok then
  33. return nil
  34. end
  35. local log_level = Log.levels[(lvim.log.level):upper() or "WARN"]
  36. local lvim_log = {
  37. lvim = {
  38. sinks = {
  39. structlog.sinks.Console(log_level, {
  40. async = true,
  41. processors = {
  42. structlog.processors.Namer(),
  43. structlog.processors.StackWriter({ "line", "file" }, { max_parents = 0, stack_level = 2 }),
  44. structlog.processors.Timestamper "%H:%M:%S",
  45. },
  46. formatter = structlog.formatters.FormatColorizer( --
  47. "%s [%-5s] %s: %-30s",
  48. { "timestamp", "level", "logger_name", "msg" },
  49. { level = structlog.formatters.FormatColorizer.color_level() }
  50. ),
  51. }),
  52. structlog.sinks.File(log_level, self:get_path(), {
  53. processors = {
  54. structlog.processors.Namer(),
  55. structlog.processors.StackWriter({ "line", "file" }, { max_parents = 3, stack_level = 2 }),
  56. structlog.processors.Timestamper "%F %H:%M:%S",
  57. },
  58. formatter = structlog.formatters.Format( --
  59. "%s [%-5s] %s: %-30s",
  60. { "timestamp", "level", "logger_name", "msg" }
  61. ),
  62. }),
  63. },
  64. },
  65. }
  66. structlog.configure(lvim_log)
  67. local logger = structlog.get_logger "lvim"
  68. -- Overwrite `vim.notify` to use the logger
  69. if lvim.log.override_notify then
  70. vim.notify = function(msg, vim_log_level, opts)
  71. notify_opts = opts or {}
  72. -- vim_log_level can be omitted
  73. if vim_log_level == nil then
  74. vim_log_level = Log.levels["INFO"]
  75. elseif type(vim_log_level) == "string" then
  76. vim_log_level = Log.levels[(vim_log_level):upper()] or Log.levels["INFO"]
  77. else
  78. -- https://github.com/neovim/neovim/blob/685cf398130c61c158401b992a1893c2405cd7d2/runtime/lua/vim/lsp/log.lua#L5
  79. vim_log_level = vim_log_level + 1
  80. end
  81. self:info(vim_log_level, msg)
  82. end
  83. end
  84. return logger
  85. end
  86. --- Configure the sink in charge of logging notifications
  87. ---@param notif_handle table The implementation used by the sink for displaying the notifications
  88. function Log:configure_notifications(notif_handle)
  89. local status_ok, structlog = pcall(require, "structlog")
  90. if not status_ok then
  91. return
  92. end
  93. local default_namer = function(logger, entry)
  94. entry["title"] = logger.name
  95. return entry
  96. end
  97. local notify_opts_injecter = function(_, entry)
  98. for key, value in pairs(notify_opts) do
  99. entry[key] = value
  100. end
  101. notify_opts = {}
  102. return entry
  103. end
  104. local sink = structlog.sinks.NvimNotify(Log.levels.INFO, {
  105. processors = {
  106. default_namer,
  107. notify_opts_injecter,
  108. },
  109. formatter = structlog.formatters.Format( --
  110. "%s",
  111. { "msg" },
  112. { blacklist_all = true }
  113. ),
  114. -- This should probably not be hard-coded
  115. params_map = {
  116. icon = "icon",
  117. keep = "keep",
  118. on_open = "on_open",
  119. on_close = "on_close",
  120. timeout = "timeout",
  121. title = "title",
  122. },
  123. impl = notif_handle,
  124. })
  125. table.insert(self.__handle.sinks, sink)
  126. end
  127. --- Adds a log entry using Plenary.log
  128. ---@param level integer [same as vim.log.levels]
  129. ---@param msg any
  130. ---@param event any
  131. function Log:add_entry(level, msg, event)
  132. local logger = self:get_logger()
  133. if not logger then
  134. return
  135. end
  136. logger:log(level, vim.inspect(msg), event)
  137. end
  138. ---Retrieves the handle of the logger object
  139. ---@return table|nil logger handle if found
  140. function Log:get_logger()
  141. local logger_ok, logger = pcall(function()
  142. return require("structlog").get_logger "lvim"
  143. end)
  144. if logger_ok and logger then
  145. return logger
  146. end
  147. logger = self:init()
  148. if not logger then
  149. return
  150. end
  151. self.__handle = logger
  152. return logger
  153. end
  154. ---Retrieves the path of the logfile
  155. ---@return string path of the logfile
  156. function Log:get_path()
  157. return string.format("%s/%s.log", get_cache_dir(), "lvim")
  158. end
  159. ---Add a log entry at TRACE level
  160. ---@param msg any
  161. ---@param event any
  162. function Log:trace(msg, event)
  163. self:add_entry(self.levels.TRACE, msg, event)
  164. end
  165. ---Add a log entry at DEBUG level
  166. ---@param msg any
  167. ---@param event any
  168. function Log:debug(msg, event)
  169. self:add_entry(self.levels.DEBUG, msg, event)
  170. end
  171. ---Add a log entry at INFO level
  172. ---@param msg any
  173. ---@param event any
  174. function Log:info(msg, event)
  175. self:add_entry(self.levels.INFO, msg, event)
  176. end
  177. ---Add a log entry at WARN level
  178. ---@param msg any
  179. ---@param event any
  180. function Log:warn(msg, event)
  181. self:add_entry(self.levels.WARN, msg, event)
  182. end
  183. ---Add a log entry at ERROR level
  184. ---@param msg any
  185. ---@param event any
  186. function Log:error(msg, event)
  187. self:add_entry(self.levels.ERROR, msg, event)
  188. end
  189. setmetatable({}, Log)
  190. return Log