pvincent
2 months ago
4 changed files with 159 additions and 141 deletions
-
27lib/semantic/dev_loader.rb
-
124lib/semantic/log_subscriber.rb
-
130lib/semantic/subscribers/action_controller.rb
-
19lib/semantic/subscribers/action_view.rb
@ -1,124 +0,0 @@ |
|||||
module Semantic |
|
||||
class LogSubscriber < ActiveSupport::LogSubscriber |
|
||||
include SemanticLogger::Loggable |
|
||||
include AnsiColors |
|
||||
def logger = SemanticLogger['Rails'] |
|
||||
|
|
||||
INTERNAL_PARAMS = %i[controller action format _method only_path].freeze |
|
||||
DEFAULT_DEV_HOSTS = ['127.0.0.1', 'localhost'].freeze |
|
||||
TERMINUS_STRING = '╙─╜'.freeze |
|
||||
|
|
||||
def initialize(session_key) |
|
||||
@session_key = session_key |
|
||||
@transactions = {} |
|
||||
super() |
|
||||
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::AnsiDimensions.new(rails: '╓─╖', before: 1) |
|
||||
if defined?(@previously_redirect) && @previously_redirect |
|
||||
dimensions = Semantic::AnsiDimensions.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_YELLOW}\\1#{CLEAR}\"") |
|
||||
end |
|
||||
logger.debug("Parameters: #{params}") unless params.empty? |
|
||||
end |
|
||||
end |
|
||||
|
|
||||
def finish_processing(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 << "GC: #{event.gc_time.round(1)}ms" |
|
||||
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 |
|
||||
logger.debug process_duration(event, additions) |
|
||||
end |
|
||||
|
|
||||
status_family = status / 100 |
|
||||
|
|
||||
dimensions = case status_family |
|
||||
when 2 |
|
||||
Semantic::AnsiDimensions.new(rails: TERMINUS_STRING) |
|
||||
when 3 |
|
||||
Semantic::AnsiDimensions.new(rails: '╙║╜') |
|
||||
when 4 |
|
||||
Semantic::AnsiDimensions.new(rails: '╙╨╜') |
|
||||
when 5 |
|
||||
Semantic::AnsiDimensions.new(rails: '╙║╜') |
|
||||
end |
|
||||
logger.info("Completed #{colorize(status, BOLD)} #{Rack::Utils::HTTP_STATUS_CODES[status]}", dimensions:) |
|
||||
logger.info(' ', dimensions: Semantic::AnsiDimensions.new(rails: ' ║ ')) if status_family == 3 |
|
||||
logger.info(' ', dimensions: Semantic::AnsiDimensions.new(rails: '╓║╖')) if status_family == 5 |
|
||||
end |
|
||||
end |
|
||||
|
|
||||
def redirect_to(event) |
|
||||
SemanticLogger.tagged(@transactions[event.transaction_id]) do |
|
||||
location = capture_path(event.payload[:location]) |
|
||||
logger.debug("Redirected to #{colorize(location, BOLD)}") |
|
||||
end |
|
||||
@previously_redirect = true |
|
||||
end |
|
||||
|
|
||||
def any_hook(event) |
|
||||
SemanticLogger.tagged(@transactions[event.transaction_id]) do |
|
||||
logger.warn("action_controller hook=<#{event.name.split('.')[0]}> needs a proper message handling!", |
|
||||
event.payload.keys) |
|
||||
end |
|
||||
end |
|
||||
|
|
||||
private |
|
||||
|
|
||||
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 |
|
@ -0,0 +1,130 @@ |
|||||
|
module Semantic |
||||
|
module Subscribers |
||||
|
class ActionController < ActiveSupport::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 |
||||
|
|
||||
|
attr_reader :logger |
||||
|
|
||||
|
def initialize(session_key) |
||||
|
@session_key = session_key |
||||
|
@transactions = {} |
||||
|
|
||||
|
short_name = self.class.to_s.split('::').last |
||||
|
@logger = SemanticLogger[short_name] |
||||
|
|
||||
|
super() |
||||
|
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::AnsiDimensions.new(rails: '╓─╖', before: 1) |
||||
|
if defined?(@previously_redirect) && @previously_redirect |
||||
|
dimensions = Semantic::AnsiDimensions.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_YELLOW}\\1#{CLEAR}\"") |
||||
|
end |
||||
|
logger.debug("Parameters: #{params}") unless params.empty? |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
def finish_processing(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 << "GC: #{event.gc_time.round(1)}ms" |
||||
|
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 |
||||
|
logger.debug process_duration(event, additions) |
||||
|
end |
||||
|
|
||||
|
status_family = status / 100 |
||||
|
|
||||
|
dimensions = case status_family |
||||
|
when 2 |
||||
|
Semantic::AnsiDimensions.new(rails: TERMINUS_STRING) |
||||
|
when 3 |
||||
|
Semantic::AnsiDimensions.new(rails: '╙║╜') |
||||
|
when 4 |
||||
|
Semantic::AnsiDimensions.new(rails: '╙╨╜') |
||||
|
when 5 |
||||
|
Semantic::AnsiDimensions.new(rails: '╙║╜') |
||||
|
end |
||||
|
logger.info("Completed #{colorize(status, BOLD)} #{Rack::Utils::HTTP_STATUS_CODES[status]}", dimensions:) |
||||
|
logger.info(' ', dimensions: Semantic::AnsiDimensions.new(rails: ' ║ ')) if status_family == 3 |
||||
|
logger.info(' ', dimensions: Semantic::AnsiDimensions.new(rails: '╓║╖')) if status_family == 5 |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
def redirect_to(event) |
||||
|
SemanticLogger.tagged(@transactions[event.transaction_id]) do |
||||
|
location = capture_path(event.payload[:location]) |
||||
|
logger.debug("Redirected to #{colorize(location, BOLD)}") |
||||
|
end |
||||
|
@previously_redirect = true |
||||
|
end |
||||
|
|
||||
|
def any_hook(event) |
||||
|
SemanticLogger.tagged(@transactions[event.transaction_id]) do |
||||
|
logger.warn("action_controller hook=<#{event.name.split('.')[0]}> needs a proper message handling!", |
||||
|
event.payload.keys) |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
private |
||||
|
|
||||
|
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 |
@ -0,0 +1,19 @@ |
|||||
|
module Semantic |
||||
|
module Subscribers |
||||
|
class ActionView < ActiveSupport::LogSubscriber |
||||
|
include AnsiColors |
||||
|
|
||||
|
attr_reader :logger |
||||
|
|
||||
|
def initialize |
||||
|
short_name = self.class.to_s.split('::').last |
||||
|
@logger = SemanticLogger[short_name] |
||||
|
super |
||||
|
end |
||||
|
|
||||
|
def render_partial(event) |
||||
|
logger.info('Rendered partial', event) |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
end |
Write
Preview
Loading…
Cancel
Save
Reference in new issue