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
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>
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}
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.
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
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
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