How to log user_name in Rails?

后端 未结 12 1645
遇见更好的自我
遇见更好的自我 2020-12-15 06:52

I use Devise in Rails 3. I want to see name of current_user in production.log.

I would like to configure rails like this:

config.log_tags = [:user_na         


        
相关标签:
12条回答
  • 2020-12-15 07:45

    As @viktortron has said in his answer at log_tags initialization time we have not a proper session objet available, but the session_id is in the request.

    If you are using a _database session_store_, as it is my case, you can rebuild the session ad-hoc:

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

    This is how my log_tags are defined:

    # 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
    

    The result is something like:

    [2013-01-22 13:51:36.659] [e52435d1] [0036484] <the log line>
    
    0 讨论(0)
  • 2020-12-15 07:45

    As a quick and ugly workaround, maybe one could log a second line after the request has been processed.

    This 500 ServerError has been presented by: #{username}

    0 讨论(0)
  • 2020-12-15 07:46

    Here's what I just added to 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]}"
      }
    ]
    

    This is for Authlogic. What you need to do might vary, so you should really dig in and see what your data exposes to you already.

    Step 1:

    See what the req object has available. Add this to config/initializers/logging.rb:

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

    Then hit a page, and see what gets dumped.

    Step 2: See if your cookie jar has enough information, using the same technique:

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

    (hit a request)

    An aside: Don't worry about putting in usernames/emails into the logs - user ID is good enough, and you can look it up in the database to get any extra metadata you need.

    0 讨论(0)
  • 2020-12-15 07:46

    I'm using Swards solution and it works like a charm. However using begin..rescue instead of Hash#has_key? is a performance killer

    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
    

    Results speak for themselves

    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
    
    0 讨论(0)
  • 2020-12-15 07:48

    I used this solution from Wojtek Kruszewski: https://gist.github.com/WojtekKruszewski

    I tweaked a little bit for my project to only include id, but basically the same.

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

    And create this initializer

    # 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
    
    0 讨论(0)
  • 2020-12-15 07:48

    Here is the cookie decrypter I use in Rails 5.1 to tag the logs with the user_id, which is stored in the cookie. It basically lets me access the controller equivalent of session[:user_id] from a raw cookie

    environments/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
    
    0 讨论(0)
提交回复
热议问题