How do I get Rails to log my Rack::CommonLogger messages? - ruby-on-rails

I am using the ruby gem rest-client with rest-client-components.
Rest-client-components enables request logging with Rack::CommonLogger.
The instructions for enabling it make use of STDOUT:
require 'restclient/components'
RestClient.enable Rack::CommonLogger, STDOUT
This works fine in development, but when I'm in production with Apache/Passenger (mod_rails), I don't see any messages from rest-client in production.log. Is there a way to integrate Rack::CommonLogger with the Rails log? Or at least to write it to a file? The former is more useful because it's easy to see the context, but the latter is better than nothing.
Thanks.

Here's the solution I came up with.
Thanks to #crohr for pointing me in the right direction.
First, create a new Logger class. Rails defaults to ActiveSupport::BufferedLogger, so we'll extend that.
# lib/rest_client_logger.rb
class RestClientLogger < ActiveSupport::BufferedLogger
def write(msg)
add(INFO, msg)
end
end
Then tell Rails to use your new logger.
# application.rb
log_file = File.open("log/#{Rails.env}.log", 'a')
log_file.sync = true # turn on auto-flushing at the file level so we get complete messages
config.logger = RestClientLogger.new(log_file)
config.logger.auto_flushing = !Rails.env.production? # turn off auto-flushing at the logger level in production for better performance
Finally, tell rest-client to use your new logger.
# config/initializers/rest_client.rb
RestClient.enable Rack::CommonLogger, Rails.logger
Limitations:
If you're using Rack::Cache with rest-client-components, this doesn't capture the cache messages.

Related

How to write log inside my rubygem that will be used in a rails app?

I'm developing a rubygem that will be used in a rails app. In this rubygem i need to log some information (warnings, errors...).
I saw some gems to do this, like logging, but apparently i need no configure the log output (stdout, some file).
My rails app log the messages in a file. So, my question: Is there a way to my gem use the same log configuration that my rails app uses? or my gem will send the log according to his own configuration?
You may use Rails.logger directly, which is valid if your gem will always only be used within a Rails application. You may alternatively define a logger for your gem namespace and default to Rails.logger if defined, or Logger.new(STDOUT) if it's not, along with a writer, so it's overridable:
module MyGem
def self.logger
##logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
end
def self.logger=(logger)
##logger = logger
end
end
Whatever the case, you will use it like this:
MyGem.logger.debug "it works"

custom logger in rails4?

It looks like Rails4's logger, unlike Rails3's, finally supports a custom formatter, like the ruby stdlib logger again.
Rails.logger.formatter # => #<ActiveSupport::Logger::SimpleFormatter:0x007ff81757d890 #datetime_format=nil>
Rails.logger.formatter = SomeFormatterClass
However, when I try to give it a formatter class that would be sufficient for stdlib Logger formatter:
[2014-03-12 16:23:27] ERROR NoMethodError: undefined method `tagged' for #<FormattedRailsLoggerFormatter:0x007fd816545ad8>
/Users/jrochkind/.gem/ruby/1.9.3/gems/activesupport-4.0.3/lib/active_support/tagged_logging.rb:67:in `tagged'
/Users/jrochkind/.gem/ruby/1.9.3/gems/railties-4.0.3/lib/rails/rack/logger.rb:20:in `call'
Does anyone know, is a custom formatter actually a supported feature of Rails4? And is how you are meant to do it documented anywhere?
Answering my own question, I've figured it out.
Rails4 provides a config variable, config.log_formatter. You would probably set it in your config/application.rb, along with other application config.
You should set it to an object implementing the stdlib Logger Formatter interface: Basically, you have to provide a method call(severity, time, progname, msg) that returns the formatted log line.
Note you set it to an object, NOT the class name, eg:
config.log_formatter = MyFormatter.new
You should not try to set Rails.logger.formatter directly -- Rails expects you to set it via config, and does some tricky stuff to make your formatter and logger work properly with Rails when you use the config. You can see that, as well as see that indeed config.log_formatter is used, in Rails source code here. (Thanks to github and it's awesome code search and display ui, is how I tracked this down and figured out the existence of config.log_formatter)
In Rails4, you should not need to monkey-patch any parts of Rails just to use a custom log formatter, just use config.log_formatter instead. (In Rails3 you did need to monkey-patch one or another to get custom log formatting).
In case it helps anyone else, in Rails 4.2.6 and Ruby 2.3.0, this is what worked for me:
# config/application.rb
module MyRailsApp
class Application < Rails::Application
require 'my_log_formatter'
...
# Custom log formatter that uses JSON
config.log_formatter = MyLogFormatter.new
end
end
To make this happen:
# lib/my_log_formatter.rb
class MyLogFormatter < ActiveSupport::Logger::SimpleFormatter
def call(severity, timestamp, progname, message)
if message.present? && message.exclude?('Started GET "/assets')
{
app: Rails.application.class.parent_name,
process_id: Process.pid,
level: severity,
time: timestamp,
progname: progname,
message: message
}.to_json + "\r\n"
else
''
end
end
end
Some notes:
The if statement prevents log entries with no message, and log messages that log the serving of asset files.
The + "\r\n" adds a CR+LF to the end of the line, so it remains somewhat human-readable in the rails server console.
I added app because I am sharing a single log file among many apps. You could delete this in a system that uses just one Rails app.
I find process_id comes in handy when you are reviewing logs on a busy server.

How to properly use logger and how to use the same logger in both Ruby and Rails

Sorry if this a very basic question, but after I checked a lot entries here (and tried to implement solutions) I still do not have an answer...
The goal is to have a ruby class which could be used withot editions (or with as little editions as possible) in both ruby scripts and as ruby on rails model. I want to have the following
logger.info 'some tracing'
However when I put such statement in ruby class I'm getting error messages - unknow method logger in both ruby and ruby on rails. Well. when I modify it such way
Rails.logger.info 'some tracing'
It starts to work in rils, but of course does not work in ruby without rails. What I'm doing wrong?
I read this http://guides.rubyonrails.org/debugging_rails_applications.html#the-logger but I still confused, since examples from the section 'send messages' do not work.
So_ sorry again, but I see no other possibility as to connect to "the hive mind" ans ask for help ;-)
Rails uses the ruby logger by default.
Take a look at the API doc here : Logger (Ruby 2.0)
Here is an example:
require 'logger'
logger = Logger.new(STDOUT)
logger.level = Logger::WARN
logger.debug("Created logger")
logger.info("Program started")
logger.warn("Nothing to do!")
EDIT
Usage:
require 'logger'
class MyClass
def initialize
#logger = logger = Logger.new(STDOUT)
#logger.level = Logger::WARN
end
def say_something
#logger.info("You can't see me I'm not important enough")
#logger.warn("I'm visible")
end
end

Logging in a gem that is used both inside and outside rails

What's a proper approach to this: I've got a gem that can be used both with or without rails - if it is used within rails, I'd like to re-use rails-services e.g. the configured logger. If it is outside of rails (e.g. in a sinatra-app), I want to fall back on the Logger from stdlib: What is a safe way for a gem to determine, if we're in a rails-app or not?!
I've tried approaches like:
#logger = defined?("Rails") ? eval("Rails.logger") : Logger.new(STDOUT)
But this only gives me an "uninitialized Constant"...
So close! Drop the quotes / eval:
#logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
I'd also encourage that you provide a config hook where the user can specify a Logger-compatible log sink. Defaults are nice but sometimes you need to configure things.

How can I write to Rails logger within my gem

I have a gem which I wrote and I use it inside my rails application.
I want to write to rails logger from my gem but obviously the standard rails logger doesn't exist there.
What is the right way to achieve what I want to do?
As Frederick Cheung says, you should use a namespaced logger for your gem: MyGem.logger.
Then set it to the Rails logger in a Railtie so that your gem works nicely both inside and outside of Rails.
module MyGem
class Railties < ::Rails::Railtie
initializer 'Rails logger' do
MyGem.logger = Rails.logger
end
end
end
While you should be able to use Rails.logger you might want to consider making the logger that your gem uses configurable, i.e. allow users to set MyGem.logger to whatever logger they want.
You can default it to something that just writes to stdout, in a rails app you can set MyGem.logger = Rails.logger in an initialiser. People who are using your gem outside of rails can do so too.

Resources