log.lua 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. local Log = {}
  2. local logfile = string.format("%s/%s.log", vim.fn.stdpath "cache", "lvim")
  3. local in_headless = #vim.api.nvim_list_uis() == 0
  4. Log.levels = {
  5. TRACE = 1,
  6. DEBUG = 2,
  7. INFO = 3,
  8. WARN = 4,
  9. ERROR = 5,
  10. }
  11. vim.tbl_add_reverse_lookup(Log.levels)
  12. function Log:init()
  13. local status_ok, structlog = pcall(require, "structlog")
  14. if not status_ok then
  15. return nil
  16. end
  17. local nvim_notify_params = {}
  18. local nvim_notify_params_injecter = function(_, entry)
  19. for key, value in pairs(nvim_notify_params) do
  20. entry[key] = value
  21. end
  22. return entry
  23. end
  24. local nvim_notify_default_namer = function(logger, entry)
  25. entry["title"] = logger.name
  26. return entry
  27. end
  28. nvim_notify_params_injecter(nil, {})
  29. local log_level = Log.levels[(lvim.log.level):upper() or "WARN"]
  30. local lvim_log = {
  31. lvim = {
  32. sinks = {
  33. structlog.sinks.Console(log_level, {
  34. async = false,
  35. processors = {
  36. structlog.processors.Namer(),
  37. structlog.processors.StackWriter({ "line", "file" }, { max_parents = 0, stack_level = 2 }),
  38. structlog.processors.Timestamper "%H:%M:%S",
  39. },
  40. formatter = structlog.formatters.FormatColorizer( --
  41. "%s [%-5s] %s: %-30s",
  42. { "timestamp", "level", "logger_name", "msg" },
  43. { level = structlog.formatters.FormatColorizer.color_level() }
  44. ),
  45. }),
  46. structlog.sinks.File(Log.levels.TRACE, logfile, {
  47. processors = {
  48. structlog.processors.Namer(),
  49. structlog.processors.StackWriter({ "line", "file" }, { max_parents = 3, stack_level = 2 }),
  50. structlog.processors.Timestamper "%H:%M:%S",
  51. },
  52. formatter = structlog.formatters.Format( --
  53. "%s [%-5s] %s: %-30s",
  54. { "timestamp", "level", "logger_name", "msg" }
  55. ),
  56. }),
  57. },
  58. },
  59. }
  60. if not in_headless and lvim.builtin.notify.active then
  61. table.insert(
  62. lvim_log.lvim.sinks,
  63. structlog.sinks.NvimNotify(Log.levels.INFO, {
  64. processors = {
  65. nvim_notify_default_namer,
  66. nvim_notify_params_injecter,
  67. },
  68. formatter = structlog.formatters.Format( --
  69. "%s",
  70. { "msg" },
  71. { blacklist_all = true }
  72. ),
  73. params_map = {
  74. icon = "icon",
  75. keep = "keep",
  76. on_open = "on_open",
  77. on_close = "on_close",
  78. timeout = "timeout",
  79. title = "title",
  80. },
  81. })
  82. )
  83. end
  84. structlog.configure(lvim_log)
  85. local logger = structlog.get_logger "lvim"
  86. if not in_headless and lvim.builtin.notify.active and lvim.log.override_notify then
  87. -- Overwrite vim.notify to use the logger
  88. vim.notify = function(msg, vim_log_level, opts)
  89. nvim_notify_params = vim.tbl_deep_extend("force", lvim.builtin.notify.opts, opts)
  90. -- vim_log_level can be omitted
  91. if vim_log_level == nil then
  92. vim_log_level = Log.levels["INFO"]
  93. end
  94. if type(vim_log_level) == "string" then
  95. vim_log_level = Log.levels[(vim_log_level):upper() or "INFO"]
  96. end
  97. -- https://github.com/neovim/neovim/blob/685cf398130c61c158401b992a1893c2405cd7d2/runtime/lua/vim/lsp/log.lua#L5
  98. logger:log(vim_log_level + 1, msg)
  99. end
  100. end
  101. return logger
  102. end
  103. --- Adds a log entry using Plenary.log
  104. ---@fparam msg any
  105. ---@param level string [same as vim.log.log_levels]
  106. function Log:add_entry(level, msg, event)
  107. if self.__handle then
  108. self.__handle:log(level, vim.inspect(msg), event)
  109. return
  110. end
  111. local logger = self:init()
  112. if not logger then
  113. return
  114. end
  115. self.__handle = logger
  116. self.__handle:log(level, vim.inspect(msg), event)
  117. end
  118. ---Retrieves the path of the logfile
  119. ---@return string path of the logfile
  120. function Log:get_path()
  121. return logfile
  122. end
  123. ---Add a log entry at TRACE level
  124. ---@param msg any
  125. ---@param event any
  126. function Log:trace(msg, event)
  127. self:add_entry(self.levels.TRACE, msg, event)
  128. end
  129. ---Add a log entry at DEBUG level
  130. ---@param msg any
  131. ---@param event any
  132. function Log:debug(msg, event)
  133. self:add_entry(self.levels.DEBUG, msg, event)
  134. end
  135. ---Add a log entry at INFO level
  136. ---@param msg any
  137. ---@param event any
  138. function Log:info(msg, event)
  139. self:add_entry(self.levels.INFO, msg, event)
  140. end
  141. ---Add a log entry at WARN level
  142. ---@param msg any
  143. ---@param event any
  144. function Log:warn(msg, event)
  145. self:add_entry(self.levels.WARN, msg, event)
  146. end
  147. ---Add a log entry at ERROR level
  148. ---@param msg any
  149. ---@param event any
  150. function Log:error(msg, event)
  151. self:add_entry(self.levels.ERROR, msg, event)
  152. end
  153. setmetatable({}, Log)
  154. return Log