我有一个Rails应用程序,其中一个操作被频繁调用,这在开发时非常不方便,因为它会产生很多我不关心的额外日志输出。如何让Rails不记录任何关于这个操作的日志(控制器、操作、参数、完成时间等)?我也想根据RAILS_ENV进行条件设置,以便在生产环境中记录完整的日志。
谢谢!
我有一个Rails应用程序,其中一个操作被频繁调用,这在开发时非常不方便,因为它会产生很多我不关心的额外日志输出。如何让Rails不记录任何关于这个操作的日志(控制器、操作、参数、完成时间等)?我也想根据RAILS_ENV进行条件设置,以便在生产环境中记录完整的日志。
谢谢!
你可以将Rails日志对象静音:
def action
Rails.logger.silence do
# Things within this block will not be logged...
end
end
silence do ... end
来代替。 - Simon Woker使用lograge
宝石。
Gemfile:
gem 'lograge'
config/application.rb:
config.lograge.enabled = true
config.lograge.ignore_actions = ['StatusController#nginx', ...]
# selective_logger.rb
class SelectiveLogger < Rails::Rack::Logger
def initialize app, opts = {}
@app = app
@opts = opts
@opts[:silenced] ||= []
end
def call env
if @opts[:silenced].include?(env['PATH_INFO']) || @opts[:silenced].any? {|silencer| silencer.is_a?( Regexp) && silencer.match( env['PATH_INFO']) }
Rails.logger.silence do
@app.call env
end
else
super env
end
end
end
告诉Rails使用它:# application.rb
config.middleware.swap Rails::Rack::Logger, SelectiveLogger, :silenced => ["/remote/every_minute", %r"^/assets/"]
上面的示例展示了静音资产服务请求,在开发环境中这意味着需要更少的(有时甚至不需要)向上滚动来查看实际请求。
def silent?(action)
false
end
# this knows more than I'd like about the internals of process, but
# the other options require knowing even more. It would have been
# nice to be able to use logger.silence, but there isn't a good
# method to hook that around, due to the way benchmarking logs.
def log_processing_with_silence_logs
if logger && silent?(action_name) then
@old_logger_level, logger.level = logger.level, Logger::ERROR
end
log_processing_without_silence_logs
end
def process_with_silence_logs(request, response, method = :perform_action, *arguments)
ret = process_without_silence_logs(request, response, method, *arguments)
if logger && silent?(action_name) then
logger.level = @old_logger_level
end
ret
end
alias_method_chain :log_processing, :silence_logs
alias_method_chain :process, :silence_logs
然后,在我想要抑制日志记录的控制器中使用该方法:
def silent?(action)
RAILS_ENV == "development" && ['my_noisy_action'].include?(action)
end
alias_method_chain
在 Ruby 2.0 中已不再使用。 - Kees Briggsgem 'silencer', '>= 1.0.1'
在config/initializers/silencer.rb文件中:
require 'silencer/logger'
Rails.application.configure do
config.middleware.swap Rails::Rack::Logger, Silencer::Logger, silence: ['/api/notifications']
end
以下内容适用于Rails 2.3.14:
创建一个可以被静音的自定义日志记录器:
#selective_logger.rb
require "active_support"
class SelectiveLogger < ActiveSupport::BufferedLogger
attr_accessor :silent
def initialize path_to_log_file
super path_to_log_file
end
def add severity, message = nil, progname = nil, &block
super unless @silent
end
end
告诉Rails使用它:
#environment.rb
config.logger = SelectiveLogger.new config.log_path
#application_controller.rb
# This method is invoked in order to log the lines that begin "Processing..."
# for each new request.
def log_processing
logger.silent = %w"ping time_zone_table".include? params[:action]
super
end
@neil-stockbridge的回答在Rails 6.0上无法使用,我进行了一些修改使其可用
# selective_logger.rb
class SelectiveLogger
def initialize app, opts = {}
@app = app
@opts = opts
@opts[:silenced] ||= []
end
def call env
if @opts[:silenced].include?(env['PATH_INFO']) || @opts[:silenced].any? {|silencer| silencer.is_a?( Regexp) && silencer.match( env['PATH_INFO']) }
Rails.logger.silence do
@app.call env
end
else
@app.call env
end
end
end
测试Rails应用以使用它:
# application.rb
config.middleware.swap Rails::Rack::Logger, SelectiveLogger, :silenced => ["/remote/every_minute", %r"^/assets/"]
Rails 5让请求处理变得更加复杂,需要在多个类中记录日志。首先,我们需要覆盖Logger
类中的call_app
方法,让我们将此文件命名为lib/logger.rb
:
# original class:
# https://github.com/rails/rails/blob/master/railties/lib/rails/rack/logger.rb
require 'rails/rack/logger'
module Rails
module Rack
class Logger < ActiveSupport::LogSubscriber
def call_app(request, env) # :doc:
unless Rails.configuration.logger_exclude.call(request.filtered_path)
instrumenter = ActiveSupport::Notifications.instrumenter
instrumenter.start "request.action_dispatch", request: request
logger.info { started_request_message(request) }
end
status, headers, body = @app.call(env)
body = ::Rack::BodyProxy.new(body) { finish(request) }
[status, headers, body]
rescue Exception
finish(request)
raise
ensure
ActiveSupport::LogSubscriber.flush_all!
end
end
end
end
lib/silent_log_subscriber.rb
:
require 'active_support/log_subscriber'
require 'action_view/log_subscriber'
require 'action_controller/log_subscriber'
# original class:
# https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/log_subscriber.rb
class SilentLogSubscriber < ActiveSupport::LogSubscriber
def start_processing(event)
return unless logger.info?
payload = event.payload
return if Rails.configuration.logger_exclude.call(payload[:path])
params = payload[:params].except(*ActionController::LogSubscriber::INTERNAL_PARAMS)
format = payload[:format]
format = format.to_s.upcase if format.is_a?(Symbol)
info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
info " Parameters: #{params.inspect}" unless params.empty?
end
def process_action(event)
return if Rails.configuration.logger_exclude.call(event.payload[:path])
info do
payload = event.payload
additions = ActionController::Base.log_process_action(payload)
status = payload[:status]
if status.nil? && payload[:exception].present?
exception_class_name = payload[:exception].first
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
end
additions << "Allocations: #{event.allocations}" if event.respond_to? :allocations
message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
message << " (#{additions.join(" | ")})" unless additions.empty?
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
message
end
end
def self.setup
# unsubscribe default processors
ActiveSupport::LogSubscriber.log_subscribers.each do |subscriber|
case subscriber
when ActionView::LogSubscriber
self.unsubscribe(:action_view, subscriber)
when ActionController::LogSubscriber
self.unsubscribe(:action_controller, subscriber)
end
end
end
def self.unsubscribe(component, subscriber)
events = subscriber.public_methods(false).reject { |method| method.to_s == 'call' }
events.each do |event|
ActiveSupport::Notifications.notifier.listeners_for("#{event}.#{component}").each do |listener|
if listener.instance_variable_get('@delegate') == subscriber
ActiveSupport::Notifications.unsubscribe listener
end
end
end
end
end
# subscribe this class
SilentLogSubscriber.attach_to :action_controller
SilentLogSubscriber.setup
rails
之后,在config/application.rb
中加载修改过的模块。require_relative '../lib/logger'
require_relative '../lib/silent_log_subscriber'
Rails.application.configure do
config.logger_exclude = ->(path) { path == "/health" }
end
lograge
宝石,它几乎以相同的方式进行了一些其他修改。尽管Rack::Loggger
自Rails 3以来已更改,因此您可能会失去某些功能。logger.rb
,然后在应用程序中要求它会导致一些冲突。我将其重命名为 silence_logger.rb
,所有问题都得到解决。 - Kkulikovskisconfig/application.rb
中,放在我的应用程序类定义内部:require 'silencer/logger'
initializer 'my_app_name.silence_health_check_request_logging' do |app|
app.config.middleware.swap(
Rails::Rack::Logger,
Silencer::Logger,
app.config.log_tags,
silence: %w[/my_health_check_path /my_other_health_check_path],
)
end
这样就保留了log_tags
配置,并在中间件被冻结之前进行修改。我想把它放在config/initializers/
里面,但还没有想出如何实现。
Sprockets-rails gem 从版本3.1.0
开始引入了quiet assets的实现。不幸的是,目前它还不够灵活,但可以很容易地进行扩展。
创建config/initializers/custom_quiet_assets.rb
文件:
class CustomQuietAssets < ::Sprockets::Rails::QuietAssets
def initialize(app)
super
@assets_regex = %r(\A/{0,2}#{quiet_paths})
end
def quiet_paths
[
::Rails.application.config.assets.prefix, # remove if you don't need to quiet assets
'/ping',
].join('|')
end
end
将其添加到config/application.rb
中间件:
# NOTE: that config.assets.quiet must be set to false (its default value).
initializer :quiet_assets do |app|
app.middleware.insert_before ::Rails::Rack::Logger, CustomQuietAssets
end
已在Rails 4.2上进行测试