log.lua 5.1 KB

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