Print to log file without newline - Ruby on Rails - ruby-on-rails

I have a logger to print to a file:
logger = Logger.new Rails.root.join 'log/my.log'
How can I print several dots in one line in different logger calls?
i.e., if I do
some_array.each do |el|
logger.info '.'
el.some_method
end
These dots will be printed in different lines.

The << method lets you write messages without any formatting the log file.
For example:
require 'logger'
log = Logger.new "test.log"
log.info "Start"
5.times { log << "." }
log << "\n"
log.info "End"
Produces:
I, [2014-03-10T15:11:04.320495 #3410] INFO -- : Start
.....
I, [2014-03-10T15:11:42.157131 #3410] INFO -- : End
Unfortunately, this doesn't let you write to the same line as previous calls to log.info

You can use Logger#<<(msg)
Dump given message to the log device without any formatting. If no log
device exists, return nil.
some_array.each do |el|
logger << '.'
el.some_method
end

And you can still keep it restricted by the logger.level by using .info?
some_array.each do |el|
logger << '.' if logger.info?
el.some_method
end

Related

Backtrace Silencer not working

In my Rails app, I have set up the following backtrace silencer, as suggested by Michael Hartl in his Rails tutorial:
Rails.backtrace_cleaner.add_silencer { |line| line =~ /rvm/ }
But still I get all the noise I intended to filter out:
7:13:55 - INFO - Running: test/controllers/tags_controller_test.rb
Started
ERROR["test_should_get_index", TagsControllerTest, 0.45206]
test_should_get_index#TagsControllerTest (0.45s)
ActionController::UrlGenerationError:
ActionController::UrlGenerationError: No route matches {:action=>"index", :controller=>"tags"}
/Users/chris/.rvm/gems/ruby-2.0.0-p353/gems/actionpack-4.1.6/lib/action_dispatch/journey/formatter.rb:39:in `generate'
/Users/chris/.rvm/gems/ruby-2.0.0-p353/gems/actionpack-4.1.6/lib/action_dispatch/routing/route_set.rb:599:in `generate'
Clearly the string "rvm" is present in the last two lines. But still they show up. Changing the string to ".rvm" didn't make any difference.
This is because of https://github.com/vipulnsward/rails/blob/ecc8f283cfc1b002b5141c527a827e74b770f2f0/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb#L155-L156
Since application_trace is empty(This is because error is not from user code but route error), we are falling back to framework_trace, which does not filter it (it filters only noise).
You can solve it with creating your own log_formatter. In your development.rb and/or test.rb add
config.log_formatter = SilentLogger.new
config.log_formatter.add_silencer { |line| line =~ /rvm/ }
And create simple class in models with only method call required. There you can modify your backtrace as you wish.
class SilentLogger
def initialize
#silencers = []
end
def add_silencer(&block)
#silencers << block
end
def call(severity, timestamp, progname, msg)
backtrace = (String === msg) ? "#{msg}\n" : "#{msg.inspect}\n"
return backtrace if #silencers.empty?
#silencers.each do |s|
backtrace = backtrace.split("\n").delete_if { |line| s.call(line) }
end
backtrace.join("\n")
end
end
Your logs show /.rvm/ your setting /rvm/ - can this be the cause?
Give it a try and dont forget to stop / restart the server.

File.open works when getting an argument at runtime, but not when getting an argument from the command line

If I enter a file name without passing it in the command line (E.g. ARGV would be empty) everything works fine, but if I pass it an argument in the command line I always get a File or directory does not exist exception.
E.g. file.rb test.txt will except everything.
start_dir = "file-io-samples/junk/"
begin
if ARGV.empty?
print "Enter a file name: "
file_name = start_dir + gets.chomp
else
file_name = start_dir + ARGV[0].chomp
puts "Writing to #{file_name}..."
end
fail "File Exists" if File.exist? file_name
puts "File ready: " if file = File.open(file_name, 'w')
while line = gets
break if line.chomp == "!"
file.puts line
puts "Written"
end
rescue Exception
puts "Exception Raised: #{$!}"
ensure
puts file.close
end
I may have misunderstood your question, but if you are passing in an absolute path and it works, but a relative path in the code doesn't, you may need to add Rails.root to the file path.
You can join sections of path together like so:
file_name = Rails.root.join(start_dir, ARGV[0].chomp)

ruby net_dav sample puts

I'm trying to put a file on a site with WEB_DAV. (a ruby gem)
When I follow the example, I get a nil exception
#### GEMS
require 'rubygems'
begin
gem "net_dav"
rescue LoadError
system("gem install net_dav")
Gem.clear_paths
end
require 'net/dav'
uri = URI('https://staging.web.mysite');
user = "dave"
pasw = "correcthorsebatterystaple"
dav = Net::DAV.new(uri, :curl => false)
dav.verify_server = false
dav.credentials(user, pasw)
cargo = ("testing.txt")
File.open(cargo, "rb") { |stream|
dav.put(urI.path +'/'+ cargo, stream, File.size(cargo))
}
when I run this I get
`digest_auth': can't convert nil into String (TypeError)
this relates to line 197 in my nav.rb file.
request_digest << ':' << params['nonce']
So what I'm wondering is what step did I not add?
Is there a reasonable example of the correct use of this gem? Something that does something that works would be sweet :)
SIDE QUESTION: Is this the correct gem to use to do web_DAV? It seems an old unmaintained gem, perhaps there's something used by more to accomplish the task?
Try referencing the hash with a symbol rather than a string, i.e.
request_digest << ':' << params[:nonce]
In a simple test
baz = "baz"
params = {:foo => "bar"}
baz << ':' << params['foo']
results in the same error as you're getting.

Open3.popen3 function to open bz, gz, and txt files errors with 'No such file or directory' or 'not opened for reading'?

I'm trying to write a utility function that will open three different types of files: .bz2, .gz, and .txt. I can't just use File.read because it gives me garbage back for the compressed files. I'm trying to use Open3.popen3 so that I can give it a different command, but I'm getting a 'no such file or directory' error with the following code:
def file_info(file)
cmd = ''
if file.match("bz2") then
cmd = "bzcat #{file}"# | head -20"
elsif file.match("gz") then
cmd = "gunzip -c #{file}"
else
cmd = "cat #{file}"
end
puts "opening file #{file}"
Open3.popen3("#{cmd}", "r+") { |stdin, stdout, stderr|
puts "stdin #{stdin.inspect}"
stdin.read {|line|
puts "line is #{line}"
if line.match('^#') then
else
break
end
}
}
end
> No such file or directory - cat /tmp/test.txt
The file does exist. I've tried using cmd instead of #{cmd} with the same results in the popen3 cmd.
I decided to hardcode it to do the txt file as follows:
def file_info(file)
puts "opening file #{file}"
Open3.popen3("cat", file, "r+") { |stdin, stdout, stderr|
puts "stdin #{stdin.inspect}"
stdin.read {|line|
puts "line is #{line}"
if line.match('^#') then
else
break
end
}
}
end
This gives me back:
stdin #<IO:fd 6>
not opened for reading
What am I doing wrong?
When I do:
Open3.popen3("cat",file) { |stdin, stdout, stderr|
puts "stdout is #{stdout.inspect}"
stdout.read {|line|
puts "line is #{line}"
if line.match('^#') then
puts "found line #{line}"
else
break
end
}
}
I get no errors and the STDOUT line is printed, but neither line statement prints out anything.
After trying several different things, the solution I came up with was:
cmd = Array.new
if file.match(/\.bz2\z/) then
cmd = [ 'bzcat', file ]
elsif file.match(/\.gz\z/) then
cmd = [ 'gunzip', '-c', file ]
else
cmd = [ 'cat', file ]
end
Open3.popen3(*cmd) do |stdin, stdout, stderr|
puts "stdout is #{stdout}"
stdout.each do |line|
if line.match('^#') then
puts "line is #{line}"
else
break
end
end
end
From the fine manual (which is rather confusingly written):
*popen3(cmd, &block)
[...]
So a commandline string and list of argument strings can be accepted as follows.
Open3.popen3("echo a") {|i, o, e, t| ... }
Open3.popen3("echo", "a") {|i, o, e, t| ... }
Open3.popen3(["echo", "argv0"], "a") {|i, o, e, t| ... }
So when you do this:
Open3.popen3("cat /tmp/test.txt", "r+")
popen3 thinks that the command name is cat /tmp/test.txt and r+ is an argument to that command, hence the specific error that you're seeing:
No such file or directory - cat /tmp/test.txt
There's no need for the usual mode flags ("r+") with Open3.popen3 since it will separate handles for reading, writing, and errors; and, as you've seen, trying to supply the mode string just causes bugs and confusion.
The second case:
Open3.popen3("cat", file, "r+") { |stdin, stdout, stderr|
stdin.each {|line|
#...
Doesn't work because stdin is the command's standard input and that's what you would write to not read from, you'd want to stdout.read instead.
You should be building your commands as arrays and your match calls should be a little stricter:
if file.match(/\.bz2\z/) then
cmd = [ 'bzcat', file ]
elsif file.match(/\.gz\z/) then
cmd = [ 'gunzip', '-c', file ]
else
cmd = [ 'cat', file ]
end
and then splat them:
Open3.popen3(*cmd) do |stdin, stdout, stderr|
#...
end
Not only does this work but it will save you from funny filenames.
You could also avoid a useless use of cat (which someone will probably complain about) by skipping the Open3.popen3 for the non-compressed cases and using File.open instead. You might also want to consider checking the file's bytes to see what it contains rather than relying on the extension (or use ruby-filemagic to check for you).
You'd better use bzip2-ruby and GzipReader for reading corresponding files. Opening a separate process for that is too expensive, complex and fragile.

Rails logger format string configuration

How can I configure the rails logger to output its log strings in another format? I would like to get something that is more informative like:
[Log Level] [Time] [Message]
Debug : 01-20-2008 13:11:03.00 : Method Called
This would really help me when I want to tail my development.log for messages that only come from a certain log level, like debug.
Did some digging and found this post in the RubyOnRails Talk google group.
So I modified it a little bit and put it at the end of my environment.rb:
module ActiveSupport
class BufferedLogger
def add(severity, message = nil, progname = nil, &block)
return if #level > severity
message = (message || (block && block.call) || progname).to_s
level = {
0 => "DEBUG",
1 => "INFO",
2 => "WARN",
3 => "ERROR",
4 => "FATAL"
}[severity] || "U"
message = "[%s: %s #%d] %s" % [level,
Time.now.strftime("%m%d %H:%M:%S"),
$$,
message]
message = "#{message}\n" unless message[-1] == ?\n
buffer << message
auto_flush
message
end
end
end
This results in a format string like this:
[DEBUG: 0121 10:35:26 #57078] Rendered layouts/_header (0.00089)
For rails 4 apps, I've put together a simple gem that not only adds support for basic tagging like time stamp and log level, but even adds color to the log messages themselves.
https://github.com/phallguy/shog
The problem with tags is that they clutter your logs to the point where they are unreadable.
I'd recommend something like timber. It automatically augments your logs with context (level, time, session id, etc) without sacrificing readability.
# config/initializers/rack_logger.rb
module Rails
module Rack
class Logger < ActiveSupport::LogSubscriber
# Add UserAgent
def started_request_message(request)
'Started %s "%s" for %s at %s by %s' % [
request.request_method,
request.filtered_path,
request.ip,
Time.now.to_default_s,
request.env['HTTP_USER_AGENT'] ]
end
end
end
end
source link

Resources