log.lua 5.5 KB

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