How to Have Ruby Logger Log Output to Stdout as Well as File

How can I have ruby logger log output to stdout as well as file?

You can write a pseudo IO class that will write to multiple IO objects. Something like:

class MultiIO
def initialize(*targets)
@targets = targets
end

def write(*args)
@targets.each {|t| t.write(*args)}
end

def close
@targets.each(&:close)
end
end

Then set that as your log file:

log_file = File.open("log/debug.log", "a")
Logger.new MultiIO.new(STDOUT, log_file)

Every time Logger calls puts on your MultiIO object, it will write to both STDOUT and your log file.

Edit: I went ahead and figured out the rest of the interface. A log device must respond to write and close (not puts). As long as MultiIO responds to those and proxies them to the real IO objects, this should work.

How to log script output to STDOUT and a file

Another option in Ruby (just the inner part) in case tee is not available (windows):

  cmd = Mixlib::ShellOut.new("/usr/local/bin/someScript.py", :timeout => 3600)
cmd.live_stream = STDOUT
cmd.run_command
# new part
log=::Tempfile.new(["xml_diff_results",".txt"])
errlog=::File.open(log.path.gsub(".txt",".err")
log.write(cmd.stdout)
errlog.write(cmd.stderr)
log.close
errlog.close
Chef::Log.info("Log results are in #{log.path}")
# end of new part
cmd.error!

Change the Chef::Log level to warn if you run chef-client without -l info and really want the path to be printed in chef log.

Main advantage is that it's platform independent, drawback is that the log files will only be written once the command has ended its execution.

Log to different files based on logging level using Ruby logger

Define your own custom logger class:

require 'logger'

class DistinctFileLogger
LOG_LEVEL = [:debug , :info , :warn , :error , :fatal , :unknown]

def initialize(path)
@loggers = {}

LOG_LEVEL.each do |level|
@loggers[level] = Logger.new("#{path}/#{level}.log")
end
end

LOG_LEVEL.each do |level|
define_method(level) do |message|
@loggers[level].send(level, message)
end
end
end

Then configure your app to use this custom logger instead of the original. In Rails – for example - this– can be done in your environment config:

Rails.logger = DistinctFileLogger.new(Rails.root.join('log'))

Ruby Logger to add context information on each line

The formatter proc should return a string, not just delegate method calls to another formatter.

Right now, your proc returns the content of msg.each_line :

msg_line1
msg_line2

You could check that the formatter does the job correctly by adding p before original_formatter.call(.... It's just that you don't use the results.

Here's a modified version of your code :

require 'logger'

$logger = Logger.new(STDOUT)
original_formatter = Logger::Formatter.new
$logger.formatter = proc { |severity, datetime, progname, msg|
msg.split("\n").map do |line|
original_formatter.call(severity, datetime, progname, line)
end.join
}

$logger.info('progname') { "msg_line1\nmsg_line2" }
# I, [2017-03-16T15:08:40.688193 #5372] INFO -- progname: msg_line1
# I, [2017-03-16T15:08:40.688193 #5372] INFO -- progname: msg_line2

Ruby - Set different log levels for different targets

Since the log level is a property of the logger, not of the IO, it sounds to me like what you really need to do is define a MultiLogger rather than a MultiIO. Something along the lines of:

class MultiLogger
attr_reader :default_level, :default_progname, :default_formatter

def initialize(**args)
@default_level = args[:default_level]
@default_progname = args[:default_progname]
@default_formatter = args[:default_formatter]
@loggers = []

Array(args[:loggers]).each { |logger| add_logger(logger) }
end

def add_logger(logger)
logger.level = default_level if default_level
logger.progname = default_progname if default_progname
logger.formatter = default_formatter if default_formatter
@loggers << logger
end

def close
@loggers.map(&:close)
end

Logger::Severity.constants.each do |level|
define_method(level.downcase) do |*args|
@loggers.each { |logger| logger.send(__method__, args) }
end

# These methods are a bit weird in the context of a "multi-logger" with varying levels,
# since they are now returning an `Array`; never a falsey value.
# You may want to define them differently, e.g. `@loggers.all? {...}`, or use a non-predicate method name here.
define_method("#{level.downcase}?".to_sym) do
@loggers.map(&__method__)
end
end
end

# Usage:

log_file = File.open(logname, "a")
log_file.sync = true
file_logger = Logger.new(log_file)

console_logger = Logger.new(STDOUT)
console_logger.level = Logger::INFO

multi_logger = MultiLogger.new(
default_progname: programname,
default_formatter: proc do |severity, datetime, progname, msg|
"#{datetime.strftime('%Y-%m-%d %I:%M:%S %p %:::z %Z')} - #{severity} - [#{progname}] | #{msg}\n"
end,
loggers: [file_logger, console_logger]
)

Using Ruby's Logger to have errors go to standard error, but other messages to file/stdout?

Anyone done this, and is there maybe something already that does this?

Ruby is cool, and flexible, and powerful, and stuff. Just with these monkey-patching things and abilities to open a class and add methods, it's too easy to forget about plain old inheritance...

class CopyLogger < Logger
def error message
# Print to standard error...
$stderr.puts message
# ...*and* to wherever you specified as well
super message
end
end

logger = CopyLogger.new(some_file)
# ...

You may put additional parameters into the constructor of your new class, such as the desired severity to tee messages of, etc.

Redirect the puts command output to a log file

I should recommend to use ruby logger, it is better than puts, you can have multiple log levels that you can turn on/off: debug, warn, info,error, etc.

 logger = Logger.new(STDOUT)
logger = Logger.new("/var/log/my-daemon.log")

I use runit package to manage ruby services, it has svlogd than will redirect daemon output to log file, here is run script for logger process:

#!/bin/sh
set -e

LOG=/var/log/my-daemon

test -d "$LOG" || mkdir -p -m2750 "$LOG" && chown nobody:adm "$LOG"
exec chpst -unobody svlogd -tt "$LOG"

How to make `thin` webserver print log to STDOUT

i think that you need to tell rails to use STDOUT for logging instead of logging to log/development.log by putting config.logger = Logger.new(STDOUT) in your app/config/environments/development.rb.



Related Topics



Leave a reply



Submit