|
| 1 | +require 'action_controller/log_subscriber' |
| 2 | +ActionController::LogSubscriber |
| 3 | + |
| 4 | +module RailsSemanticLogger |
| 5 | + module ActionController |
| 6 | + class LogSubscriber < ActiveSupport::LogSubscriber |
| 7 | + INTERNAL_PARAMS = %w(controller action format _method only_path) |
| 8 | + |
| 9 | + # Log as debug to hide Processing messages in production |
| 10 | + def start_processing(event) |
| 11 | + controller_logger(event).debug { "Processing ##{event.payload[:action]}" } |
| 12 | + end |
| 13 | + |
| 14 | + def process_action(event) |
| 15 | + controller_logger(event).info do |
| 16 | + payload = event.payload.dup |
| 17 | + |
| 18 | + # Unused, but needed for Devise 401 status code monkey patch to still work. |
| 19 | + ::ActionController::Base.log_process_action(payload) |
| 20 | + |
| 21 | + # According to PR https://github.com/rocketjob/rails_semantic_logger/pull/37/files |
| 22 | + # payload[:params] is not always a Hash. |
| 23 | + payload[:params] = payload[:params].to_unsafe_h unless payload[:params].is_a?(Hash) |
| 24 | + payload[:params].except!(*INTERNAL_PARAMS) |
| 25 | + payload.delete(:params) if payload[:params].empty? |
| 26 | + |
| 27 | + format = payload[:format] |
| 28 | + payload[:format] = format.to_s.upcase if format.is_a?(Symbol) |
| 29 | + |
| 30 | + payload[:path] = extract_path(payload[:path]) if payload.has_key?(:path) |
| 31 | + |
| 32 | + exception = payload.delete(:exception) |
| 33 | + if payload[:status].nil? && exception.present? |
| 34 | + exception_class_name = exception.first |
| 35 | + payload[:status] = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name) |
| 36 | + end |
| 37 | + |
| 38 | + # Rounds off the runtimes. For example, :view_runtime, :mongo_runtime, etc. |
| 39 | + payload.keys.each do |key| |
| 40 | + payload[key] = payload[key].to_f.round(2) if key.to_s.match(/(.*)_runtime/) |
| 41 | + end |
| 42 | + |
| 43 | + payload[:status_message] = ::Rack::Utils::HTTP_STATUS_CODES[payload[:status]] if payload[:status].present? |
| 44 | + # Causes excessive log output with Rails 5 RC1 |
| 45 | + payload.delete(:headers) |
| 46 | + |
| 47 | + { |
| 48 | + message: "Completed ##{payload[:action]}", |
| 49 | + duration: event.duration, |
| 50 | + payload: payload |
| 51 | + } |
| 52 | + end |
| 53 | + end |
| 54 | + |
| 55 | + def halted_callback(event) |
| 56 | + controller_logger(event).info { "Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected" } |
| 57 | + end |
| 58 | + |
| 59 | + def send_file(event) |
| 60 | + controller_logger(event).info('Sent file') { {path: event.payload[:path], duration: event.duration} } |
| 61 | + end |
| 62 | + |
| 63 | + def redirect_to(event) |
| 64 | + controller_logger(event).info('Redirected to') { {location: event.payload[:location]} } |
| 65 | + end |
| 66 | + |
| 67 | + def send_data(event) |
| 68 | + controller_logger(event).info('Sent data') { {file_name: event.payload[:filename], duration: event.duration} } |
| 69 | + end |
| 70 | + |
| 71 | + def unpermitted_parameters(event) |
| 72 | + controller_logger(event).debug do |
| 73 | + unpermitted_keys = event.payload[:keys] |
| 74 | + "Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.join(", ")}" |
| 75 | + end |
| 76 | + end |
| 77 | + |
| 78 | + %w(write_fragment read_fragment exist_fragment? |
| 79 | + expire_fragment expire_page write_page).each do |method| |
| 80 | + class_eval <<-METHOD, __FILE__, __LINE__ + 1 |
| 81 | + def #{method}(event) |
| 82 | + # enable_fragment_cache_logging as of Rails 5 |
| 83 | + return if ::ActionController::Base.respond_to?(:enable_fragment_cache_logging) && !::ActionController::Base.enable_fragment_cache_logging |
| 84 | + controller_logger(event).info do |
| 85 | + key_or_path = event.payload[:key] || event.payload[:path] |
| 86 | + {message: "#{method.to_s.humanize} \#{key_or_path}", duration: event.duration} |
| 87 | + end |
| 88 | + end |
| 89 | + METHOD |
| 90 | + end |
| 91 | + |
| 92 | + private |
| 93 | + |
| 94 | + # Returns the logger for the supplied event. |
| 95 | + # Returns ActionController::Base.logger if no controller is present |
| 96 | + def controller_logger(event) |
| 97 | + controller = event.payload[:controller] |
| 98 | + return ::ActionController::Base.logger unless controller |
| 99 | + |
| 100 | + controller.constantize.logger || ::ActionController::Base.logger |
| 101 | + rescue NameError |
| 102 | + ::ActionController::Base.logger |
| 103 | + end |
| 104 | + |
| 105 | + def extract_path(path) |
| 106 | + index = path.index('?') |
| 107 | + index ? path[0, index] : path |
| 108 | + end |
| 109 | + |
| 110 | + end |
| 111 | + end |
| 112 | +end |
0 commit comments