【问题标题】:How to log user_name in Rails?如何在 Rails 中记录用户名?
【发布时间】:2012-06-04 09:35:12
【问题描述】:

我在 Rails 3 中使用 Devise。我想在 production.log 中查看 current_user 的名称。

我想像这样配置导轨:

config.log_tags = [:user_name]

【问题讨论】:

标签: ruby-on-rails logging devise


【解决方案1】:

我发现这个有点棘手但有效的解决方案。

Warden 将用户信息存储在 session 中,但 req.session 不可用于记录。

所以你必须将它添加到 config/application.rb

config.middleware.delete(ActionDispatch::Cookies)
config.middleware.delete(ActionDispatch::Session::CookieStore)
config.middleware.insert_before(Rails::Rack::Logger, ActionDispatch::Session::CookieStore)
config.middleware.insert_before(ActionDispatch::Session::CookieStore, ActionDispatch::Cookies)

然后创建文件config/initializers/logging.rb

Rails.configuration.log_tags = [
  proc do |req|
    if req.session["warden.user.user.key"].nil?
      "Anonym"
    else
      "user_id:#{req.session["warden.user.user.key"][0][0]}"
    end
  end
]

现在我看到这个是匿名的:

[Anonym] Served asset ...

这是给用户的:

[user_id:1] Served asset ...

【讨论】:

  • @CharlesBergeron 你确定这种方法是否可行?
  • @rit 是的,像这样插入我们自己的自定义日志中间件:require_relative '../lib/my_app/log_user_id'config.middleware.insert_after(ActiveRecord::SessionStore, MyApp::LogUserId)
  • @CharlesBergeron 谢谢!
  • 请注意,在尝试更改 cookie 存储选项时,此方法无法正常工作。你的选择基本上会被丢弃
【解决方案2】:

我使用了 Wojtek Kruszewski 的这个解决方案:https://gist.github.com/WojtekKruszewski

我对我的项目进行了一些调整,只包含 id,但基本相同。

# config/application.rb

config.log_tags = [
  ->(req){
    if user_id = WardenTaggedLogger.extract_user_id_from_request(req)
      user_id.to_s
    else
      "?"
    end
  }
]

并创建这个初始化器

# initializers/warden_tagged_logger.rb

module WardenTaggedLogger
  def self.extract_user_id_from_request(req)
    session_key = Rails.application.config.session_options[:key]
    session_data = req.cookie_jar.encrypted[session_key]
    warden_data = session_data["warden.user.user.key"]
    warden_data[0][0]
    rescue
      nil
  end
end

【讨论】:

  • 这是最干净的方法。
【解决方案3】:

于 2013 年 3 月 7 日添加:

在 Rails 4 中 encrypted_cookie_store 是默认的会话存储。这是您可以访问会话数据的方式:

session_data = req.cookie_jar.signed[ "_qnaire_session" ]

warden_data 在我的新应用中看起来不同,例如:[[542], "$2a$10$e5aYxr/PIp6OOj8jzE7mke"],其中第一项是用户 ID。

这是我当前的 sn-p:https://gist.github.com/wojt-eu/5109643

以前的版本:

这是我想出的:

config.log_tags = [
  :remote_ip,
  ->(req){
    session_data = req.cookie_jar.signed[ "_qnaire_session" ]
    warden_data = session_data["warden.user.provider_user.key"]
    if warden_data
      '#' + warden_data[1][0].to_s
    else
      "guest"
    end
  }
]

_qnaire_session 可以替换为 instance.config.session_options[:key] 或通过单例:Rails.application.config.session_options[:key]

我有 ProviderUser 模型,因此是 warden.user.provider_user.key。我想对于用户模型,这将是warden.user.user.key

虽然比较杂乱,但不会影响正常的身份验证过程、中间件堆栈顺序等。如果在某些更新期间中断,只会影响标记日志,我在查看开发日志时应该很快注意到这一点。

【讨论】:

  • “qnaire”是“questionnaire”的缩写吗?
  • @AndrewGrimm 是的 (-:
【解决方案4】:

这是我在 Rails 5.1 中使用的 cookie 解密器,用于使用存储在 cookie 中的user_id 标记日志。它基本上让我可以从原始 cookie 访问相当于 session[:user_id] 的控制器

环境/production.rb:

config.log_tags = [
  :request_id,
  :remote_ip,
  lambda do |req|
    session_data = CookieDecrypter.new(req).session_data
    "user_id:#{session_data['user_id']}"
  end
]

app/models/cookie_decrypter.rb:

class CookieDecrypter
  attr_reader :request

  def initialize(request)
    @request = request
  end

  def session_data
    cookie = request.cookies[session_key]
    return {} unless cookie.present?
    cookie = CGI::unescape(cookie)
    key_generator = ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000)
    secret = key_generator.generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len]
    sign_secret = key_generator.generate_key(signed_salt)
    encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON)
    encryptor.decrypt_and_verify(cookie) || {}
  end

  private

  def session_key
    Rails.application.config.session_options[:key]
  end

  def secret_key_base
    Rails.application.secrets[:secret_key_base]
  end

  def salt
    Rails.application.config.action_dispatch.encrypted_cookie_salt
  end

  def signed_salt
    Rails.application.config.action_dispatch.encrypted_signed_cookie_salt
  end
end

【讨论】:

    【解决方案5】:

    这是我刚刚添加到config/initializers/logging.rb 的内容:

    Rails.configuration.log_tags = [
      :uuid,   # request UUID
      lambda { |req|
        # Credentials are (currently) in the format of:
        #
        #   <session_hash>::<user_id>
        #
        # So we have to split by '::' to obtain the user_id for logging.
        #
        # This will just output "User: nil" if there is no current session.
        "User: #{req.cookies['user_credentials'].to_s.split('::')[1]}"
      }
    ]
    

    这是为 Authlogic 准备的。您需要做的事情可能会有所不同,因此您应该真正深入了解您的数据已经向您公开了什么。

    第 1 步:

    查看req 对象有什么可用的。将此添加到config/initializers/logging.rb

    Rails.configuration.log_tags = [
      lambda { |req|
        req.inspect
      }
    ]
    

    然后点击一个页面,看看什么被转储了。

    第 2 步:查看您的 cookie 罐是否有足够的信息,使用相同的技术:

    Rails.configuration.log_tags = [
      lambda { |req|
        req.cookies.inspect
      }
    ]
    

    (点击请求)

    顺便说一句:不要担心将用户名/电子邮件放入日志 - 用户 ID 已经足够了,您可以在数据库中查找它以获得您需要的任何额外元数据。

    【讨论】:

    • 感谢您的回复。遗憾的是,Devise 不使用 cookie 来存储用户信息。
    【解决方案6】:

    不幸的是,日志标签只在请求委托的一开始就被评估一次(在Rails::Rack::Logger中间件中)。在这个阶段没有控制器,所以任何 current_user 帮助器都不可用。还没有设置守望者甚至会话,但至少有一个 cookiejar,所以如果您将 session_id 存储在那里,您可以直接恢复会话或记录 session_id。

    config.log_tags = [ lambda { |req| req.cookie_jar["_session_id"].to_s } ]
    

    我认为最好的选择是直接在登录时将用户名存储在 cookie 中,并通过会话销毁它。

    config.log_tags = [ lambda { |req| req.cookie_jar["user_name"] || 'Noone' } ]
    

    不工作:

    但是如果你使用devise,它使用warden raack中间件,所以env['warden']应该是可用的,你可以试试吗?

    config.log_tags = [ lambda { |req| user = req.env['warden'].user; user && user.name || 'Noone'; } ]
    

    即使没有warden,由于您确实可以通过env['rack.session'] 获得会话,如果您将用户ID 存储在会话中,您可以执行类似的操作

    config.log_tags = [ lambda { |req| user = User.find_by_id(req.env['rack.session']['user_id']); user && user.name || 'Noone'; }
    

    【讨论】:

    • 感谢您的回复。我尝试了您的第一个示例,但它对我不起作用。我收到“内部服务器错误”。
    • 现在怎么样?更改为 req.env
    • 也缺少 final ']',但我仍然收到“Internal Server Error”。
    【解决方案7】:

    现在来点完全不同的东西……

    https://github.com/roidrage/lograge/issues/23#issuecomment-11709861

    我刚刚使用 Rails 4、最新的 Devise 和 Lograge 进行了尝试。

    【讨论】:

    • 最好的方法。谢谢
    【解决方案8】:

    几乎对我有用的(Rails 3.2.22.2)是这里的答案:http://benjit.com/rails/logger/2016/02/26/getting-admin-user-into-rails-logfile/

    这假定cookie_jar 对象响应encrypted。然而,对我来说并非如此。最终对我有用的方法如下:

    config/initializers/logging.rb:

    Rails.configuration.log_tags = [
      lambda { |req|
        session_key = Rails.application.config.session_options[:key]
        session_data = req.cookie_jar.signed[Rails.application.config.session_options[:key] ]
        warden_data = ( session_data["warden.user.user.key"]|| [[]])
        admin_user = warden_data[0][0]
        "u: #{admin_user || 0}"
      }
    ]
    

    【讨论】:

      【解决方案9】:

      正如@viktortron 在his answerlog_tags 初始化时间所说,我们没有合适的session 对象可用,但session_id 在请求中。

      如果您使用的是_database session_store_,就像我的情况一样,您可以临时重建session

      session = ActiveRecord::SessionStore::Session.find_by_session_id(request.cookie_jar["_session_id"])
      

      这就是我的log_tags 的定义方式:

      # config/initializers/rails_log.rb
      def get_user_id(req)
        session = ActiveRecord::SessionStore::Session.find_by_session_id(req.cookie_jar["_session_id"])
        result = session ? session.data["user_id"] : 0
      
        "%07d" % result
      end
      
      log_tags = []
      log_tags << lambda { |req| Time.now.strftime("%F %T.%L") }
      log_tags << lambda { |req| req.uuid.first(8) }
      log_tags << lambda { |req| get_user_id(req) }
      
      Rails.configuration.log_tags = log_tags
      

      结果是这样的:

      [2013-01-22 13:51:36.659] [e52435d1] [0036484] <the log line>
      

      【讨论】:

        【解决方案10】:

        我正在使用Swards solution,它就像一个魅力。但是使用begin..rescue而不是Hash#has_key?是性能杀手

        require 'benchmark/ips'
        
        good_hash = { 'warden.user.admin_user.key' => [[1]]}
        bad_hash = {}
        
        Benchmark.ips do |x|
          x.report('begin+rescue good hash') { good_hash['warden.user.admin_user.key'][0][0] }
          x.report('has_key good hash') do
            good_hash.has_key?('warden.user.admin_user.key') &&
                good_hash['warden.user.admin_user.key'][0][0]
          end
        
          x.report('begin+rescue bad hash') { bad_hash['warden.user.admin_user.key'][0][0] rescue nil }
          x.report('has_key bad hash') do
            if bad_hash.has_key?('warden.user.admin_user.key')
              bad_hash['warden.user.admin_user.key'][0][0]
            end
          end
        
          # Compare the iterations per second of the various reports!
          x.compare!
        end
        

        结果不言自明

        Comparison:
            has_key bad hash:  4870164.1 i/s
        begin+rescue good hash:  3544134.7 i/s - 1.37x slower
           has_key good hash:  2127500.6 i/s - 2.29x slower
        begin+rescue bad hash:     4468.2 i/s - 1089.95x slower
        

        【讨论】:

          【解决方案11】:

          作为一种快速而丑陋的解决方法,也许可以在处理请求后记录第二行。

          此 500 ServerError 由以下人员提供:#{username}

          【讨论】:

            【解决方案12】:

            对于使用 Redis::Store 的任何人,@fjuillen 的答案如下所示:

            redis = Redis::Store.new
            redis.select 3 # only if you use a different database
            result = redis.get req.cookie_jar["_session_id"]
            

            在 Rails 4 上测试。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-05-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多