You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.0 KiB

10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
  1. require_relative 'wrapper'
  2. require_relative 'base'
  3. require 'io/console'
  4. require 'amazing_print'
  5. # Opinioned Rails custom formatter
  6. class BasicFormatter < SemanticLogger::Formatters::Color
  7. NAME_MAX_SIZE = 25
  8. TERMINAL_PREFIX = ENV['TERMINAL_PREFIX'].to_i || 0
  9. CONTENT_PREFIX = '---'.freeze
  10. ANSI_DEBUG = "\e[90m".freeze
  11. ANSI_INFO = SemanticLogger::AnsiColors::GREEN
  12. ANSI_WARN = SemanticLogger::AnsiColors::YELLOW
  13. ANSI_ERROR = "\e[91m".freeze
  14. ANSI_NEUTRAL_INFO = SemanticLogger::AnsiColors::WHITE
  15. ANSI_REVERSED_WARNING = "\e[0;30;43m".freeze
  16. ANSI_REVERSED_ERROR = "\e[1;30;41m".freeze
  17. ANSI_REVERSED_FATAL = "\e[1;30;41m".freeze
  18. CONTENT_COLOR_MAP = ColorMap.new(
  19. debug: ANSI_DEBUG,
  20. info: ANSI_NEUTRAL_INFO,
  21. warn: ANSI_REVERSED_WARNING,
  22. error: ANSI_REVERSED_ERROR,
  23. fatal: ANSI_REVERSED_FATAL
  24. )
  25. EXCLUDE_LAMBDA = lambda { |log|
  26. if log.name == 'ActionView::Base'
  27. !log.message.starts_with?(' Rendering')
  28. elsif log.name == 'Rails' && !log.message.nil?
  29. log.message.exclude?('Started GET "/rails/live/reload')
  30. else
  31. true
  32. end
  33. }
  34. def initialize
  35. super(color_map: ColorMap.new(
  36. debug: ANSI_DEBUG,
  37. info: ANSI_INFO,
  38. warn: ANSI_WARN,
  39. error: ANSI_ERROR,
  40. fatal: ANSI_ERROR
  41. ))
  42. end
  43. def message
  44. return unless log.message
  45. message = log.message
  46. if log.name == 'Rails' && message.starts_with?('Completed')
  47. message.rstrip!
  48. message += "\n" unless message.starts_with?('Completed 5')
  49. end
  50. wrap_length = compute_useful_length
  51. md = message.match(/^\s*/)
  52. space_prefix = md.match(0)
  53. message = md.post_match
  54. message = Wrapper.wrap("#{CONTENT_COLOR_MAP[log.level]}#{message}", before_message(true) + space_prefix.to_s,
  55. wrap_length - md.end(0))
  56. "#{CONTENT_COLOR_MAP[log.level]}#{space_prefix}#{message}#{color_map.clear}"
  57. end
  58. def payload
  59. return unless log.payload
  60. lines = log.payload.ai(ruby19_syntax: true, indent: 2).split("\n")
  61. first_line = lines.shift
  62. space_prefix = first_line.match(/^\s*/)
  63. lines = lines.map do |l|
  64. "#{before_message(true)}#{space_prefix}#{l}"
  65. end
  66. result = lines.unshift(first_line).join("\n")
  67. result.sub!(/\s*/) { |m| '-' * m.length }
  68. result
  69. end
  70. def level
  71. case log.level
  72. when :info, :debug then draw_rails ' '
  73. else draw_rails(log.level.to_s.chr.upcase)
  74. end
  75. end
  76. def draw_rails(char)
  77. "#{color}#{char}#{color_map.clear}"
  78. end
  79. def continuation
  80. draw_rails('┆')
  81. end
  82. def name
  83. "#{ANSI_DEBUG}#{log.name.truncate(NAME_MAX_SIZE).center(NAME_MAX_SIZE)}#{color_map.clear}"
  84. end
  85. def exception # rubocop:disable Metrics/AbcSize
  86. return unless log.exception
  87. root_path = Rails.root.to_s
  88. stack = log.exception.backtrace.select { |line| line.starts_with?(root_path) }
  89. stack = stack.map { |line| line.delete_prefix("#{root_path}/") }
  90. "#{ANSI_REVERSED_WARNING}#{log.exception.class}#{color_map.clear} #{ANSI_REVERSED_ERROR}#{log.exception.message}#{color_map.clear}#{backtrace(stack)}" # rubocop:disable Layout/LineLength
  91. end
  92. def call(log, logger)
  93. unless log.message.is_a?(String)
  94. log.payload = log.message
  95. log.message = nil
  96. end
  97. self.color = color_map[log.level]
  98. self.log = log
  99. self.logger = logger
  100. before_message + [message, payload, exception].compact.join(' ')
  101. end
  102. private
  103. def compute_useful_length
  104. wrap_length = IO.console.winsize[1] - TERMINAL_PREFIX - before_message.length + CONTENT_PREFIX.length + 12
  105. rescue StandardError
  106. wrap_length = 100 # FIXME: CONSTANTIZE, only useful in DEBUGGER, no IO.console detected!
  107. end
  108. def before_message(wrapped = false)
  109. [name, wrapped ? continuation : level, tags, named_tags, duration].compact.join(' ') + CONTENT_PREFIX
  110. end
  111. def backtrace(stack)
  112. nil unless stack.count.positive?
  113. "\n#{before_message} #{ANSI_ERROR}#{stack.join("\n#{before_message} #{ANSI_ERROR}")}#{color_map.clear}"
  114. end
  115. def color_content
  116. color
  117. end
  118. end