I am using Tagged Logging with Unicorn and using the following configuration in my environment file.
config.logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
config.log_tags = [:uuid]
So far so good.
When it comes to tags, is there a way to -
Print out specific request headers
Print a custom UUID, i.e something that I can generate. The default UUID that rails spits out is too long.
See some examples from this Gist here https://gist.github.com/2513183
You can add a proc to the log_tags array, which has access to the request object.
You can generate a UUID in that proc, or you can pass something through the request.env from your ApplicationController in a before_filter, like so:
#application_controller.rb
before_filter :set_some_request_env
def set_some_request_env
request.env['some_var'] = "Happy"
end
# application.rb
config.log_tags = [
-> request {
request.env['some_var']
}
]
Update
You can use the #tagged method to add tags to all log messages sent within a given block.
To get a parameter from your request or controller params into the tagged output, you can do the following:
#application_controller.rb
around_filter :add_tags_to_logs
def add_tags_to_logs
Rails.logger.tagged(custom_uuid_for_current_user) do
yield
end
end
Related
Many simmilar Q/A on this topic here and there, but I was unable to find exact solution for my problem. Using Rails 3.0.9 now, and trying to upgrade existing older application(not Rails).
The goal is to send simple email to new clients created by admins.
Have been following this oficial guide (and many others), but with no success.
The issue is, that method(s) defined in this controller, from class 'UserMailer', aren`t recognised from another controller, while class 'UserMailer' itself recognised is(how do I know this, will be explained below):
/app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
default :from => "info#xxxxx.sk"
def kokotina # << this is just a dummy method for testing
caf = "ssss"
end
def regMailUsr(nazov, priezvisko, email, pass)
#nazov = nazov
#priezvisko = priezvisko
#email = email
#pass = pass
#url = "http://loyalty2.xxxx.sk"
mail(to: email, subject: 'Vaša registrácia bola dokončená.')
end
end
I have also created View for this mail controller but that is not important right now.
The fragments from clients controller are here:
/app/controllers/clients_controller.rb
# encoding: UTF-8
class ClientsController < ApplicationController
load_and_authorize_resource
.......
def new
#noveHeslo = genHeslo(10) # << I defined this in application_controller.rb and it works
UserMailer.kokotina # << just a dummy method from UserMailer
#client = Client.new(params[:client])
.......
end
.......
def create
.......
if #client.save
#send email to new client:
UserMailer.regMailUsr(params[:client][:fname], params[:client][:lname], params[:client][:email], params[:client][:password]).deliver
.....
end ......
Now how do I know that my class is loaded? If in client controller, I change 'UserMailer' to 'xUserMailer', I will get 'no class or method in ...' error, but without 'x', I get only:
'undefined method `kokotina' for UserMailer:Class'
I also tried to define my methods in UserMailer:Class like this:
def self.kokotina # << this is just a dummy method for testing
caf = "ssss"
end
#or even like this
def self <<
def kokotina # << this is just a dummy method for testing
caf = "ssss"
end
end
#and then tried to invoke this method(s) like this:
UserMailer.new.kokotina
#or simply
kokotina
Strange is, that when I put contents of file '/app/mailers/user_mailer.rb' at the end of 'application_helper.rb' file, just after the end of 'module ApplicationHelper', I get no errors but of course, it won`t work.
Please keep in mind that I have no problem coding in another languages, but this mystic/kryptic rules of Ruby on Rails are still a complete mistery to me and unfortunatelly, I don`t have time or even motivation to read time extensive quides or even books for RoR beginners. I have been coding much more difficult applications and implementations, but this heavily discriminating system is driving me nuts.
Thank you all!
Problem solved!
The trick was, that in '/app/mailers/user_mailer.rb', I had multibyte characters. In mail subject.
So I added:
# encoding: UTF-8
at the very first line of '/app/mailers/user_mailer.rb'
I found this by total accident: later my rails app could not start, and server was simply throwing HTTP 500 error. So no trace, error defining etc.
I found out that multibyte string in:
mail(to: email, subject: 'Vaša registrácia bola dokončená.')
Was responsible for crash. When I removed that string, I noticed one important side effect: my methods became magicaly available for another controller!!!!
So if someone could give me at least one reason to lowe Rails...
I am implementing background email processing with Resque using the resque_mailer gem (https://github.com/zapnap/resque_mailer). I was able to get it to work for all my emails except the ones sent by Devise.
I went through a bunch of SO questions, and blog posts (for instance http://teeparham.posterous.com/send-devise-emails-with-resque) but could not find a way to get it to work.
What are the precise steps to follow to get resque_mailer to work with Devise?
I went through tee's answer and several resources online, but couldn't find a working solution.
After a few days of reading through resque-mailer and devise code, a solution that worked for me. Thanks to tee for gist which put me in right direction.
Assuming your app/mailers/application_mailer.rb looks similar to
class ApplicationMailer < ActionMailer::Base
include Resque::Mailer # This will add a `self.perform` class method, which we will overwrite in DeviseResqueMailer
end
In config/initializers/devise.rb
Devise.parent_mailer = "ApplicationMailer"
Devise.setup do |config|
config.mailer = 'DeviseResqueMailer'
end
In the resource class which uses devise, overwrite the send_devise_notification method to send resource class and id instead of object to prevent marshalling
# app/models/user.rb
protected
def send_devise_notification(notification, *args)
# Based on https://github.com/zapnap/resque_mailer/blob/64d2be9687e320de4295c1bd1b645f42bd547743/lib/resque_mailer.rb#L81
# Mailer may completely skip Resque::Mailer in certain cases - and will fail as we write custom handle in DeviseResqueMailer assuming mails are handled via resque
# So in those cases, don't retain original devise_mailer so things work properly
if ActionMailer::Base.perform_deliveries && Resque::Mailer.excluded_environments.exclude?(Rails.env.to_sym)
# Originally devise_mailer.send(notification, self, *args).deliver
# Modified to ensure devise mails are safely sent via resque
resource_id, resource_class = self.id, self.class.name
devise_mailer.send(notification, {resource_id: resource_id, resource_class: resource_class}, *args).deliver
else
super
end
end
Finally, in app/mailers/devise_resque_mailer.rb, fetch the record again from the database and continue
class DeviseResqueMailer < Devise::Mailer
def self.perform(action, *args)
# Hack to prevent RuntimeError - Could not find a valid mapping for admin.attributes
record_hash = args.shift
record = record_hash["resource_class"].constantize.find(record_hash["resource_id"])
args.unshift(record)
super # From resque-mailer
end
end
I feel this approach is a better than using devise-async as all the mails go through same code path. Its easier to control and overwrite if needed.
I'd take a look at devise-async. Looks like it fits your use case. Devise Async
Is there a way to add the session id to each log in Rails.
Now, I've added this in my environment.rb:
class Logger
def format_message(severity, timestamp, progname, msg)
"#{timestamp.to_formatted_s(:db)} #{severity} #{msg}\n"
end
end
First of all, is this best-practice? And how do I add the session id?
Unfortunately, giving :session_id to log_tags don't work yet (as of May 23 2012), since there is no session_id in Rack middleware.
Since log_tags array accept a Proc object, and the proc object is invoked with a request object, I could configure the log_tags following way:
config.log_tags = [ :uuid, :remote_ip,
lambda {|req| "#{req.cookie_jar["_session_id"]}" } ]
It's working with Rails 3.2.3, with ActiveRecord's session store is in use.
when you are working with rails 3.2 you can use log tags:
Update Rails::Rack::Logger middleware to apply any tags set in
config.log_tags to the newly ActiveSupport::TaggedLogging
Rails.logger. This makes it easy to tag log lines with debug
information like subdomain and request id -- both very helpful in
debugging multi-user production applications DHH
with some luck, adding this to your environment might work:
config.log_tags = [:uuid, :remote_ip, :session_id]
UPDATE
for the unlucky, the solution is from the answer of #shigeya.
unfortunately rails does NOT provide a way to access your sessionid in a sane manner.
you always have to rely on what rack is doing in it's internals... accessing the cookie_jar with your cookie key is the "recommended" way of doing so.
this is what i use for my application:
config.log_tags = [
:host,
:remote_ip,
lambda { |request| "#{request.uuid}"[0..15] },
lambda { |request| "#{request.cookie_jar["_session_id"]}"[0..15] },
]
output then looks like this:
[hamburg.onruby.dev] [127.0.0.1] [27666d0897c88b32] [BAh7B0kiD3Nlc3Np] Completed 200 OK in 298ms (Views: 286.2ms | ActiveRecord: 9.2ms)
For Rails 3.2 with ActiveSupport::TaggedLogging, if you're using :cookie_store:
config.log_tags = [ :uuid, :remote_ip,
lambda { |r| "#{r.cookie_jar.signed["_session_id"]["session_id"]}" } ]
Note: Change the "_session_id" with your :key value at config/initializers/session_store.rb
Unfortunately this isn't easy with Rails log tags. Moreover, it clutters your logs to the point where they are unreadable.
I'd recommend something like timber, it captures session ids (and more) by simply augmenting your logs with metadata. It's automatic and you don't lose readability.
My thinking is to capture the session_id and store it in thread local storage, e.g.
Thread.current[:session_id] = session[:session_id]
But some rails logging occurs before my filter is called.
I thought I might capture the session_id by writing a middleware plug-in. But again, I don't seem to get it early enough for all logging.
What is the earliest that I can capture session_id?
Thanks!
Since Rails 3.2 support tagged logging using log_tags configuration array, log_tags array accept a Proc object, and the proc object is invoked with a request object, I could configure the log_tags following way:
config.log_tags = [ :uuid, :remote_ip,
lambda {|req| "#{req.cookie_jar["_session_id"]}" } ]
It's working with Rails 3.2.3, with ActiveRecord's session store is in use.
Okay, I finally figured this out.
I moved ActionDispatch::Cookies and ActionDispatch::Session::CookieStore way earlier in the rack stack. This appears to be safe, and is necessary because otherwise some logging happens before the session is initialized.
I added my own rack middleware component that sets the session_id in thread local storage.
I override the rails logger and prepend the session_id to each log message.
This is very helpful in being able to separate out and analyze all logs for particular user session.
I'd be interested to know how anyone else accomplishes this.
Based on #Felix's answer. i've done these in rails 4:
# config/application.rb
config.middleware.delete "ActionDispatch::Cookies"
config.middleware.delete "ActionDispatch::Session::CookieStore"
config.middleware.insert_before Rails::Rack::Logger, ActionDispatch::Cookies
config.middleware.insert_before Rails::Rack::Logger, ActionDispatch::Session::CookieStore
# config/environment/development.rb and production.rb
config.log_tags = [
lambda {|req| "#{req.subdomain}/#{req.session["user_id"]}" },
:uuid
]
config.log_formatter = Logger::Formatter.new
This produces logs like this:
I, [2015-11-05T15:45:42.617759 #22056] INFO -- : [verimor/2] [77e593dc-c852-4102-a999-5c90ea0c9d66] Started GET "/home/dashboard" for 192.168.1.37 at 2015-11-05 15:45:42 +0200
[verimor/2] is subdomain/user_id (this is a multitenant app).
[77e593dc-c852-4102-a999-5c90ea0c9d66] is a unique id for this request. Useful for keeping track of the lifecycle of requests.
HTH.
For Rails 3.2 with ActiveSupport::TaggedLogging, if you're using :cookie_store:
config.log_tags = [ :uuid, :remote_ip,
lambda { |r| "#{r.cookie_jar.signed["_session_id"]["session_id"]}" } ]
Note: Change the "_session_id" with your :key value at config/initializers/session_store.rb
Related: https://stackoverflow.com/a/22487360/117382
Using Sorcery 0.7.4 with Rails 3.1.1 for authentication.
Everything was going well until I tried to setup password resetting.
Activation works perfectly and emails are sent, but for some reason I get this error when trying to send the reset password email.
undefined method `reset_password_email' for nil:NilClass
I copied the tutorial exactly, and when I did a quick test in the console it shot off the email as expected. In console:
user = User.find(1)
user.deliver_reset_password_instructions!
In the actual controller, it finds the user by the email submitted from the form and in the log I can see it is retrieving the right user and setting the token, but errors out as above and rolls back.
I checked the gem's code for deliver_reset_password_instructions! and there seems to be no reason for it to fail.
PasswordResetsController:
#user = User.find_by_email(params[:email])
#user.deliver_reset_password_instructions! if #user
The following is copied from the gem code:
Instance Method in Gem:
def deliver_reset_password_instructions!
config = sorcery_config
# hammering protection
return false if config.reset_password_time_between_emails && self.send(config.reset_password_email_sent_at_attribute_name) && self.send(config.reset_password_email_sent_at_attribute_name) > config.reset_password_time_between_emails.ago.utc
self.send(:"#{config.reset_password_token_attribute_name}=", TemporaryToken.generate_random_token)
self.send(:"#{config.reset_password_token_expires_at_attribute_name}=", Time.now.in_time_zone + config.reset_password_expiration_period) if config.reset_password_expiration_period
self.send(:"#{config.reset_password_email_sent_at_attribute_name}=", Time.now.in_time_zone)
self.class.transaction do
self.save!(:validate => false)
generic_send_email(:reset_password_email_method_name, :reset_password_mailer)
end
end
The method called above for mailing:
def generic_send_email(method, mailer)
config = sorcery_config
mail = config.send(mailer).send(config.send(method),self)
if defined?(ActionMailer) and config.send(mailer).superclass == ActionMailer::Base
mail.deliver
end
end
Again all the required mailer bits and pieces are there and work from the console.
Uncomment this lines in the sorcery initializer
user.reset_password_mailer = UserMailer
user.reset_password_email_method_name = :reset_password_email
Check your app/mailers/user_mailer.rb file.
If you were following the tutorial you probably did something like copy and paste the method definition from the wiki (which takes one parameter) into the generated method definition (which doesn't take any parameter), hence the 1 for 0 ArgumentError.
In other words, you likely have something that looks like this:
def reset_password_email
def reset_password_email(user)
This is bad, but an easy fix :-)