Logging in a gem that is used both inside and outside rails - ruby-on-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.

Related

logger in google-cloud-logging is not working for rails

I'm currently working on the GCP and deployed my rails app on the GCP instance.
I'm currently using https://github.com/brendeschuijmert/google-cloud-ruby/tree/master/google-cloud-logging for the compute engine.
When I use this on the rails application.
Rails.logger.info "Hello World" works well.
But logger.warn "Hola Mundo" is not working.
I want someone shade some light on this problem.
Thanks
If you're trying to call logger from outside of a controller/model or some other file that is a part of Rails' structure - you will have to explicitly create a logger for yourself with:
logger = Logger.new(STDOUT) # Or wherever you want to log to
However if Rails.logger is working, then you likely just need an alias like logger = Rails.logger to allow you to use logger.warn without the Rails namespace prefix. You're probably in a file that doesn't already have a helper that is aliasing that for you.
Some more digging in the API reveals that the logger stems from ActiveSupport::LogSubscriber - several classes like ActionController include child LogSubscribers that inherit from that module and hence have the logger method available to them. You can manually alias it to Rails.logger to have it work for you wherever you are trying to invoke it.
Source: https://api.rubyonrails.org/classes/ActiveSupport/LogSubscriber.html#method-c-logger

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"

How do I get Rails to log my Rack::CommonLogger messages?

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.

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.

Rails: Logging for code in the lib directory?

What is the best/easiest way to configure logging for code kept in the lib directory?
There's two ways to go about it:
Assuming your library is self-contained and has a module, you can add a logger attribute to your module and use that everywhere in your library code.
module MyLibrary
mattr_accessor :logger
end
You then either use an initializer in config/initializers/, or an config.after_initialize block in config/environment.rb to initialize your logger, like so:
require 'mylibrary'
MyLibrary.logger = Rails.logger
This would still allow you to use your self-contained library from scripts outside of Rails. Which is nice, on occasion.
If using your library without Rails really doesn't make sense at all, then you can also just use Rails.logger directly.
In either case, you're dealing with a standard Ruby Logger. Also keep in mind that, in theory, the logger may be nil.
We can use Rails logger directly into the lib, see the following snippet.
require 'logger'
Rails.logger.info "Hay..!!! Im in lib"
Rails.logger.debug "Debugging this object from Lib #{object.inspect}"
Rails.logger.error "This is an error..."

Resources