log.lua 4.9 KB

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