Rails 怎让某个特定的 action 不打印 log

bright · 2016年06月21日 · 最后由 quakewang 回复于 2016年06月27日 · 3324 次阅读

系统配置了 nginx 以后 nginx 会对/nginx_check/alive 页面频繁的进行健康检查,(就是一个 get 请求)来确保机器没有 down 掉,但是这会导致大量的无用 log,使得排查问题的时候非常艰难 现在想屏蔽这个这个 action 默认的系统 log(或者把这部分系统 log 重定向到其他文件)

尝试使用 Rails.logger.silence,但是没有效果

class NginxCheckController < ApplicationController
   layout false
   def alive
      Rails.logger.silence do
      end
   end
 end

google 了很久,rails 对 logger 似乎也没有提供 hook,让我们去做事前操作。

不知道该怎么解决,求帮助~

#1 楼 @hw676018683 我前阵子就是参考 quiet_assets 做的,本地开发环境跑没有问题,但是以下这段代码在线上跑时会有问题

def call_with_quiet_assets(env)
  begin
    if env['PATH_INFO'] =~ ASSETS_REGEX
      env[KEY] = Rails.logger.level
      Rails.logger.level = Logger::ERROR
    end
    call_without_quiet_assets(env)
  ensure
    Rails.logger.level = env[KEY] if env[KEY]
  end
end

我猜想是在多进程的情况下,env[KEY] 线程不安全导致,但我本地试了用 puma 起多个 worker 然后写了个脚本并发地试了好多次,都没有办法重现。为了尽快解决这个问题,我不用 env[KEY],而是先直接写死

module QuietHeartbeat
  class Railtie < ::Rails::Railtie
    initializer 'quiet_heartbeat.initialize' do |app|

      PATH_PREFIX_REGEX = /\A(\/heartbeats)/

      Rails::Rack::Logger.class_eval do
        def call_with_quiet_heartbeat_request(env)
          begin
            Rails.logger.level = Logger::ERROR if env['PATH_INFO'] =~ PATH_PREFIX_REGEX
            call_without_quiet_heartbeat_request(env)
          ensure
            Rails.logger.level = Logger::DEBUG
          end
        end
        alias_method_chain :call, :quiet_heartbeat_request
      end
    end
  end
end

另外,可用 rack middleware 的方式实现

# lib/quiet_heartbeats_middleware.rb
class QuietHeartbeatsMiddleware
  PATH_PREFIX_REGEX = /\A(\/heartbeats)/

  def initialize(app)
    @app = app
  end

  def call(env)
    if env['PATH_INFO'] =~ PATH_PREFIX_REGEX
      # 如果你想直接就返回 OK,那么可以
      [200, { 'Context-Type' => 'text/plain' }, ['']]
      # 如果还想要 Rails 处理,那么
      # Rails.logger.silence do
      #   @app.call(env)
      # end
    else
      @app.call(env)
    end
  end
end

嵌入到 middleware stack 里去

# config/application.rb
module Your_Rails_App_name
   class Application < Rails::Application
     require 'quiet_heartbeats_middleware'
     config.middleware.insert_before Rack::Lock, QuietHeartbeatsMiddleware

     # .....
  end
end

持续关注 很多无用 log 挺烦的 不过我提供另外一个思路 就是可以利用 Logger 类定制一些 Log 这样会方便排查一些问题

#3 楼 @so_zengtao 我也是这样做的,自定义了一些 log,在自定义的 log 里可以过滤一些 controller 或者 action,不过也没考虑过怎么去过滤掉系统的 log,关注

#3 楼 @so_zengtao 可以修改定制系统 log 吗?我理解应该是只能修改开发者自己打印的 log 吧?

#5 楼 @bright 自己定义的 log 当然可以定制了,可以控制答应某个 action

# lib/silent_logger.rb

#
# SilentLogger can be used to keep requests out of log file
# It is best used for ping requests from uptime services like
# NewRelic, Uptime Robot, etc.
#
# Source: http://dennisreimann.de/blog/silencing-the-rails-log-on-a-per-action-basis/
#
# Include logger in your config/application.rb after require 'rails/all'
# require File.dirname(__FILE__) + '/../lib/silent_logger.rb'
#
# In application.rb or environments/production.rb swap Rails logger:
# config.middleware.swap Rails::Rack::Logger, SilentLogger, silenced: ['/newrelic-ping']
#
class SilentLogger < Rails::Rack::Logger

  def initialize(app, taggers = nil)
    @app = app
    @taggers = taggers

    if @taggers.keys.include? :silence
      @silenced_paths = @taggers[:silence]
      @taggers.delete(:silence)
    end

    super
  end

  def call(env)
    if env['X-SILENCE-LOGGER'] || @silenced_paths.include?(env['PATH_INFO']) || @silenced_paths.any?{|path| path.is_a?(Regexp) && path.match(env['PATH_INFO'])}
      Rails.logger.silence do
        @app.call(env)
      end
    else
      super(env)
    end
  end

end


config/application.rb

require File.dirname(__FILE__) + '/../lib/silent_logger.rb'
module YourApp
  class Application < Rails::Application
    config.middleware.swap Rails::Rack::Logger, SilentLogger, silence: ['/live-ping', %r'^/assets/']
  end
end

换个方式,用现成的 ^_^ gem 'rack-heartbeat'

需要 登录 后方可回复, 如果你还没有账号请 注册新账号