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.

144 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
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. WRAP_PREFIX = ' … '.freeze
  11. ANSI_DEBUG = "\e[90m".freeze
  12. ANSI_INFO = SemanticLogger::AnsiColors::GREEN
  13. ANSI_WARN = SemanticLogger::AnsiColors::YELLOW
  14. ANSI_ERROR = "\e[91m".freeze
  15. ANSI_NEUTRAL_INFO = SemanticLogger::AnsiColors::WHITE
  16. ANSI_REVERSED_WARNING = "\e[0;30;43m".freeze
  17. ANSI_REVERSED_ERROR = "\e[1;30;41m".freeze
  18. ANSI_REVERSED_FATAL = "\e[1;30;41m".freeze
  19. CONTENT_COLOR_MAP = ColorMap.new(
  20. debug: ANSI_DEBUG,
  21. info: ANSI_NEUTRAL_INFO,
  22. warn: ANSI_REVERSED_WARNING,
  23. error: ANSI_REVERSED_ERROR,
  24. fatal: ANSI_REVERSED_FATAL
  25. )
  26. EXCLUDE_LAMBDA = lambda { |log|
  27. if log.name == 'ActionView::Base'
  28. !log.message.starts_with?(' Rendering')
  29. elsif log.name == 'Rails' && !log.message.nil?
  30. log.message.exclude?('Started GET "/rails/live/reload')
  31. else
  32. true
  33. end
  34. }
  35. def initialize
  36. super(color_map: ColorMap.new(
  37. debug: ANSI_DEBUG,
  38. info: ANSI_INFO,
  39. warn: ANSI_WARN,
  40. error: ANSI_ERROR,
  41. fatal: ANSI_ERROR
  42. ))
  43. end
  44. def message
  45. return unless log.message
  46. message = log.message
  47. if log.name == 'Rails' && message.starts_with?('Completed')
  48. message.rstrip!
  49. message += "\n" unless message.starts_with?('Completed 5')
  50. end
  51. wrap_length = compute_useful_length
  52. space_prefix = message.match(/^\s*/)
  53. message = Wrapper.wrap("#{CONTENT_COLOR_MAP[log.level]}#{message}", before_message(true, space_prefix),
  54. wrap_length - space_prefix.length)
  55. "#{CONTENT_COLOR_MAP[log.level]}#{message}#{color_map.clear}"
  56. end
  57. def payload
  58. return unless log.payload
  59. lines = log.payload.ai(ruby19_syntax: true, indent: 2).split("\n")
  60. first_line = lines.shift
  61. lines = lines.map do |l|
  62. space_prefix = l.match(/^\s*/)
  63. "#{before_message(true, space_prefix)}#{l}"
  64. end
  65. lines.unshift(first_line).join("\n")
  66. end
  67. def level
  68. level = case log.level
  69. when :info then ' '
  70. when :debug then 'd'
  71. else log.level.to_s.chr.upcase
  72. end
  73. "#{color}#{level}#{color_map.clear}"
  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. unless log.message.is_a?(String)
  87. log.payload = log.message
  88. log.message = nil
  89. end
  90. self.color = color_map[log.level]
  91. self.log = log
  92. self.logger = logger
  93. self.log.payload =
  94. [before_message, message, payload, exception].compact.join(' ')
  95. end
  96. private
  97. def compute_useful_length
  98. wrap_length = IO.console.winsize[1] - TERMINAL_PREFIX - before_message.length + CONTENT_PREFIX.length + 12
  99. rescue StandardError
  100. wrap_length = 100
  101. end
  102. def before_message(wrapped = false, prefix = '')
  103. result = [name, level, tags, named_tags, duration, CONTENT_PREFIX].compact.join(' ')
  104. if wrapped
  105. [name, level, tags, named_tags, duration,
  106. "#{color}#{WRAP_PREFIX}#{color_map.clear}#{prefix}"].compact.join(' ')
  107. else
  108. [name, level, tags, named_tags, duration,
  109. CONTENT_PREFIX].compact.join(' ')
  110. end
  111. end
  112. def backtrace(stack)
  113. nil unless stack.count.positive?
  114. "\n#{before_message} #{ANSI_ERROR}#{stack.join("\n#{before_message} #{ANSI_ERROR}")}#{color_map.clear}"
  115. end
  116. def color_content
  117. color
  118. end
  119. end