Not able to find global log file in Ruby - ruby

I have a script called main.rb in which I call modules. In the script, I am creating a log file (something like \abc\xyz\script\tool.log) and storing it in a global variable $LOG: $LOG = Logger.new('tool.log', 'daily')
From main.rb I call another module's method (say HostM.changeDir1) which changes directory to \abc\xyz. Now when I am trying to write into $LOG it errors out by printing \abc\xyz\tool.log does not exist. I was wondering how can I make it look for the log in proper location i.e., \abc\xyz\script\tool.log. Need guidance in fixing this issue.

Add the full path to the log file is one option
$LOG = Logger.new('\abc\xyz\script\tool.log', 'daily')
However, using a global variable is usually poor practice, and I expect using an absolute path is also going to cause problems if you move the app to another location. Therefore, I'd suggest you wrap the Logger in a new class that defines a standard behaviour and a path to the log file that is relative to the file where the new class is defined.
require 'logger'
class AppLogger < Logger
def initialize(period = 'daily')
path_to_log = File.expand_path('relative/path/to/tool.log', File.dirname(__FILE__))
super path_to_log, period
end
end
Then when you need a logger in your app, you can do this:
logger = AppLogger.new
logger.debug 'Whoops'
If you want to ensure you only work with one logger instance, I'd load the logger instance in your Main class, and share it from there:
class Main
require_relative 'app_logger'
def self.logger
#logger ||= AppLogger.new
end
....
end
Then in the rest of your app, you'll be able to call the logger like this:
Main.logger.debug 'Whoops!'

Related

Calling a method in a separate class from the whenever schedule.rb

I am trying to call a method from a whenever schedule like this:
schedule.rb:
require '../app/bots/task_bot'
every 1.minute do
runner 'TaskBot.test_robot'
end
And I have a class called TaskBot placed inside the directory app/bots/task_bot.rb. It looks like this:
task_bot.rb:
class TaskBot
def test_robot()
logger = Logger.new('log/robot.log')
logger.debug("TEST")
end
end
Then I ran the command whenever --update-crontab and verified that the crontab is updated by using crontab -l. Crontab was updated but I don't see the Logger being executed every minute. I'm new to this, am I missing something here?
test_robot is an instance method but you are trying to execute it as a class method; in order for it to work you have two options:
Create the TaskBot object first and then call test_robot:
every 1.minutes do
runner 'TaskBot.new.test_robot'
end
Create a class method, and do not modify schedule code (i prefer this way):
class TaskBot
def self.test_robot
logger = Logger.new('log/robot.log')
logger.debug("TEST")
end
end

How to provide config file for Ruby command line utility written in Ruby?

I have a command line utility written in Ruby using GLI framework. I would like to have configuration for my command line utility in my home directory, using Ruby itself as DSL to handle it (similar to Gemfile or Rakefile).
I have in class ConfigData in folder lib/myapp. The class looks like following way:
class ConfigData
##data = {}
class ConfigItem
def initialize
#data = {}
end
def missing_method(name, *args)
#data[name] = args[0]
end
end
def self.add(section)
item = ConfigItem.new()
yield item
##data[section]=item
end
end
Now, what I would like to have, is the config file, preferrably with name Myappfile, in current working folder, with the following content
add('section1') do |i|
i.param1 'Some data'
i.param2 'More data'
end
When this code was included between class and end of ConfigData, it worked fine. But now I would like to have it placed in the working folder, where I start the application.
I tried require('./Myappfile') between class and end of ConfigData, but it doesn't work for me. I tried to read the source codes of rake, but it is not very much clear to me.
Any hint how this can be solved?
To evaluate code within the context of an instance, which is what you want to do, you need the instance_eval() method. Never, ever, use normal eval. Ever. Anyway, here's how you'd load your fingi file and get the data:
config = ConfigData.new
config.instance_eval(File.read("Myconfig"))
#Access configuration data here from the config object
That simple. After you've loaded the object in that way, you can access values of the object.
WARNING: This is not very secure. This is actually a gaping security hole. Here's the secure version:
f = Fiber.new {str = File.read("Myconfig"); $SAFE = 4; config = ConfigData.new; config.instance_eval(str); Fiber.yield config}
confdata = f.resume
#Access configuration data here from confdata.
This executes the external code in a (sort of) sandbox, so that it can't do anything dastardly.
Also, why don't you just use a YAML config? Unless configuration needs to run code like pwd or access RUBY_VERSION, YAML is much simpler and more secure, in addition to being more failproof.

implementing logger in ruby script

This is a ruby script which i am using to log my status.
require 'logger'
log = Logger.new( 'log.txt', 'daily' )
log.debug "Once the log becomes at least one"
log.debug "day old, it will be renamed and a"
log.debug "new log.txt file will be created."
Now i want to make a new ruby file in which i want to include logger it can be like this
module Logging
def logger
Logging.logger
end
def self.logger
#logger ||= Logger.new(STDOUT)
end
end
But i am not able to understand this so can anyone explain it.
my requirement is like there are lots of ruby script file i want to put a logger in module and include that in every file than write log in log file it can be warning it can be a info or anything else.
A better alternative would be:
module Logging
def logger
#logger ||= Logger.new(STDOUT)
end
end
And use it like this
class MyLoggingClient
include Logging
#Now you have access to the method `logger` and the instance variable `#logger`.
end
What I can gather from your question is the code works... you're just confused on what it does... if that's correct then to explain, when you inculde Logging in any of your classes the from anywhere in the classes methods call logger.log("This is a log message") it will log that message to the console that started the scripts.
Does that answer your question?

Ruby: How to find path relative to where class was instantiated?

I am creating a gem that is a Rack application, so I assume my application is going to be instantiated in a config.ru file. I expect certain paths to be relative to this config.ru file. So how can I get and set the path when the app is initialized?
For example:
Hidden away in my gem:
class MyApp
def initialize
#base_path = get_the_base_path_here
end
def call(env)
html = render_view(#base_path + '/views/index.erb')
end
end
User of the gem's config.ru:
require 'my_app'
run MyApp.new
...and their views directory:
/views
index.erb
Update:
One way to achieve this is to pass in the base path as an argument, but I would like to find a way to achieve this without passing it as an argument.
require 'my_app'
run MyApp.new(File.dirname(__FILE__))
Absolute Path of Current File
In general, you can simply use File.expand_path(__FILE__) to find the absolute path of the current file, which you can then store a variable or global if you like. For example:
$file_path = File.expand_path(__FILE__)
Absolute Path of Current Program
File.expand_path($0) is similar, but returns the program that was called. The distinction is sometimes subtle, but can be useful from time to time.
Creating an Absolute Path to a File in the Same Base Directory
If you want to use the directory name of the location of the current file to address another file, you can use File#join. For example:
File.join File.dirname(File.expand_path(__FILE__)), '.X11-unix'
=> "/tmp/.X11-unix"
Probably not the best way but you can find config.ru with:
$:.find{|path| File.exists? "#{path}/config.ru"}

Can I disable the log header for ruby logger?

I'm currently running into kind of a problem.
As you might know, the ruby logger adds a logging header at the top of every newly created logfile.
"# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
I am logging CSV files to import them in a warehouse later, usually I just skip the first line with the header. It's seems like there is a bug in the logger, because sometimes the logging header appears more than once, right in the middle of a log file.
So I decided to simply leave that header out. To my surprise I didn't find any argument one could pass at the creation of a logger. I thought of something like this:
Logger.new "info.log", :skip_header => true
But it's just not there. I searched in the ruby core sources and surprisingly there really is nothing that could prevent the logger from adding the log header:
def create_logfile(filename)
logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT))
logdev.sync = true
add_log_header(logdev)
logdev
end
def add_log_header(file)
file.write(
"# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
)
end
Does anyone have an idea what I could do, to prevent the log header? I'm using Ruby 1.8.7 302 with Rails 2.3.5 here. Simply ignoring the comments on the warehouse side is not possible because I have no control over the code there, and it seems to be to risky to simply ignore it, if something goes wrong with a a logging line.
Does someone know a logger that allows this? Do you think it would be a good idea to use and write plain to a file?
Thanks in advance,
Tommy
Ideally the method add_log_header on the Logger instance should be overwritten, but since add_log_header is called on initialize, you're too late by the time you get your hands on it.
Well, you could just overwrite the add_log_header method on the Class.
class Logger::LogDevice
def add_log_header(file)
end
end
log1 = Logger.new('info1.log')
But if your app needs more instances of Logger after this, they will behave the same: no header. To prevent this:
# dismantle the header and save it under another name
class Logger::LogDevice
alias orig_add_log_header add_log_header
def add_log_header(file)
end
end
# Quick,create an instance
log1 = Logger.new('test_log1file.log')
# restore the old method:
class Logger::LogDevice
alias add_log_header orig_add_log_header
end
Here's a solution that involves subclassing Logger. We have to be sneaky with initialize and super to keep it from creating a standard Logger::LogDevice too early.
class HeadlessLogger < Logger
def initialize(logdev, shift_age = 0, shift_size = 1048576)
super(nil) # this prevents it from initializing a LogDevice
if logdev
#logdev = HeadlessLogger::LogDevice.new(logdev, shift_age: shift_age, shift_size: shift_size)
end
end
class LogDevice < ::Logger::LogDevice
def add_log_header(file) ; end
end
end
As an alternative to patching the logger class, simply do not let it create the log file by touching it upfront:
FileUtils.touch logfile_path
Logger.new logfile_path
In plain Ruby you will need to require 'fileutils' from stdlib obviously.
Edit: This will not work if you use the built-in logrotation though, or the file is deleted, as there is no on-rotate hook and it will then write the header yet again.

Resources