log.lua 4.9 KB

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