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.

152 lines
4.3 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
  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 # rubocop:disable Metrics/ClassLength
  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. elsif log.name == 'ActiveRecord::Base'
  31. log.message.exclude?('↳ lib/formatters/basic_formatter.rb')
  32. else
  33. true
  34. end
  35. }
  36. def initialize
  37. super(color_map: ColorMap.new(
  38. debug: ANSI_DEBUG,
  39. info: ANSI_INFO,
  40. warn: ANSI_WARN,
  41. error: ANSI_ERROR,
  42. fatal: ANSI_ERROR
  43. ))
  44. end
  45. def message
  46. return unless log.message
  47. message = transform_message(log)
  48. message = wrap_message(message)
  49. "#{CONTENT_COLOR_MAP[log.level]}#{message}#{color_map.clear}"
  50. end
  51. def payload
  52. return unless log.payload
  53. lines = log.payload.ai(ruby19_syntax: true, indent: 2).split("\n")
  54. first_line = lines.shift
  55. space_prefix = first_line.match(/^\s*/)
  56. lines = lines.map do |l|
  57. "#{before_message(wrapped: true)}#{space_prefix}#{l}"
  58. end
  59. result = lines.unshift(first_line).join("\n")
  60. result.sub!(/\s*/) { |m| '-' * m.length }
  61. result
  62. end
  63. def level
  64. case log.level
  65. when :info, :debug then draw_rails ' '
  66. else draw_rails(log.level.to_s.chr.upcase)
  67. end
  68. end
  69. def draw_rails(char)
  70. "#{color}#{char}#{color_map.clear}"
  71. end
  72. def continuation
  73. draw_rails('┆')
  74. end
  75. def name
  76. "#{ANSI_DEBUG}#{log.name.truncate(NAME_MAX_SIZE).center(NAME_MAX_SIZE)}#{color_map.clear}"
  77. end
  78. def exception # rubocop:disable Metrics/AbcSize
  79. return unless log.exception
  80. root_path = Rails.root.to_s
  81. stack = log.exception.backtrace.select { |line| line.starts_with?(root_path) }
  82. stack = stack.map { |line| line.delete_prefix("#{root_path}/") }
  83. "#{ANSI_REVERSED_WARNING}#{log.exception.class}#{color_map.clear} #{ANSI_REVERSED_ERROR}#{log.exception.message}#{color_map.clear}#{backtrace(stack)}" # rubocop:disable Layout/LineLength
  84. end
  85. def call(log, logger)
  86. self.color = color_map[log.level]
  87. self.log = log
  88. self.logger = logger
  89. before_message + [message, payload, exception].compact.join(' ')
  90. end
  91. private
  92. def wrap_message(message)
  93. message, space_prefix = split_spaces_in_front(message)
  94. message = Wrapper.wrap("#{CONTENT_COLOR_MAP[log.level]}#{message}",
  95. before_message(wrapped: true) + space_prefix.to_s,
  96. compute_useful_length - space_prefix.length)
  97. "#{space_prefix}#{message}"
  98. end
  99. def split_spaces_in_front(message)
  100. md = message.match(/^\s*/)
  101. [md.post_match, md.match(0)]
  102. end
  103. def transform_message(log)
  104. return log.message unless log.name == 'Rails' && log.message.starts_with?('Completed')
  105. message = log.message.rstrip!
  106. message += "\n" unless message.starts_with?('Completed 5')
  107. message
  108. end
  109. def compute_useful_length
  110. IO.console.winsize[1] - TERMINAL_PREFIX - before_message.length + CONTENT_PREFIX.length + 12
  111. rescue StandardError
  112. 100 # FIXME: CONSTANTIZE, only useful in DEBUGGER, no IO.console detected!
  113. end
  114. def before_message(wrapped: false)
  115. [name, wrapped ? continuation : level, tags, named_tags, duration].compact.join(' ') + CONTENT_PREFIX
  116. end
  117. def backtrace(stack)
  118. nil unless stack.count.positive?
  119. "\n#{before_message} #{ANSI_ERROR}#{stack.join("\n#{before_message} #{ANSI_ERROR}")}#{color_map.clear}"
  120. end
  121. def color_content
  122. color
  123. end
  124. end