How can I write to Rails logger within my gem - ruby-on-rails

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.

Related

Rails: How to use initializer code inside gem

I'm using the okcomputer gem for a Rails app (All you need to do to use this gem is to place some code in an initializer).
I'd like to wrap this gem inside a custom gem that can be used without an initializer. I hope to be able to use this gem in several dockerized microservices just by installing it.
I read that it's possible to put initializer code in an init.rb file at the root of the gem. In my case, that doesn't seem to be working (the routes generated by okcomputer are not found), but I'm not sure where the issue lies.
In general, can I expect code in init.rb to behave like code in an initializer?
Your best approach is probably to create a Railties and run your code in an after_initialize hook.
module Gemname
class MyCoolRailtie < ::Rails::Railtie
config.after_initialize do
OKComputer::Registry.register "resque_critical",
OKComputer::ResqueBackedUpCheck.new("critical", 10)
end
end
end
https://api.rubyonrails.org/classes/Rails/Railtie.html

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"

Local gem class is not found in Rails application

I have written a generator which creates the following ruby file and folder:
app/tests/test.rb
in the test.rb file I have a Test class which looks like this:
class Test < MyCustomModule::MyCustomClass::Base
...
end
Now, I want to use its functionality in one of the show.html.erb files creating new instance like this:
Test.new(...).render(...).html_safe
but I am getting the following error:
uninitialized constant MyCustomModule::MyCustomClass::Base
I have use the following answer to link my gem and my rails application. It seems to work as I am able to use the generator, but the gem module and class are not seen in the rails application.
Could anyone tell how to fix this issue?
I have try to follow the tips posted here but still nothing changed:
Adding config.autoload_paths += Dir["#{config.root}/lib/**/"] in application.rb file
I have created my gem structure looking at CarrierWave gem, so the naming should be correct
I try to disable config.threadsafe! but it is already disabled since config.cache_classes and config.eager_load are set to false in development
DEPRECATION WARNING: config.threadsafe! is deprecated. Rails
applications behave by default as thread safe in production as long as
config.cache_classes and config.eager_load are set to true.
Also, looking at adding-asset-to-your-gems rails documentation, it is said that:
A good example of this is the jquery-rails gem which comes with Rails
as the standard JavaScript library gem. This gem contains an engine
class which inherits from Rails::Engine. By doing this, Rails is
informed that the directory for this gem may contain assets and the
app/assets, lib/assets and vendor/assets directories of this engine
are added to the search path of Sprockets.
So, I have done this, and put my model class file in assets folder, but the result is the same.
The following screenshots demonstrate my real case:
The screenshot below displays my gem file structure
Here you can see how I am loading the gem in my Rails application Gemfile:
gem 'thumbnail_hover_effect', '0.0.3', github: 'thumbnail_hover_effec/thumbnail_hover_effec', branch: 'master'
Then I am using the gem generator a ruby file with a cutstom name in app/thumbnails/test.rb folder with the following code:
class Test < ThumbnailHoverEffect::Image::Base
...
end
and trying to use the Test class gives me uninitialized constant ThumbnailHoverEffect::Image::Base error.
Back in the gem files, these are how the thumbnail_hover_effect file looks like
require 'thumbnail_hover_effect/version'
require 'thumbnail_hover_effect/engine'
require 'thumbnail_hover_effect/image'
module ThumbnailHoverEffect
# Your code goes here...
end
and hoe the image file looks like:
module ThumbnailHoverEffect
#
class Image
...
end
end
From what you've posted here there is no ThumbnailHoverEffect::Image::Base defined. Rails autoloading conventions (which you should not be depending on a gem btw, more on that later) would be looking for this file in thumbnail_hover_effect/image/base.rb, but the directory structure you printed does not have that. Of course you could define the class in thumbnail_hover_effect/image.rb and it would work, but the abridged snippet you posted does not show that. So where is ThumbnailHoverEffect::Image::Base defined?
If it's in thumbnail_hover_effect/image/base.rb then that would indicate the file is not being loaded. You can sanity check this by putting a puts 'loading this stupid file' at the top of thumbnail_hover_effect/image/base.rb. That will allow you to bisect the problem by seeing whether there is a problem with your definition of the class, or whether the problem is with loading the proper files. Debugging is all about bisecting the problem.

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

Resources