How to make Rails add line numbers / time stamps to log messages? - ruby-on-rails

I use tail -f to display the log file when developing my Rails app.
It shows the log messages (in color! :), which is great.
But with so much information in the 80-width console, it becomes difficult to track where a certain "set" of log messages started when, say, I clicked on a button to GET a resource.
It would be easier if there was a line number or even a time stamp at the start of each log message/line. This way I could remember that I need to start looking at the log "after line number 2365" or "after 2010/10/10 23:33:23:45".
Is this possible to do? Is there some Rails internal option for this ?

why don't you just edit your desired environment's log tags
development.rb
config.log_tags [ lambda {|r| DateTime.now } ]

If you wanted to get a time stamp:
class ApplicationController < ActionController::Base
# ...
before_filter :log_tracker
def log_tracker
Rails.logger.add(1, "Log Date: #{DateTime.now}")
end
end
And format the date however you see fit....
That would work for Rails 2.1 +, prior you could access the ActiveSupport::Buffered log object with the constant: RAILS_DEFAULT_LOGGER
Get access to the actual log file with Rails.logger.instance_values["log"]
Getting the number of lines is difficult because the logger only opens the file for writing, probably for economy. I get an IOError: not opened for reading when I try.
`

Thanks #scaney.
I found a solution here.
I modified that code to add my own coloring highlights (for development only of course!) and now I can see things like 'parameters' in yellow in the console and I'm very pleased now!
In case someone is interested, here is the code I put at the end of environment.rb.
Here is my current (dirty) implementation. Will probably fix this up later (maybe make a gem, but for now this serves me fine)
WARNING
DIRTY CODE FOLLOWS! Use at your own risk!
module ActiveSupport
class BufferedLogger
#define the ANSI escape codes for normal and bright colors
$my_my_ansi_colors = {
:normal => "\x1B[0m",
:black => "\x1B[30m",
:red => "\x1B[31m", #red
:green => "\x1B[32m",
:yellow => "\x1B[33m",
:blue => "\x1B[34m",
:magenta => "\x1B[35m",
:cyan => "\x1B[36m",
:white => "\x1B[37m",
:bred => "\x1B[1m\x1B[31m", #bright red
:bgreen => "\x1B[1m\x1B[32m",
:byellow => "\x1B[1m\x1B[33m",
:bblue => "\x1B[1m\x1B[34m",
:bmagenta => "\x1B[1m\x1B[35m",
:bcyan => "\x1B[1m\x1B[36m",
:bwhite => "\x1B[1m\x1B[37m",
}
#take a string and using the keys in the hash, replace the keys in the
#string but surround the keys with ANSI color codes
#No idea how to retain the case of the key!(TODO someday)
def my_highlight msgx,hash
return msgx if msgx.blank?
return msgx if hash.empty?
hash.each_pair do |k,v|
if not k.nil?
msgx.gsub! Regexp.new(k, Regexp::IGNORECASE), $my_my_ansi_colors[:normal]+$my_my_ansi_colors[v]+k.upcase+$my_my_ansi_colors[:normal]
end
end
msgx
end
def add(severity, message = nil, progname = nil, &block)
return if #level > severity
message = (message || (block && block.call) || progname).to_s
#INSERT BEGINS
if not $myownglobalnumbercounter.nil?
$myownglobalnumbercounter += 1
else
$myownglobalnumbercounter = 1
end
level = {
0 => "DEBUG",
1 => "INFO",
2 => "WARN",
3 => "ERROR",
4 => "FATAL"
}[severity] || "U"
message = "\x1B[0m[%d %s] : %s" % [$myownglobalnumbercounter,level,message]
message = my_highlight message, {
"debug" => :white,
"error" => :bred,
"info" => :bwhite,
"warning" => :byellow,
"warn" => :byellow ,
"parameters" => :byellow,
"#" => :bgreen,
"ms " => :bmagenta,
"GET " => :bmagenta,
"PUT " => :bmagenta,
"POST " => :bmagenta,
"DELETE " => :bmagenta
}
#INSERT ENDS
message = "#{message}\n" unless message[-1] == ?\n
buffer << message
auto_flush
message
end
end
end

Related

Relationships created by a rake task are not persisted though the rails server

I'm working my first project using Neo4j. I'm parsing wikipedia's page and pagelinks dumps to create a graph where the nodes are pages and the edges are links.
I've defined some rake tasks that download the dumps, parse the data, and save it in a Neo4j database. At the end of the rake task I print the number of pages and links created, and some of the pages with the most links. Here is the output of the raks task for the zawiki.
$ rake wiki[zawiki]
[ omitted ]
...
:: Done parsing zawiki
:: 1984 pages
:: 2144 links
:: The pages with the most links are:
9625.0 - Emijrp/List_of_Wikipedians_by_number_of_edits_(bots_included): 40
1363.0 - Gvangjsih_Bouxcuengh_Swcigih: 30
9112.0 - Fuzsuih: 27
1367.0 - Cungzcoj: 26
9279.0 - Vangz_Yenfanh: 19
It looks like pages and links are being created, but when I start a rails console, or the server the links aren't found.
$ rails c
jruby-1.7.5 :013 > Pages.all.count
=> 1984
jruby-1.7.5 :003 > Pages.all.reduce(0) { |count, page| count + page.links.count}
=> 0
jruby-1.7.5 :012 > Pages.all.sort_by { |p| p.links.count }.reverse[0...5].map { |p| p.links.count }
=> [0, 0, 0, 0, 0]
Here is the rake task, and this is the projects github page. Can anyone tell me why the links aren't saved?
DUMP_DIR = Rails.root.join('lib','assets')
desc "Download wiki dumps and parse them"
task :wiki, [:wiki] => 'wiki:all'
namespace :wiki do
task :all, [:wiki] => [:get, :parse] do |t, args|
# Print info about the newly created pages and links.
link_count = 0
Pages.all.each do |page|
link_count += page.links.count
end
indent "Done parsing #{args[:wiki]}"
indent "#{Pages.count} pages"
indent "#{link_count} links"
indent "The pages with the most links are:"
Pages.all.sort_by { |a| a.links.count }.reverse[0...5].each do |page|
puts "#{page.page_id} - #{page.title}: #{page.links.count}"
end
end
desc "Download wiki page and page links database dumps to /lib/assets"
task :get, :wiki do |t, args|
indent "Downloading dumps"
sh "#{Rails.root.join('lib', "get_wiki").to_s} #{args[:wiki]}"
indent "Done"
end
desc "Parse all dumps"
task :parse, [:wiki] => 'parse:all'
namespace :parse do
task :all, [:wiki] => [:pages, :pagelinks]
desc "Read wiki page dumps from lib/assests into the database"
task :pages, [:wiki] => :environment do |t, args|
parse_dumps('page', args[:wiki]) do |obj|
page = Pages.create_from_dump(obj)
end
indent = "Created #{Pages.count} pages"
end
desc "Read wiki pagelink dumps from lib/assests into the database"
task :pagelinks, [:wiki] => :environment do |t, args|
errors = 0
parse_dumps('pagelinks', args[:wiki]) do |from_id, namespace, to_title|
from = Pages.find(:page_id => from_id)
to = Pages.find(:title => to_title)
if to.nil? || from.nil?
errors = errors.succ
else
from.links << to
from.save
end
end
end
end
end
def indent *args
print ":: "
puts args
end
def parse_dumps(dump, wiki_match, &block)
wiki_match ||= /\w+/
DUMP_DIR.entries.each do |file|
file, wiki = *(file.to_s.match(Regexp.new "(#{wiki_match})-#{dump}.sql"))
if file
indent "Parsing #{wiki} #{dump.pluralize} from #{file}"
each_value(DUMP_DIR.join(file), &block)
end
end
end
def each_value(filename)
f = File.open(filename)
num_read = 0
begin # read file until line starting with INSERT INTO
line = f.gets
end until line.match /^INSERT INTO/
begin
line = line.match(/\(.*\)[,;]/)[0] # ignore begining of line until (...) object
begin
yield line[1..-3].split(',').map { |e| e.match(/^['"].*['"]$/) ? e[1..-2] : e.to_f }
num_read = num_read.succ
line = f.gets.chomp
end while(line[0] == '(') # until next insert block, or end of file
end while line.match /^INSERT INTO/ # Until line doesn't start with (...
f.close
end
app/models/pages.rb
class Pages < Neo4j::Rails::Model
include Neo4j::NodeMixin
has_n(:links).to(Pages)
property :page_id
property :namespace, :type => Fixnum
property :title, :type => String
property :restrictions, :type => String
property :counter, :type => Fixnum
property :is_redirect, :type => Fixnum
property :is_new, :type => Fixnum
property :random, :type => Float
property :touched, :type => String
property :latest, :type => Fixnum
property :length, :type => Fixnum
property :no_title_convert, :type => Fixnum
def self.create_from_dump(obj)
# TODO: I wonder if there is a way to compine these calls
page = {}
# order of this array is important, it corresponds to the data in obj
attrs = [:page_id, :namespace, :title, :restrictions, :counter, :is_redirect,
:is_new, :random, :touched, :latest, :length, :no_title_convert]
attrs.each_index { |i| page[attrs[i]] = obj[i] }
page = Pages.create(page)
return page
end
end
I must admit that I have no idea of how Neo4j works.
Transferring from other databases though, I too assume that either some validation is wrong, or maybe even something is misconfigured in your use of the database. The latter I can't give any advice on where to look, but if it's about validation, you can look at Page#errors or try calling Page#save! and see what it raises.
One crazy idea that just came to mind looking at this example is that maybe for that relation to be configured properly, you need a back reference, too.
Maybe has_n(:links).to(Page, :links) will help you. Or, if that doesn't work:
has_n(:links_left).to(Page, :links_right)
has_n(:links_right).from(Page, :links_left)
The more I look at this, the more I think the back reference to the same table is not configured properly and thus won't validate.

I18n: How to check if a translation key/value pairs is missing?

I am using Ruby on Rails 3.1.0 and the I18n gem. I (am implementing a plugin and) I would like to check at runtime if the I18n is missing a translation key/value pairs and, if so, to use a custom string. That is, I have:
validates :link_url,
:format => {
:with => REGEX,
:message => I18n.t(
'custom_invalid_format',
:scope => 'activerecord.errors.messages'
)
}
If in the .yml file there is not the following code
activerecord:
errors:
messages:
custom_invalid_format: This is the test error message 1
I would like to use the This is the test error message 2. Is it possible? If so, how can I make that?
BTW: For performance reasons, is it advisable to check at runtime if the translation key/value pairs is present?
You could pass a :default parameter to I18n.t:
I18n.t :missing, :default => 'Not here'
# => 'Not here'
You can read more about it here.
I just had the same question and I want to compute an automatic string in case the translation is missing. If I use the :default option I have to compute the automatic string every time even when the translation is not missing. So I searched for another solution.
You can add the option :raise => true or use I18n.translate! instead of I18n.translate. If no translation can be found an exception is raised.
begin
I18n.translate!('this.key.should.be.translated', :raise => true)
rescue I18n::MissingTranslationData
do_some_resource_eating_text_generation_here
end
I don't know how to this at runtime but you can use rake to find it out. You'll have create your own rake task for that. Here's one:
namespace :i18n do
desc "Find and list translation keys that do not exist in all locales"
task :missing_keys => :environment do
def collect_keys(scope, translations)
full_keys = []
translations.to_a.each do |key, translations|
new_scope = scope.dup << key
if translations.is_a?(Hash)
full_keys += collect_keys(new_scope, translations)
else
full_keys << new_scope.join('.')
end
end
return full_keys
end
# Make sure we've loaded the translations
I18n.backend.send(:init_translations)
puts "#{I18n.available_locales.size} #{I18n.available_locales.size == 1 ? 'locale' : 'locales'} available: #{I18n.available_locales.to_sentence}"
# Get all keys from all locales
all_keys = I18n.backend.send(:translations).collect do |check_locale, translations|
collect_keys([], translations).sort
end.flatten.uniq
puts "#{all_keys.size} #{all_keys.size == 1 ? 'unique key' : 'unique keys'} found."
missing_keys = {}
all_keys.each do |key|
I18n.available_locales.each do |locale|
I18n.locale = locale
begin
result = I18n.translate(key, :raise => true)
rescue I18n::MissingInterpolationArgument
# noop
rescue I18n::MissingTranslationData
if missing_keys[key]
missing_keys[key] << locale
else
missing_keys[key] = [locale]
end
end
end
end
puts "#{missing_keys.size} #{missing_keys.size == 1 ? 'key is missing' : 'keys are missing'} from one or more locales:"
missing_keys.keys.sort.each do |key|
puts "'#{key}': Missing from #{missing_keys[key].join(', ')}"
end
end
end
put the given in a .rake file in your lib/tasks directory and execute:
rake i18n:missing_keys
Information source is here and code on github here.
If you wish to pass variable to the message like This is the test error message {variable}
This is possible using variable in language file like below.
# app/views/home/index.html.erb
<%=t 'greet_username', :user => "Bill", :message => "Goodbye" %>
# config/locales/en.yml
en:
greet_username: "%{message}, %{user}!"
More description you can find here.

Error while testing assets : can't dup NilClass

I'm using rails 3.0.9 with ruby 1.9.2. I am doing a system that allow users to put items into different closets. One of the user's possibility is to copy an item of an other user into his own closet (please tell me if you don't understand, I'm not good in explaining things..).
To handle images, I'm using paperclip 2.3.16. I'm testing my app with Rspec-rails 2.6.1, Cucumber-rails 1.0.2. When I run my tests with guard & spork on the asset's copy part, i'm having a "TypeError: can't dup NilClass". I've search on the net for an answer but didn't find anything specific and working :(.
Have you an idea about this issue ?
My code for closet_spec.rb (if you need something else, just tell me) :
describe '#tidy_up' do
let!(:other_user) { Factory.create(:confirmed_user, :user_name => 'Plop', :email => 'coin#plop.fr') }
let!(:closet) { other_user.closets.first }
let!(:item) { Item.create! :type_id => 42, :closet_id => dream_dressing.id }
let!(:dressing_item) { Item.create! :type_id => 42, :closet_id => dressing.id }
before do
path = File.join(Rails.root, "spec", "support", 'image.jpg')
f = File.open(path)
2.times.map do |n|
i = Item.new :type_id => 2#, :asset => Asset.new(picture)
i.save!
puts ">>>>>>>>>>>>>>>>>>>>> #{i.id}"
i.build_asset(:picture => f)
closet.items << i
end
#count = 0
end
it 'copies given items and assets to closet' do
expect { dream_dressing.tidy_up closet.items }.to change { dream_dressing.items.count }.by 2
expect { dream_dressing.tidy_up closet.items }.to change { dream_dressing.items.map{|i| i.asset; puts ">>>>>>>>>>>>>>>>> asset : #{i.asset} " }.length }.by 2
end

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

Is there equivalent for PHP's print_r in Ruby / Rails?

In PHP you can do:
print_r($var) or vardump($var)
which prints "human-readible" information about variable.
Is there equivalent functions / helpers for those in Ruby / Rails ?
In Rails templates you can do
<%= debug an_object %>
and it will do nice HTML PRE output.
Try using pp.
You will need to require it in scripts (or in irb if your .irbc doesn't already do this):
require 'pp'
Then you can 'PrettyPrint' an object thus:
pp object
Instead of requiring 'pp' and using pp, you can simply do
p object
Tested example
require 'pp'
class A
def initialize
#a = 'somevar'
#b = [1,2,3]
#c = {'var' => 'val'}
end
end
a = A.new
pp a # Gives -> #<A:0x2c6d048 #a="somevar", #b=[1, 2, 3], #c={"var"=>"val"}>
p a # Gives -> #<A:0x2c6d048 #a="somevar", #b=[1, 2, 3], #c={"var"=>"val"}>. No need to require 'pp'
There's the method inspect which helps. Sometimes calling the to_s method on an object will help (to_s returns a string representation of the object). You can also query methods, local_variables, class_variables, instance_variables, constants and global_variables.
p ['Hello',"G'day",'Bonjour','Hola'].inspect
# >> "[\"Hello\", \"G'day\", \"Bonjour\", \"Hola\"]"
p ['Hello',"G'day",'Bonjour','Hola'].to_s
# >> "HelloG'dayBonjourHola"
p Array.new.methods
# >> ["select", "[]=", "inspect", "compact"...]
monkey = 'baboon'
p local_variables
# >> ["monkey"]
class Something
def initialize
#x, #y = 'foo', 'bar'
##class_variable = 'gorilla'
end
end
p Something.class_variables
# >> ["##class_variable"]
s = Something.new
p s.instance_variables
# >> ["#x", "#y"]
p IO.constants
# >> ["TRUNC", "SEEK_END", "LOCK_SH"...]
p global_variables
# >> ["$-d", "$\"", "$$", "$<", "$_", "$-K"...]
I know this is an old post, but it is the first thing that Google pops up when searching for "Ruby equivalent of PHP print_r". I'm using Ruby in the command line mode, and there's really not a very good equivalent. "pp" is ok for fairly simple structures, but as soon as you start nesting hashes in arrays in hashes in more arrays, it turns into a jumble pretty fast. Since I haven't found a good emulation of print_r, I wrote one myself. It's good enough for my purposes, not overly complicated and I thought I'd share it to save other people some headache. Compare the output with the real PHP print_r
def print_r(inHash, *indent)
#indent = indent.join
if (inHash.class.to_s == "Hash") then
print "Hash\n#{#indent}(\n"
inHash.each { |key, value|
if (value.class.to_s =~ /Hash/) || (value.class.to_s =~ /Array/) then
print "#{#indent} [#{key}] => "
self.print_r(value, "#{#indent} ")
else
puts "#{#indent} [#{key}] => #{value}"
end
}
puts "#{#indent})\n"
elsif (inHash.class.to_s == "Array") then
print "Array\n#{#indent}(\n"
inHash.each_with_index { |value,index|
if (value.class.to_s == "Hash") || (value.class.to_s == "Array") then
print "#{#indent} [#{index}] => "
self.print_r(value, "#{#indent} ")
else
puts "#{#indent} [#{index}] => #{value}"
end
}
puts "#{#indent})\n"
end
# Pop last indent off
8.times {#indent.chop!}
end
Here's an example (made messy on purpose to show why the PHP print_r is so nice):
carTools = [ "Socket Set", "Combination Wrenches", "Oil Filter puller", "Brake Compressor" ]
houseTools =[ "Circular Saw", "Miter Saw", "Drill" ]
garageItems = Hash["Car1" => "Ford Mustang", "Car2" => "Honda Civic", "Bike1" => "IronHorse"]
garageItems["Tools"] = Hash["Car Tools" => carTools, "House Tools" => houseTools]
constructionSupplies = Hash["Plywood" => ["3/4\" T&G Plywood Sheets", "1/2\" Plywood Sheets"],
"Boards" => ["2x4s", "2x6s", "Engineered I-Joists"],
"Drywall" => ["4x8 1/2\" Sheetrock", "Mesh tape", "Paper tape", "Joint compount"]]
carParts = Hash["Mustang" => ["Clutch", "Transmission", "3.55 Ring & Pinion Gears", "Differential", "30# Injectors", "Pro-M 77mm MAF"]]
garageItems["Supplies"] = ["Oil", "WD40", constructionSupplies, carParts, "Brake Fluid"]
print_r(garageItems)
Output of print_r (actually comprehensible by a human):
Hash
(
[Car1] => Ford Mustang
[Car2] => Honda Civic
[Bike1] => IronHorse
[Tools] => Hash
(
[Car Tools] => Array
(
[0] => Socket Set
[1] => Combination Wrenches
[2] => Oil Filter puller
[3] => Brake Compressor
)
[House Tools] => Array
(
[0] => Circular Saw
[1] => Miter Saw
[2] => Drill
)
)
[Supplies] => Array
(
[0] => Oil
[1] => WD40
[2] => Hash
(
[Plywood] => Array
(
[0] => 3/4" T&G Plywood Sheets
[1] => 1/2" Plywood Sheets
)
[Boards] => Array
(
[0] => 2x4s
[1] => 2x6s
[2] => Engineered I-Joists
)
[Drywall] => Array
(
[0] => 4x8 1/2" Sheetrock
[1] => Mesh tape
[2] => Paper tape
[3] => Joint compount
)
)
[3] => Hash
(
[Mustang] => Array
(
[0] => Clutch
[1] => Transmission
[2] => 3.55 Ring & Pinion Gears
[3] => Differential
[4] => 30# Injectors
[5] => Pro-M 77mm MAF
)
)
[4] => Brake Fluid
)
)
Check out the guide for debugging rails:
http://guides.rubyonrails.com/debugging_rails_applications.html
hints:
script/console is great to try stuff in the context of your app
script/server --debugger to start the server with a debugger turned on, you can then use 'debug' in your code to break into an interactive shell
One approach I lean on a lot is this:
logger.debug "OBJECT: #{an_object.to_yaml}"
Easy to read, although it can get a little unwieldy for large objects.
Guess I'm a little late to this, but what about logger.info [debug|warning]? Use this from Controllers and Models. It will show up in your log files (development.log when in dev mode); and the above mentioned <%= debug("str: " + str) %> for views.
These aren't exact answers to your questions but you can also use script/console to load your rails app in to an interactive session.
Lastly, you can place debugger in a line of your rails application and the browser will "hang" when your app executes this line and you'll be able to be in a debug session from the exact line your placed your debugger in the source code.

Resources