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.
135 lines
5.3 KiB
135 lines
5.3 KiB
module Semantic
|
|
module Subscribers
|
|
# LogSubscriber for event_group :action_controller
|
|
class ActionController < LogSubscriber
|
|
include AnsiColors
|
|
|
|
INTERNAL_PARAMS = %i[controller action format _method only_path].freeze
|
|
DEFAULT_DEV_HOSTS = ['127.0.0.1', 'localhost'].freeze
|
|
TERMINUS_STRING = '╙─╜'.freeze
|
|
|
|
# options = { main_session_tag: 'ANY_SESSION_KEY' }
|
|
def initialize(**options)
|
|
super(:controller)
|
|
@session_key = options[:main_session_tag]
|
|
@transactions = {}
|
|
end
|
|
|
|
def start_processing(event)
|
|
session_value = session_value(event)
|
|
@transactions[event.transaction_id] = session_value # preserve session_value to help finish_processing
|
|
|
|
SemanticLogger.tagged(session_value) do
|
|
request = event.payload[:request]
|
|
path = colorize(request.filtered_path, BOLD)
|
|
|
|
dimensions = Semantic::FancyDimensions.new(rails: '╓─╖', before: 1)
|
|
if defined?(@previously_redirect) && @previously_redirect
|
|
dimensions = Semantic::FancyDimensions.new(rails: '╓║╖', before: 0)
|
|
@previously_redirect = false
|
|
end
|
|
logger.info("Started #{request.raw_request_method} #{path}", dimensions:)
|
|
|
|
format = event.payload[:format]
|
|
format = format.to_s.upcase if format.is_a?(Symbol)
|
|
format = '*/*' if format.nil?
|
|
format = colorize(format, BOLD)
|
|
logger.debug("Processing by #{event.payload[:controller]}##{event.payload[:action]} as #{format}")
|
|
|
|
params = event.payload[:params].deep_symbolize_keys.except(*INTERNAL_PARAMS)
|
|
unless params.empty?
|
|
params = params.ai(ruby19_syntax: true, plain: true, multiline: false)
|
|
params.gsub!(/(\w+):/, "#{TEXT_CYAN}\\1#{CLEAR}:")
|
|
params.gsub!(/"(.*?)"/, "\"#{TEXT_BROWN}\\1#{CLEAR}\"")
|
|
end
|
|
logger.debug("Parameters: #{params}") unless params.empty?
|
|
end
|
|
end
|
|
|
|
def process_action(event)
|
|
session_value = @transactions.delete(event.transaction_id) # delete previous session_value from start_processing
|
|
SemanticLogger.tagged(session_value) do
|
|
payload = event.payload
|
|
additions = ::ActionController::Base.log_process_action(payload)
|
|
status = payload[:status]
|
|
|
|
if status.nil? && (exception_class_name = payload[:exception]&.first)
|
|
status = ::ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
|
end
|
|
|
|
additions << pop_active_record_summary
|
|
additions << "GC: #{event.gc_time.round(1)}ms"
|
|
additions.compact!
|
|
|
|
if event.duration >= 1200
|
|
logger.error process_duration(event, additions)
|
|
elsif event.duration >= 600
|
|
logger.warn process_duration(event, additions)
|
|
elsif event.duration >= 300
|
|
logger.info process_duration(event, additions)
|
|
# elsif event.duration >= 100
|
|
else
|
|
logger.debug process_duration(event, additions)
|
|
end
|
|
|
|
status_family = status / 100
|
|
|
|
dimensions = case status_family
|
|
when 2
|
|
Semantic::FancyDimensions.new(rails: TERMINUS_STRING)
|
|
when 3, 5
|
|
Semantic::FancyDimensions.new(rails: '╙║╜')
|
|
when 4
|
|
Semantic::FancyDimensions.new(rails: '╙╨╜')
|
|
end
|
|
logger.info("Completed #{colorize(status, BOLD)} #{Rack::Utils::HTTP_STATUS_CODES[status]}", dimensions:)
|
|
logger.info(' ', dimensions: Semantic::FancyDimensions.new(rails: ' ║ ')) if status_family == 3
|
|
logger.info(' ', dimensions: Semantic::FancyDimensions.new(rails: '╓║╖')) if status_family == 5
|
|
end
|
|
end
|
|
|
|
def redirect_to(event)
|
|
location = capture_path(event.payload[:location])
|
|
logger.debug("Redirected to #{colorize(location, BOLD)}")
|
|
@previously_redirect = true
|
|
end
|
|
|
|
private
|
|
|
|
# FIXME: might be more accurate, multiple transactions, sum of CRUD
|
|
def pop_active_record_summary
|
|
active_record_transactions = Thread.current[ActiveRecord.to_s]
|
|
return unless active_record_transactions
|
|
|
|
# reset thread local
|
|
Thread.current[ActiveRecord.to_s] = nil
|
|
|
|
active_record_transactions.map do |k, art|
|
|
art.except(:total_duration)
|
|
.select { |_, value| value.positive? }
|
|
.map { |k, v| "#{v} #{k.to_s.pluralize(v)}" }
|
|
.join(',')
|
|
end.compact.join('|')
|
|
end
|
|
|
|
def redirect_regex
|
|
return @redirect_regex if defined?(@redirect_regex)
|
|
|
|
options = Rails.application.routes.default_url_options
|
|
dev_hosts = DEFAULT_DEV_HOSTS + Array.wrap(options[:host])
|
|
dev_hosts_or = dev_hosts.uniq.join('|')
|
|
dev_from = "http://(?:#{dev_hosts_or}):#{options[:port]}(.*)"
|
|
|
|
@redirect_regex = /^#{dev_from}/
|
|
end
|
|
|
|
def capture_path(url)
|
|
m = redirect_regex.match(url)
|
|
m.nil? ? url : m[1]
|
|
end
|
|
|
|
def session_value(event) = event.payload[:headers]['rack.session'].fetch(@session_key, nil)
|
|
def process_duration(event, additions) = "Processed in #{event.duration.round}ms (#{additions.join(' | ')})"
|
|
end
|
|
end
|
|
end
|