lumberjack-1.0.13/ 0000755 0000041 0000041 00000000000 13260145344 013745 5 ustar www-data www-data lumberjack-1.0.13/Rakefile 0000644 0000041 0000041 00000001552 13260145344 015415 0 ustar www-data www-data require 'rubygems'
require 'rake'
require 'rubygems/package_task'
desc 'Default: run unit tests.'
task :default => :test
desc 'RVM likes to call it tests'
task :tests => :test
begin
require 'rspec'
require 'rspec/core/rake_task'
desc 'Run the unit tests'
RSpec::Core::RakeTask.new(:test)
rescue LoadError
task :test do
STDERR.puts "You must have rspec 2.0 installed to run the tests"
end
end
namespace :rbx do
desc "Cleanup *.rbc files in lib directory"
task :delete_rbc_files do
FileList["**/*.rbc"].each do |rbc_file|
File.delete(rbc_file)
end
nil
end
end
spec_file = File.expand_path('../lumberjack.gemspec', __FILE__)
if File.exist?(spec_file)
spec = eval(File.read(spec_file))
Gem::PackageTask.new(spec) do |p|
p.gem_spec = spec
end
Rake.application["package"].prerequisites.unshift("rbx:delete_rbc_files")
end
lumberjack-1.0.13/MIT_LICENSE 0000644 0000041 0000041 00000002040 13260145344 015457 0 ustar www-data www-data Copyright (c) 2011 Brian Durand
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
lumberjack-1.0.13/spec/ 0000755 0000041 0000041 00000000000 13260145344 014677 5 ustar www-data www-data lumberjack-1.0.13/spec/formatter/ 0000755 0000041 0000041 00000000000 13260145344 016702 5 ustar www-data www-data lumberjack-1.0.13/spec/formatter/exception_formatter_spec.rb 0000644 0000041 0000041 00000001270 13260145344 024322 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Formatter::ExceptionFormatter do
it "should convert an exception without a backtrace to a string" do
e = ArgumentError.new("not expected")
formatter = Lumberjack::Formatter::ExceptionFormatter.new
formatter.call(e).should == "ArgumentError: not expected"
end
it "should convert an exception with a backtrace to a string" do
begin
raise ArgumentError.new("not expected")
rescue => e
formatter = Lumberjack::Formatter::ExceptionFormatter.new
formatter.call(e).should == "ArgumentError: not expected#{Lumberjack::LINE_SEPARATOR} #{e.backtrace.join(Lumberjack::LINE_SEPARATOR + ' ')}"
end
end
end
lumberjack-1.0.13/spec/formatter/string_formatter_spec.rb 0000644 0000041 0000041 00000000521 13260145344 023630 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Formatter::StringFormatter do
it "should format objects as string by calling their to_s method" do
formatter = Lumberjack::Formatter::StringFormatter.new
formatter.call("abc").should == "abc"
formatter.call(:test).should == "test"
formatter.call(1).should == "1"
end
end
lumberjack-1.0.13/spec/formatter/pretty_print_formatter_spec.rb 0000644 0000041 0000041 00000000536 13260145344 025073 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Formatter::PrettyPrintFormatter do
it "should convert an object to a string using pretty print" do
object = Object.new
def object.pretty_print(q)
q.text "woot!"
end
formatter = Lumberjack::Formatter::PrettyPrintFormatter.new
formatter.call(object).should == "woot!"
end
end
lumberjack-1.0.13/spec/formatter/inspect_formatter_spec.rb 0000644 0000041 0000041 00000000633 13260145344 023773 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Formatter::InspectFormatter do
it "should format objects as string by calling their inspect method" do
formatter = Lumberjack::Formatter::InspectFormatter.new
formatter.call("abc").should == "\"abc\""
formatter.call(:test).should == ":test"
formatter.call(1).should == "1"
formatter.call([:a, 1, "b"]).should == [:a, 1, "b"].inspect
end
end
lumberjack-1.0.13/spec/spec_helper.rb 0000644 0000041 0000041 00000001141 13260145344 017512 0 ustar www-data www-data require File.expand_path("../../lib/lumberjack.rb", __FILE__)
require 'stringio'
require 'fileutils'
require 'timecop'
RSpec.configure do |config|
config.expect_with :rspec do |c|
c.syntax = [:should, :expect]
end
config.mock_with :rspec do |c|
c.syntax = [:should, :expect]
end
end
def tmp_dir
File.expand_path("../tmp", __FILE__)
end
def create_tmp_dir
FileUtils.rm_r(tmp_dir) if File.exist?(tmp_dir)
FileUtils.mkdir_p(tmp_dir)
end
def delete_tmp_dir
FileUtils.rm_r(tmp_dir)
end
def delete_tmp_files
Dir.glob(File.join(tmp_dir, "*")) do |file|
File.delete(file)
end
end
lumberjack-1.0.13/spec/log_entry_spec.rb 0000644 0000041 0000041 00000005634 13260145344 020250 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::LogEntry do
it "should have a time" do
t = Time.now
entry = Lumberjack::LogEntry.new(t, Lumberjack::Severity::INFO, "test", "app", 1500, "ABCD")
entry.time.should == t
entry.time = t + 1
entry.time.should == t + 1
end
it "should have a severity" do
entry = Lumberjack::LogEntry.new(Time.now, Lumberjack::Severity::INFO, "test", "app", 1500, "ABCD")
entry.severity.should == Lumberjack::Severity::INFO
entry.severity = Lumberjack::Severity::WARN
entry.severity.should == Lumberjack::Severity::WARN
end
it "should convert a severity label to a numeric level" do
entry = Lumberjack::LogEntry.new(Time.now, "INFO", "test", "app", 1500, "ABCD")
entry.severity.should == Lumberjack::Severity::INFO
end
it "should get the severity as a string" do
Lumberjack::LogEntry.new(Time.now, Lumberjack::Severity::DEBUG, "test", "app", 1500, nil).severity_label.should == "DEBUG"
Lumberjack::LogEntry.new(Time.now, Lumberjack::Severity::INFO, "test", "app", 1500, nil).severity_label.should == "INFO"
Lumberjack::LogEntry.new(Time.now, Lumberjack::Severity::WARN, "test", "app", 1500, nil).severity_label.should == "WARN"
Lumberjack::LogEntry.new(Time.now, Lumberjack::Severity::ERROR, "test", "app", 1500, nil).severity_label.should == "ERROR"
Lumberjack::LogEntry.new(Time.now, Lumberjack::Severity::FATAL, "test", "app", 1500, nil).severity_label.should == "FATAL"
Lumberjack::LogEntry.new(Time.now, -1, "test", "app", 1500, nil).severity_label.should == "UNKNOWN"
Lumberjack::LogEntry.new(Time.now, 1000, "test", "app", 1500, nil).severity_label.should == "UNKNOWN"
end
it "should have a message" do
entry = Lumberjack::LogEntry.new(Time.now, Lumberjack::Severity::INFO, "test", "app", 1500, "ABCD")
entry.message.should == "test"
entry.message = "new message"
entry.message.should == "new message"
end
it "should have a progname" do
entry = Lumberjack::LogEntry.new(Time.now, Lumberjack::Severity::INFO, "test", "app", 1500, "ABCD")
entry.progname.should == "app"
entry.progname = "prog"
entry.progname.should == "prog"
end
it "should have a pid" do
entry = Lumberjack::LogEntry.new(Time.now, Lumberjack::Severity::INFO, "test", "app", 1500, "ABCD")
entry.pid.should == 1500
entry.pid = 150
entry.pid.should == 150
end
it "should have a unit_of_work_id" do
entry = Lumberjack::LogEntry.new(Time.now, Lumberjack::Severity::INFO, "test", "app", 1500, "ABCD")
entry.unit_of_work_id.should == "ABCD"
entry.unit_of_work_id = "1234"
entry.unit_of_work_id.should == "1234"
end
it "should be converted to a string" do
t = Time.parse("2011-01-29T12:15:32.001")
entry = Lumberjack::LogEntry.new(t, Lumberjack::Severity::INFO, "test", "app", 1500, "ABCD")
entry.to_s.should == "[2011-01-29T12:15:32.001 INFO app(1500) #ABCD] test"
end
end
lumberjack-1.0.13/spec/lumberjack_spec.rb 0000644 0000041 0000041 00000001507 13260145344 020360 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack do
context "unit of work" do
it "should create a unit work with a unique id in a block" do
Lumberjack.unit_of_work_id.should == nil
Lumberjack.unit_of_work do
id_1 = Lumberjack.unit_of_work_id
id_1.should match(/^[0-9a-f]{12}$/)
Lumberjack.unit_of_work do
id_2 = Lumberjack.unit_of_work_id
id_2.should match(/^[0-9a-f]{12}$/)
id_2.should_not == id_1
end
id_1.should == Lumberjack.unit_of_work_id
end
Lumberjack.unit_of_work_id.should == nil
end
it "should allow you to specify a unit of work id for a block" do
Lumberjack.unit_of_work("foo") do
Lumberjack.unit_of_work_id.should == "foo"
end
Lumberjack.unit_of_work_id.should == nil
end
end
end
lumberjack-1.0.13/spec/logger_spec.rb 0000644 0000041 0000041 00000035041 13260145344 017520 0 ustar www-data www-data require 'spec_helper'
require 'pathname'
describe Lumberjack::Logger do
context "initialization" do
before :all do
create_tmp_dir
end
after :all do
delete_tmp_dir
end
it "should wrap an IO stream in a device" do
output = StringIO.new
logger = Lumberjack::Logger.new(output)
logger.device.class.should == Lumberjack::Device::Writer
end
it "should have a formatter" do
output = StringIO.new
logger = Lumberjack::Logger.new(output)
logger.formatter.should be
end
it "should open a file path in a device" do
logger = Lumberjack::Logger.new(File.join(tmp_dir, "log_file_1.log"))
logger.device.class.should == Lumberjack::Device::LogFile
end
it "should open a pathname in a device" do
logger = Lumberjack::Logger.new(Pathname.new(File.join(tmp_dir, "log_file_1.log")))
logger.device.class.should == Lumberjack::Device::LogFile
end
it "should use the null device if the stream is :null" do
logger = Lumberjack::Logger.new(:null)
logger.device.class.should == Lumberjack::Device::Null
end
it "should set the level with a numeric" do
logger = Lumberjack::Logger.new(:null, :level => Lumberjack::Severity::WARN)
logger.level.should == Lumberjack::Severity::WARN
end
it "should set the level with a level" do
logger = Lumberjack::Logger.new(:null, :level => :warn)
logger.level.should == Lumberjack::Severity::WARN
end
it "should default the level to INFO" do
logger = Lumberjack::Logger.new(:null)
logger.level.should == Lumberjack::Severity::INFO
end
it "should set the progname"do
logger = Lumberjack::Logger.new(:null, :progname => "app")
logger.progname.should == "app"
end
it "should create a thread to flush the device" do
Thread.should_receive(:new)
logger = Lumberjack::Logger.new(:null, :flush_seconds => 10)
end
end
context "attributes" do
it "should have a level" do
logger = Lumberjack::Logger.new
logger.level = Lumberjack::Severity::DEBUG
logger.level.should == Lumberjack::Severity::DEBUG
end
it "should have a progname" do
logger = Lumberjack::Logger.new
logger.progname = "app"
logger.progname.should == "app"
end
it "should be able to silence the log in a block" do
output = StringIO.new
logger = Lumberjack::Logger.new(output, :buffer_size => 0, :level => Lumberjack::Severity::INFO, :template => ":message")
logger.info("one")
logger.silence do
logger.level.should == Lumberjack::Severity::ERROR
logger.info("two")
logger.error("three")
end
logger.info("four")
output.string.split.should == ["one", "three", "four"]
end
it "should be able to customize the level of silence in a block" do
output = StringIO.new
logger = Lumberjack::Logger.new(output, :buffer_size => 0, :level => Lumberjack::Severity::INFO, :template => ":message")
logger.info("one")
logger.silence(Lumberjack::Severity::FATAL) do
logger.level.should == Lumberjack::Severity::FATAL
logger.info("two")
logger.error("three")
logger.fatal("woof")
end
logger.info("four")
output.string.split.should == ["one", "woof", "four"]
end
it "should not be able to silence the logger if silencing is disabled" do
output = StringIO.new
logger = Lumberjack::Logger.new(output, :buffer_size => 0, :level => Lumberjack::Severity::INFO, :template => ":message")
logger.silencer = false
logger.info("one")
logger.silence do
logger.level.should == Lumberjack::Severity::INFO
logger.info("two")
logger.error("three")
end
logger.info("four")
output.string.split.should == ["one", "two", "three", "four"]
end
it "should be able to set the progname in a block" do
logger = Lumberjack::Logger.new
logger.set_progname("app")
logger.progname.should == "app"
block_executed = false
logger.set_progname("xxx") do
block_executed = true
logger.progname.should == "xxx"
end
block_executed.should == true
logger.progname.should == "app"
end
it "should only affect the current thread when silencing the logger" do
output = StringIO.new
logger = Lumberjack::Logger.new(output, :buffer_size => 0, :level => Lumberjack::Severity::INFO, :template => ":message")
# status is used to make sure the two threads are executing at the same time
status = 0
begin
Thread.new do
logger.silence do
logger.info("inner")
status = 1
loop{ sleep(0.001); break if status == 2}
end
end
loop{ sleep(0.001); break if status == 1}
logger.info("outer")
status = 2
logger.close
output.string.should include("outer")
output.string.should_not include("inner")
ensure
status = 2
end
end
it "should only affect the current thread when changing the progname in a block" do
output = StringIO.new
logger = Lumberjack::Logger.new(output, :progname => "thread1", :buffer_size => 0, :level => Lumberjack::Severity::INFO, :template => ":progname :message")
# status is used to make sure the two threads are executing at the same time
status = 0
begin
Thread.new do
logger.set_progname("thread2") do
logger.info("inner")
status = 1
loop{ sleep(0.001); break if status == 2}
end
end
loop{ sleep(0.001); break if status == 1}
logger.info("outer")
status = 2
logger.close
output.string.should include("thread1")
output.string.should include("thread2")
ensure
status = 2
end
end
end
context "flushing" do
it "should autoflush the buffer if it hasn't been flushed in a specified number of seconds" do
output = StringIO.new
logger = Lumberjack::Logger.new(output, :flush_seconds => 0.1, :level => Lumberjack::Severity::INFO, :template => ":message", :buffer_size => 32767)
logger.info("message 1")
logger.info("message 2")
output.string.should == ""
sleep(0.15)
output.string.split(Lumberjack::LINE_SEPARATOR).should == ["message 1", "message 2"]
logger.info("message 3")
output.string.should_not include("message 3")
sleep(0.15)
output.string.split(Lumberjack::LINE_SEPARATOR).should == ["message 1", "message 2", "message 3"]
end
it "should write the log entries to the device on flush and update the last flushed time" do
output = StringIO.new
logger = Lumberjack::Logger.new(output, :level => Lumberjack::Severity::INFO, :template => ":message", :buffer_size => 32767)
logger.info("message 1")
output.string.should == ""
last_flushed_at = logger.last_flushed_at
logger.flush
output.string.split(Lumberjack::LINE_SEPARATOR).should == ["message 1"]
logger.last_flushed_at.should >= last_flushed_at
end
it "should flush the buffer and close the devices" do
output = StringIO.new
logger = Lumberjack::Logger.new(output, :level => Lumberjack::Severity::INFO, :template => ":message", :buffer_size => 32767)
logger.info("message 1")
output.string.should == ""
logger.close
output.string.split(Lumberjack::LINE_SEPARATOR).should == ["message 1"]
output.should be_closed
end
end
context "logging" do
let(:output){ StringIO.new }
let(:device){ Lumberjack::Device::Writer.new(output, :buffer_size => 0) }
let(:logger){ Lumberjack::Logger.new(device, :level => Lumberjack::Severity::INFO, :progname => "test") }
let(:n){ Lumberjack::LINE_SEPARATOR }
it "should add entries with a numeric severity and a message" do
time = Time.parse("2011-01-30T12:31:56.123")
Time.stub(:now => time)
logger.add(Lumberjack::Severity::INFO, "test")
output.string.should == "[2011-01-30T12:31:56.123 INFO test(#{$$}) #] test#{n}"
end
it "should add entries with a severity label" do
time = Time.parse("2011-01-30T12:31:56.123")
Time.stub(:now => time)
logger.add(:info, "test")
output.string.should == "[2011-01-30T12:31:56.123 INFO test(#{$$}) #] test#{n}"
end
it "should add entries with a custom progname and message" do
time = Time.parse("2011-01-30T12:31:56.123")
Time.stub(:now => time)
logger.add(Lumberjack::Severity::INFO, "test", "app")
output.string.should == "[2011-01-30T12:31:56.123 INFO app(#{$$}) #] test#{n}"
end
it "should add entries with a local progname and message" do
time = Time.parse("2011-01-30T12:31:56.123")
Time.stub(:now => time)
logger.set_progname("block") do
logger.add(Lumberjack::Severity::INFO, "test")
end
output.string.should == "[2011-01-30T12:31:56.123 INFO block(#{$$}) #] test#{n}"
end
it "should add entries with a progname but no message or block" do
time = Time.parse("2011-01-30T12:31:56.123")
Time.stub(:now => time)
logger.set_progname("default") do
logger.add(Lumberjack::Severity::INFO, nil, "message")
end
output.string.should == "[2011-01-30T12:31:56.123 INFO default(#{$$}) #] message#{n}"
end
it "should add entries with a block" do
time = Time.parse("2011-01-30T12:31:56.123")
Time.stub(:now => time)
logger.add(Lumberjack::Severity::INFO){"test"}
output.string.should == "[2011-01-30T12:31:56.123 INFO test(#{$$}) #] test#{n}"
end
it "should log entries (::Logger compatibility)" do
time = Time.parse("2011-01-30T12:31:56.123")
Time.stub(:now => time)
logger.log(Lumberjack::Severity::INFO, "test")
output.string.should == "[2011-01-30T12:31:56.123 INFO test(#{$$}) #] test#{n}"
end
it "should append messages with unknown severity to the log" do
time = Time.parse("2011-01-30T12:31:56.123")
Time.stub(:now => time)
logger << "test"
output.string.should == "[2011-01-30T12:31:56.123 UNKNOWN test(#{$$}) #] test#{n}"
end
it "should ouput entries to STDERR if they can't be written the the device" do
stderr = $stderr
$stderr = StringIO.new
begin
time = Time.parse("2011-01-30T12:31:56.123")
Time.stub(:now => time)
device.should_receive(:write).and_raise(StandardError.new("Cannot write to device"))
logger.add(Lumberjack::Severity::INFO, "test")
$stderr.string.should include("[2011-01-30T12:31:56.123 INFO test(#{$$})] test")
$stderr.string.should include("StandardError: Cannot write to device")
ensure
$stderr = stderr
end
end
context "log helper methods" do
let(:device){ Lumberjack::Device::Writer.new(output, :buffer_size => 0, :template => ":message") }
it "should only add messages whose severity is greater or equal to the logger level" do
logger.add(Lumberjack::Severity::DEBUG, "debug")
logger.add(Lumberjack::Severity::INFO, "info")
logger.add(Lumberjack::Severity::ERROR, "error")
output.string.should == "info#{n}error#{n}"
end
it "should only log fatal messages when the level is set to fatal" do
logger.level = Lumberjack::Severity::FATAL
logger.fatal("fatal")
logger.fatal?.should == true
logger.error("error")
logger.error?.should == false
logger.warn("warn")
logger.warn?.should == false
logger.info("info")
logger.info?.should == false
logger.debug("debug")
logger.debug?.should == false
logger.unknown("unknown")
output.string.should == "fatal#{n}unknown#{n}"
end
it "should only log error messages and higher when the level is set to error" do
logger.level = Lumberjack::Severity::ERROR
logger.fatal("fatal")
logger.fatal?.should == true
logger.error("error")
logger.error?.should == true
logger.warn("warn")
logger.warn?.should == false
logger.info("info")
logger.info?.should == false
logger.debug("debug")
logger.debug?.should == false
logger.unknown("unknown")
output.string.should == "fatal#{n}error#{n}unknown#{n}"
end
it "should only log warn messages and higher when the level is set to warn" do
logger.level = Lumberjack::Severity::WARN
logger.fatal("fatal")
logger.fatal?.should == true
logger.error("error")
logger.error?.should == true
logger.warn("warn")
logger.warn?.should == true
logger.info("info")
logger.info?.should == false
logger.debug("debug")
logger.debug?.should == false
logger.unknown("unknown")
output.string.should == "fatal#{n}error#{n}warn#{n}unknown#{n}"
end
it "should only log info messages and higher when the level is set to info" do
logger.level = Lumberjack::Severity::INFO
logger.fatal("fatal")
logger.fatal?.should == true
logger.error("error")
logger.error?.should == true
logger.warn("warn")
logger.warn?.should == true
logger.info("info")
logger.info?.should == true
logger.debug("debug")
logger.debug?.should == false
logger.unknown("unknown")
output.string.should == "fatal#{n}error#{n}warn#{n}info#{n}unknown#{n}"
end
it "should log all messages when the level is set to debug" do
logger.level = Lumberjack::Severity::DEBUG
logger.fatal("fatal")
logger.fatal?.should == true
logger.error("error")
logger.error?.should == true
logger.warn("warn")
logger.warn?.should == true
logger.info("info")
logger.info?.should == true
logger.debug("debug")
logger.debug?.should == true
logger.unknown("unknown")
output.string.should == "fatal#{n}error#{n}warn#{n}info#{n}debug#{n}unknown#{n}"
end
it "should only log unkown messages when the level is set above fatal" do
logger.level = Lumberjack::Severity::FATAL + 1
logger.fatal("fatal")
logger.fatal?.should == false
logger.error("error")
logger.error?.should == false
logger.warn("warn")
logger.warn?.should == false
logger.info("info")
logger.info?.should == false
logger.debug("debug")
logger.debug?.should == false
logger.unknown("unknown")
output.string.should == "unknown#{n}"
end
end
end
end
lumberjack-1.0.13/spec/severity_spec.rb 0000644 0000041 0000041 00000002234 13260145344 020111 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Severity do
it "should convert a level to a label" do
Lumberjack::Severity.level_to_label(Lumberjack::Severity::DEBUG).should == "DEBUG"
Lumberjack::Severity.level_to_label(Lumberjack::Severity::INFO).should == "INFO"
Lumberjack::Severity.level_to_label(Lumberjack::Severity::WARN).should == "WARN"
Lumberjack::Severity.level_to_label(Lumberjack::Severity::ERROR).should == "ERROR"
Lumberjack::Severity.level_to_label(Lumberjack::Severity::FATAL).should == "FATAL"
Lumberjack::Severity.level_to_label(-1).should == "UNKNOWN"
end
it "should convert a label to a level" do
Lumberjack::Severity.label_to_level("DEBUG").should == Lumberjack::Severity::DEBUG
Lumberjack::Severity.label_to_level(:info).should == Lumberjack::Severity::INFO
Lumberjack::Severity.label_to_level(:warn).should == Lumberjack::Severity::WARN
Lumberjack::Severity.label_to_level("Error").should == Lumberjack::Severity::ERROR
Lumberjack::Severity.label_to_level("FATAL").should == Lumberjack::Severity::FATAL
Lumberjack::Severity.label_to_level("???").should == Lumberjack::Severity::UNKNOWN
end
end
lumberjack-1.0.13/spec/template_spec.rb 0000644 0000041 0000041 00000003622 13260145344 020054 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Template do
let(:time_string){ "2011-01-15T14:23:45.123" }
let(:time){ Time.parse(time_string) }
let(:entry){ Lumberjack::LogEntry.new(time, Lumberjack::Severity::INFO, "line 1#{Lumberjack::LINE_SEPARATOR}line 2#{Lumberjack::LINE_SEPARATOR}line 3", "app", 12345, "ABCD") }
it "should format a log entry with a template string" do
template = Lumberjack::Template.new(":message - :severity, :time, :progname@:pid (:unit_of_work_id)")
template.call(entry).should == "line 1 - INFO, 2011-01-15T14:23:45.123, app@12345 (ABCD)#{Lumberjack::LINE_SEPARATOR}line 2#{Lumberjack::LINE_SEPARATOR}line 3"
end
it "should be able to specify the time format for log entries as microseconds" do
template = Lumberjack::Template.new(":message (:time)", :time_format => :microseconds)
template.call(entry).should == "line 1 (2011-01-15T14:23:45.123000)#{Lumberjack::LINE_SEPARATOR}line 2#{Lumberjack::LINE_SEPARATOR}line 3"
end
it "should be able to specify the time format for log entries as milliseconds" do
template = Lumberjack::Template.new(":message (:time)", :time_format => :milliseconds)
template.call(entry).should == "line 1 (2011-01-15T14:23:45.123)#{Lumberjack::LINE_SEPARATOR}line 2#{Lumberjack::LINE_SEPARATOR}line 3"
end
it "should be able to specify the time format for log entries with a custom format" do
template = Lumberjack::Template.new(":message (:time)", :time_format => "%m/%d/%Y, %I:%M:%S %p")
template.call(entry).should == "line 1 (01/15/2011, 02:23:45 PM)#{Lumberjack::LINE_SEPARATOR}line 2#{Lumberjack::LINE_SEPARATOR}line 3"
end
it "should be able to specify a template for additional lines in a message" do
template = Lumberjack::Template.new(":message (:time)", :additional_lines => " // :message")
template.call(entry).should == "line 1 (2011-01-15T14:23:45.123) // line 2 // line 3"
end
end
lumberjack-1.0.13/spec/rack/ 0000755 0000041 0000041 00000000000 13260145344 015617 5 ustar www-data www-data lumberjack-1.0.13/spec/rack/request_id_spec.rb 0000644 0000041 0000041 00000004022 13260145344 021320 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Rack::RequestId do
it "should use the action dispatch request id if it exists" do
app = lambda{|env| [200, {"Content-Type" => env["Content-Type"], "Unit-Of-Work" => Lumberjack.unit_of_work_id.to_s}, ["OK"]]}
handler = Lumberjack::Rack::RequestId.new(app)
response = handler.call("Content-Type" => "text/plain", "action_dispatch.request_id" => "0123-4567-89ab-cdef")
response[0].should == 200
response[1]["Content-Type"].should == "text/plain"
response[1]["Unit-Of-Work"].should == "0123-4567-89ab-cdef"
response[2].should == ["OK"]
end
it "should use an abbreviated action dispatch request id if abbreviated is true" do
app = lambda{|env| [200, {"Content-Type" => env["Content-Type"], "Unit-Of-Work" => Lumberjack.unit_of_work_id.to_s}, ["OK"]]}
handler = Lumberjack::Rack::RequestId.new(app, true)
response = handler.call("Content-Type" => "text/plain", "action_dispatch.request_id" => "0123-4567-89ab-cdef")
response[0].should == 200
response[1]["Content-Type"].should == "text/plain"
response[1]["Unit-Of-Work"].should == "0123"
response[2].should == ["OK"]
end
it "should create a unit of work in a middleware stack if the request id doesn't exist" do
app = lambda{|env| [200, {"Content-Type" => env["Content-Type"], "Unit-Of-Work" => Lumberjack.unit_of_work_id.to_s}, ["OK"]]}
handler = Lumberjack::Rack::RequestId.new(app)
response = handler.call("Content-Type" => "text/plain")
response[0].should == 200
response[1]["Content-Type"].should == "text/plain"
unit_of_work_1 = response[1]["Unit-Of-Work"]
response[2].should == ["OK"]
response = handler.call("Content-Type" => "text/html")
response[0].should == 200
response[1]["Content-Type"].should == "text/html"
unit_of_work_2 = response[1]["Unit-Of-Work"]
response[2].should == ["OK"]
unit_of_work_1.should_not == nil
unit_of_work_2.should_not == nil
unit_of_work_1.should_not == unit_of_work_2
end
end
lumberjack-1.0.13/spec/rack/unit_of_work_spec.rb 0000644 0000041 0000041 00000001615 13260145344 021666 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Rack::UnitOfWork do
it "should create a unit of work in a middleware stack" do
app = lambda{|env| [200, {"Content-Type" => env["Content-Type"], "Unit-Of-Work" => Lumberjack.unit_of_work_id.to_s}, ["OK"]]}
handler = Lumberjack::Rack::UnitOfWork.new(app)
response = handler.call("Content-Type" => "text/plain")
response[0].should == 200
response[1]["Content-Type"].should == "text/plain"
unit_of_work_1 = response[1]["Unit-Of-Work"]
response[2].should == ["OK"]
response = handler.call("Content-Type" => "text/html")
response[0].should == 200
response[1]["Content-Type"].should == "text/html"
unit_of_work_2 = response[1]["Unit-Of-Work"]
response[2].should == ["OK"]
unit_of_work_1.should_not == nil
unit_of_work_2.should_not == nil
unit_of_work_1.should_not == unit_of_work_2
end
end
lumberjack-1.0.13/spec/device/ 0000755 0000041 0000041 00000000000 13260145344 016136 5 ustar www-data www-data lumberjack-1.0.13/spec/device/writer_spec.rb 0000644 0000041 0000041 00000011244 13260145344 021013 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Device::Writer do
let(:time_string){ "2011-01-15T14:23:45.123" }
let(:time){ Time.parse(time_string) }
let(:stream){ StringIO.new }
let(:entry){ Lumberjack::LogEntry.new(time, Lumberjack::Severity::INFO, "test message", "app", 12345, "ABCD") }
it "should buffer output and not write directly to the stream" do
device = Lumberjack::Device::Writer.new(stream, :template => ":message", :buffer_size => 32767)
device.write(entry)
stream.string.should == ""
device.flush
stream.string.should == "test message#{Lumberjack::LINE_SEPARATOR}"
end
it "should flush the buffer when it gets to the specified size" do
device = Lumberjack::Device::Writer.new(stream, :buffer_size => 15, :template => ":message")
device.write(entry)
stream.string.should == ""
device.write(entry)
stream.string.should == "test message#{Lumberjack::LINE_SEPARATOR}test message#{Lumberjack::LINE_SEPARATOR}"
end
it "should sync the stream and flush it when the device is flushed" do
# Create an IO like object that require flush to be called
io = Object.new
def io.init; @string = ""; @buffer = ""; @sync = false; end
def io.write(string); @buffer << string; end
def io.flush; @string << @buffer; @buffer = ""; end
def io.string; @string; end
def io.sync=(val); @sync = val; end
def io.sync; @sync; end
io.init
device = Lumberjack::Device::Writer.new(io, :template => ":message", :buffer_size => 32767)
device.write(entry)
io.string.should == ""
device.flush
io.string.should == "test message#{Lumberjack::LINE_SEPARATOR}"
io.sync.should == true
end
it "should be able to set the buffer size" do
device = Lumberjack::Device::Writer.new(stream, :buffer_size => 15)
device.buffer_size.should == 15
device.buffer_size = 100
device.buffer_size.should == 100
end
it "should have a default buffer size of 0" do
device = Lumberjack::Device::Writer.new(stream)
device.buffer_size.should == 0
end
it "should write entries out to the stream with a default template" do
device = Lumberjack::Device::Writer.new(stream)
device.write(entry)
device.flush
stream.string.should == "[2011-01-15T14:23:45.123 INFO app(12345) #ABCD] test message#{Lumberjack::LINE_SEPARATOR}"
end
it "should write entries out to the stream with a custom template" do
device = Lumberjack::Device::Writer.new(stream, :template => ":message")
device.write(entry)
device.flush
stream.string.should == "test message#{Lumberjack::LINE_SEPARATOR}"
end
it "should be able to specify the time format for the template" do
device = Lumberjack::Device::Writer.new(stream, :time_format => :microseconds)
device.write(entry)
device.flush
stream.string.should == "[2011-01-15T14:23:45.123000 INFO app(12345) #ABCD] test message#{Lumberjack::LINE_SEPARATOR}"
end
it "should be able to specify a block template for log entries" do
device = Lumberjack::Device::Writer.new(stream, :template => lambda{|e| e.message.upcase})
device.write(entry)
device.flush
stream.string.should == "TEST MESSAGE#{Lumberjack::LINE_SEPARATOR}"
end
it "should write to STDERR if an error is raised when flushing to the stream" do
stderr = $stderr
$stderr = StringIO.new
begin
device = Lumberjack::Device::Writer.new(stream, :template => ":message")
stream.should_receive(:write).and_raise(StandardError.new("Cannot write to stream"))
device.write(entry)
device.flush
$stderr.string.should include("test message#{Lumberjack::LINE_SEPARATOR}")
$stderr.string.should include("StandardError: Cannot write to stream")
ensure
$stderr = stderr
end
end
context "multi line messages" do
let(:message){ "line 1#{Lumberjack::LINE_SEPARATOR}line 2#{Lumberjack::LINE_SEPARATOR}line 3" }
let(:entry){ Lumberjack::LogEntry.new(time, Lumberjack::Severity::INFO, message, "app", 12345, "ABCD") }
it "should have a default template for multiline messages" do
device = Lumberjack::Device::Writer.new(stream)
device.write(entry)
device.flush
stream.string.split(Lumberjack::LINE_SEPARATOR).should == ["[2011-01-15T14:23:45.123 INFO app(12345) #ABCD] line 1", "> [#ABCD] line 2", "> [#ABCD] line 3"]
end
it "should be able to specify a template for multiple line messages" do
device = Lumberjack::Device::Writer.new(stream, :additional_lines => " // :message")
device.write(entry)
device.flush
stream.string.should == "[2011-01-15T14:23:45.123 INFO app(12345) #ABCD] line 1 // line 2 // line 3#{Lumberjack::LINE_SEPARATOR}"
end
end
end
lumberjack-1.0.13/spec/device/rolling_log_file_spec.rb 0000644 0000041 0000041 00000012545 13260145344 023012 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Device::RollingLogFile do
before :all do
Lumberjack::Device::SizeRollingLogFile #needed by jruby
create_tmp_dir
end
after :all do
delete_tmp_dir
end
before :each do
delete_tmp_files
end
let(:entry){ Lumberjack::LogEntry.new(Time.now, 1, "New log entry", nil, $$, nil) }
it "should check for rolling the log file on flush" do
device = Lumberjack::Device::RollingLogFile.new(File.join(tmp_dir, "test.log"), :buffer_size => 32767, :min_roll_check => 0)
device.write(entry)
expect(device).to receive(:roll_file?).twice.and_return(false)
device.flush
device.close
end
it "should roll the file by archiving the existing file and opening a new stream and calling after_roll" do
log_file = File.join(tmp_dir, "test_2.log")
device = Lumberjack::Device::RollingLogFile.new(log_file, :template => ":message", :buffer_size => 32767, :min_roll_check => 0)
expect(device).to receive(:roll_file?).and_return(false, true)
expect(device).to receive(:after_roll)
device.stub(:archive_file_suffix => "rolled")
device.write(entry)
device.flush
device.write(Lumberjack::LogEntry.new(Time.now, 1, "Another log entry", nil, $$, nil))
device.close
File.read("#{log_file}.rolled").should == "New log entry#{Lumberjack::LINE_SEPARATOR}"
File.read(log_file).should == "Another log entry#{Lumberjack::LINE_SEPARATOR}"
end
it "should reopen the file if the stream inode doesn't match the file path inode" do
log_file = File.join(tmp_dir, "test_3.log")
device = Lumberjack::Device::RollingLogFile.new(log_file, :template => ":message", :min_roll_check => 0)
device.stub(:roll_file? => false)
device.write(entry)
device.flush
File.rename(log_file, "#{log_file}.rolled")
device.flush
device.write(Lumberjack::LogEntry.new(Time.now, 1, "Another log entry", nil, $$, nil))
device.close
File.read("#{log_file}.rolled").should == "New log entry#{Lumberjack::LINE_SEPARATOR}"
File.read(log_file).should == "Another log entry#{Lumberjack::LINE_SEPARATOR}"
end
it "should roll the file properly with multiple thread and processes using it" do
log_file = File.join(tmp_dir, "test_4.log")
process_count = 8
thread_count = 4
entry_count = 400
max_size = 128
severity = Lumberjack::Severity::INFO
message = "This is a test message that is written to the log file to indicate what the state of the application is."
logger_test = lambda do
device = Lumberjack::Device::SizeRollingLogFile.new(log_file, :max_size => max_size, :template => ":message", :buffer_size => 32767, :min_roll_check => 0)
threads = []
thread_count.times do
threads << Thread.new do
entry_count.times do |i|
device.write(Lumberjack::LogEntry.new(Time.now, severity, message, "test", $$, nil))
device.flush if i % 10 == 0
end
device.flush
end
end
threads.each{|thread| thread.value}
device.close
end
# Process.fork is unavailable on jruby so we need to use the java threads instead.
if RUBY_PLATFORM.match(/java/)
outer_threads = []
process_count.times do
outer_threads << Thread.new(&logger_test)
end
outer_threads.each{|thread| thread.value}
else
process_count.times do
Process.fork(&logger_test)
end
Process.waitall
end
line_count = 0
file_count = 0
Dir.glob("#{log_file}*").each do |file|
file_count += 1
lines = File.read(file).split(Lumberjack::LINE_SEPARATOR)
line_count += lines.size
lines.each do |line|
line.should == message
end
end
file_count.should > 3
end
it "should only keep a specified number of archived log files" do
log_file = File.join(tmp_dir, "test_5.log")
device = Lumberjack::Device::RollingLogFile.new(log_file, :template => ":message", :keep => 2, :buffer_size => 32767, :min_roll_check => 0)
expect(device).to receive(:roll_file?).and_return(false, true, true, true)
expect(device).to receive(:archive_file_suffix).and_return("delete", "another", "keep")
t = Time.now
expect(File).to receive(:ctime).with("#{log_file}.delete").at_least(1).times.and_return(t + 1)
expect(File).to receive(:ctime).with("#{log_file}.another").at_least(1).times.and_return(t + 2)
expect(File).to receive(:ctime).with("#{log_file}.keep").at_least(1).times.and_return(t + 3)
device.write(entry)
device.flush
device.write(entry)
device.flush
device.write(entry)
device.flush
device.write(entry)
device.close
Dir.glob("#{log_file}*").sort.should == [log_file, "#{log_file}.another", "#{log_file}.keep"]
end
context "when file is rolled" do
let(:log_file) { File.join(tmp_dir, "test_6.log") }
let(:device) do
device = Lumberjack::Device::RollingLogFile.new(log_file, :template => ":message", :keep => 2, :buffer_size => 32767, :min_roll_check => 0)
device.stub(:roll_file?).and_return(true)
device.stub(:archive_file_suffix => "rolled")
device
end
before do
device.write(entry)
device.flush
end
it "reopens file with proper encoding" do
encoding = device.send(:stream).external_encoding
expect(encoding).to_not be_nil
expect(encoding.name).to eq "ASCII-8BIT"
end
end
end
lumberjack-1.0.13/spec/device/date_rolling_log_file_spec.rb 0000644 0000041 0000041 00000004547 13260145344 024012 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Device::DateRollingLogFile do
before :all do
create_tmp_dir
end
after :all do
delete_tmp_dir
end
before :each do
delete_tmp_files
end
let(:one_day){ 60 * 60 * 24 }
it "should roll the file daily" do
now = Time.now
log_file = File.join(tmp_dir, "a#{rand(1000000000)}.log")
device = Lumberjack::Device::DateRollingLogFile.new(log_file, :roll => :daily, :template => ":message", :min_roll_check => 0)
logger = Lumberjack::Logger.new(device, :buffer_size => 2)
Timecop.travel(now) do
logger.error("test day one")
logger.flush
end
Timecop.travel(now + one_day) do
logger.error("test day two")
logger.close
end
File.read("#{log_file}.#{now.to_date.strftime('%Y-%m-%d')}").should == "test day one#{Lumberjack::LINE_SEPARATOR}"
File.read(log_file).should == "test day two#{Lumberjack::LINE_SEPARATOR}"
end
it "should roll the file weekly" do
now = Time.now
log_file = File.join(tmp_dir, "b#{rand(1000000000)}.log")
device = Lumberjack::Device::DateRollingLogFile.new(log_file, :roll => :weekly, :template => ":message", :min_roll_check => 0)
logger = Lumberjack::Logger.new(device, :buffer_size => 2)
Timecop.freeze(now) do
logger.error("test week one")
logger.flush
end
Timecop.freeze(now + (7 * one_day)) do
logger.error("test week two")
logger.close
end
File.read("#{log_file}.#{now.to_date.strftime('week-of-%Y-%m-%d')}").should == "test week one#{Lumberjack::LINE_SEPARATOR}"
File.read(log_file).should == "test week two#{Lumberjack::LINE_SEPARATOR}"
end
it "should roll the file monthly" do
now = Time.now
log_file = File.join(tmp_dir, "c#{rand(1000000000)}.log")
device = Lumberjack::Device::DateRollingLogFile.new(log_file, :roll => :monthly, :template => ":message", :min_roll_check => 0)
logger = Lumberjack::Logger.new(device, :buffer_size => 2)
Timecop.freeze(now) do
logger.error("test month one")
logger.flush
end
Timecop.freeze(now + (31 * one_day)) do
logger.error("test month two")
logger.close
end
File.read("#{log_file}.#{now.to_date.strftime('%Y-%m')}").should == "test month one#{Lumberjack::LINE_SEPARATOR}"
File.read(log_file).should == "test month two#{Lumberjack::LINE_SEPARATOR}"
end
end
lumberjack-1.0.13/spec/device/size_rolling_log_file_spec.rb 0000644 0000041 0000041 00000004036 13260145344 024040 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Device::SizeRollingLogFile do
before :all do
create_tmp_dir
end
after :all do
delete_tmp_dir
end
before :each do
delete_tmp_files
end
it "should roll a file when it gets to a specified size" do
log_file = File.join(tmp_dir, "a#{rand(1000000000)}.log")
device = Lumberjack::Device::SizeRollingLogFile.new(log_file, :max_size => 40, :template => ":message", :min_roll_check => 0)
logger = Lumberjack::Logger.new(device, :buffer_size => 2)
4.times do |i|
logger.error("test message #{i + 1}")
logger.flush
end
logger.close
File.read("#{log_file}.1").split(Lumberjack::LINE_SEPARATOR).should == ["test message 1", "test message 2", "test message 3"]
File.read(log_file).should == "test message 4#{Lumberjack::LINE_SEPARATOR}"
end
it "should be able to specify the max size in kilobytes" do
log_file = File.join(tmp_dir, "b#{rand(1000000000)}.log")
device = Lumberjack::Device::SizeRollingLogFile.new(log_file, :max_size => "32K", :min_roll_check => 0)
device.max_size.should == 32768
end
it "should be able to specify the max size in megabytes" do
log_file = File.join(tmp_dir, "c#{rand(1000000000)}.log")
device = Lumberjack::Device::SizeRollingLogFile.new(log_file, :max_size => "100M", :min_roll_check => 0)
device.max_size.should == 104_857_600
end
it "should be able to specify the max size in gigabytes" do
log_file = File.join(tmp_dir, "d#{rand(1000000000)}.log")
device = Lumberjack::Device::SizeRollingLogFile.new(log_file, :max_size => "1G", :min_roll_check => 0)
device.max_size.should == 1_073_741_824
end
it "should figure out the next archive file name available" do
log_file = File.join(tmp_dir, "filename.log")
(3..11).each do |i|
File.open("#{log_file}.#{i}", 'w'){|f| f.write(i.to_s)}
end
device = Lumberjack::Device::SizeRollingLogFile.new(log_file, :max_size => "100M", :min_roll_check => 0)
device.archive_file_suffix.should == "12"
end
end
lumberjack-1.0.13/spec/device/log_file_spec.rb 0000644 0000041 0000041 00000002403 13260145344 021254 0 ustar www-data www-data # -*- coding: utf-8 -*-
require 'spec_helper'
describe Lumberjack::Device::LogFile do
before :all do
create_tmp_dir
end
after :all do
delete_tmp_dir
end
before :each do
delete_tmp_files
end
it "should append to a file" do
log_file = File.join(tmp_dir, "a#{rand(1000000000)}.log")
File.open(log_file, 'w') do |f|
f.puts("Existing contents")
end
device = Lumberjack::Device::LogFile.new(log_file, :template => ":message")
device.write(Lumberjack::LogEntry.new(Time.now, 1, "New log entry", nil, $$, nil))
device.close
File.read(log_file).should == "Existing contents\nNew log entry#{Lumberjack::LINE_SEPARATOR}"
end
it "properly handles messages with broken UTF-8 characters" do
log_file = File.join(tmp_dir, "test_6.log")
device = Lumberjack::Device::LogFile.new(log_file,
:keep => 2, :buffer_size => 32767)
message = [0xC4, 0x90, 0xE1, 0xBB].pack("c*").force_encoding "ASCII-8BIT"
entry = Lumberjack::LogEntry.new(Time.now, 1, message, nil, $$, nil)
device.write entry
message = "проверка"
entry = Lumberjack::LogEntry.new(Time.now, 1, message, nil, $$, nil)
device.write entry
expect do
device.flush
end.to_not raise_error
end
end
lumberjack-1.0.13/spec/device/null_spec.rb 0000644 0000041 0000041 00000000424 13260145344 020447 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Device::Null do
it "should not generate any output" do
device = Lumberjack::Device::Null.new
device.write(Lumberjack::LogEntry.new(Time.now, 1, "New log entry", nil, $$, nil))
device.flush
device.close
end
end
lumberjack-1.0.13/spec/formatter_spec.rb 0000644 0000041 0000041 00000002660 13260145344 020245 0 ustar www-data www-data require 'spec_helper'
describe Lumberjack::Formatter do
let(:formatter){ Lumberjack::Formatter.new }
it "should have a default set of formatters" do
formatter.format("abc").should == "abc"
formatter.format([1, 2, 3]).should == "[1, 2, 3]"
formatter.format(ArgumentError.new("boom")).should == "ArgumentError: boom"
end
it "should be able to add a formatter object for a class" do
formatter.add(Numeric, lambda{|obj| "number: #{obj}"})
formatter.format(10).should == "number: 10"
end
it "should be able to add a formatter block for a class" do
formatter.add(Numeric){|obj| "number: #{obj}"}
formatter.format(10).should == "number: 10"
end
it "should be able to remove a formatter for a class" do
formatter.remove(String)
formatter.format("abc").should == "\"abc\""
end
it "should be able to chain add and remove calls" do
formatter.remove(String).should == formatter
formatter.add(String, Lumberjack::Formatter::StringFormatter.new).should == formatter
end
it "should format an object based on the class hierarchy" do
formatter.add(Numeric){|obj| "number: #{obj}"}
formatter.add(Integer){|obj| "fixed number: #{obj}"}
formatter.format(10).should == "fixed number: 10"
formatter.format(10.1).should == "number: 10.1"
end
it "should have a default formatter" do
formatter.remove(Object)
formatter.format(:test).should == ":test"
end
end
lumberjack-1.0.13/lib/ 0000755 0000041 0000041 00000000000 13260145344 014513 5 ustar www-data www-data lumberjack-1.0.13/lib/lumberjack.rb 0000644 0000041 0000041 00000003241 13260145344 017157 0 ustar www-data www-data # frozen_string_literals: true
require 'rbconfig'
require 'time'
require 'thread'
require 'securerandom'
module Lumberjack
LINE_SEPARATOR = (RbConfig::CONFIG['host_os'].match(/mswin/i) ? "\r\n" : "\n")
require File.expand_path("../lumberjack/severity.rb", __FILE__)
require File.expand_path("../lumberjack/log_entry.rb", __FILE__)
require File.expand_path("../lumberjack/formatter.rb", __FILE__)
require File.expand_path("../lumberjack/device.rb", __FILE__)
require File.expand_path("../lumberjack/logger.rb", __FILE__)
require File.expand_path("../lumberjack/template.rb", __FILE__)
require File.expand_path("../lumberjack/rack.rb", __FILE__)
class << self
# Define a unit of work within a block. Within the block supplied to this
# method, calling +unit_of_work_id+ will return the same value that can
# This can then be used for tying together log entries.
#
# You can specify the id for the unit of work if desired. If you don't supply
# it, a 12 digit hexidecimal number will be automatically generated for you.
#
# For the common use case of treating a single web request as a unit of work, see the
# Lumberjack::Rack::UnitOfWork class.
def unit_of_work(id = nil)
save_val = Thread.current[:lumberjack_logger_unit_of_work_id]
id ||= SecureRandom.hex(6)
Thread.current[:lumberjack_logger_unit_of_work_id] = id
begin
return yield
ensure
Thread.current[:lumberjack_logger_unit_of_work_id] = save_val
end
end
# Get the UniqueIdentifier for the current unit of work.
def unit_of_work_id
Thread.current[:lumberjack_logger_unit_of_work_id]
end
end
end
lumberjack-1.0.13/lib/lumberjack/ 0000755 0000041 0000041 00000000000 13260145344 016632 5 ustar www-data www-data lumberjack-1.0.13/lib/lumberjack/formatter.rb 0000644 0000041 0000041 00000006023 13260145344 021163 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
# This class controls the conversion of log entry messages into strings. This allows you
# to log any object you want and have the logging system worry about converting it into a string.
#
# Formats are added to a Formatter by associating them with a class using the +add+ method. Formats
# are any object that responds to the +call+ method.
#
# By default, all object will be converted to strings using their inspect method except for Strings
# and Exceptions. Strings are not converted and Exceptions are converted using the ExceptionFormatter.
class Formatter
require File.expand_path("../formatter/exception_formatter.rb", __FILE__)
require File.expand_path("../formatter/inspect_formatter.rb", __FILE__)
require File.expand_path("../formatter/pretty_print_formatter.rb", __FILE__)
require File.expand_path("../formatter/string_formatter.rb", __FILE__)
def initialize
@class_formatters = {}
@_default_formatter = InspectFormatter.new
add(Object, @_default_formatter)
add(String, :string)
add(Exception, :exception)
end
# Add a formatter for a class. The formatter can be specified as either an object
# that responds to the +call+ method or as a symbol representing one of the predefined
# formatters, or as a block to the method call.
#
# The predefined formatters are: :inspect, :string, :exception, and :pretty_print.
#
# === Examples
#
# # Use a predefined formatter
# formatter.add(MyClass, :pretty_print)
#
# # Pass in a formatter object
# formatter.add(MyClass, Lumberjack::Formatter::PrettyPrintFormatter.new)
#
# # Use a block
# formatter.add(MyClass){|obj| obj.humanize}
#
# # Add statements can be chained together
# formatter.add(MyClass, :pretty_print).add(YourClass){|obj| obj.humanize}
def add(klass, formatter = nil, &block)
formatter ||= block
if formatter.is_a?(Symbol)
formatter_class_name = "#{formatter.to_s.gsub(/(^|_)([a-z])/){|m| $~[2].upcase}}Formatter"
formatter = Formatter.const_get(formatter_class_name).new
end
@class_formatters[klass] = formatter
self
end
# Remove the formatter associated with a class. Remove statements can be chained together.
def remove(klass)
@class_formatters.delete(klass)
self
end
# Format a message object as a string.
def format(message)
formatter_for(message.class).call(message)
end
# Hack for compatibility with Logger::Formatter
def call(severity, timestamp, progname, msg)
"#{format(msg)}\n"
end
private
# Find the formatter for a class by looking it up using the class hierarchy.
def formatter_for(klass) #:nodoc:
while klass != nil do
formatter = @class_formatters[klass]
return formatter if formatter
klass = klass.superclass
end
@_default_formatter
end
end
end
lumberjack-1.0.13/lib/lumberjack/rack.rb 0000644 0000041 0000041 00000000314 13260145344 020075 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
module Rack
require File.expand_path("../rack/unit_of_work.rb", __FILE__)
require File.expand_path("../rack/request_id.rb", __FILE__)
end
end
lumberjack-1.0.13/lib/lumberjack/device.rb 0000644 0000041 0000041 00000001741 13260145344 020421 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
# This is an abstract class for logging devices. Subclasses must implement the +write+ method and
# may implement the +close+ and +flush+ methods if applicable.
class Device
require File.expand_path("../device/writer.rb", __FILE__)
require File.expand_path("../device/log_file.rb", __FILE__)
require File.expand_path("../device/rolling_log_file.rb", __FILE__)
require File.expand_path("../device/date_rolling_log_file.rb", __FILE__)
require File.expand_path("../device/size_rolling_log_file.rb", __FILE__)
require File.expand_path("../device/null.rb", __FILE__)
# Subclasses must implement this method to write a LogEntry.
def write(entry)
raise NotImplementedError
end
# Subclasses may implement this method to close the device.
def close
flush
end
# Subclasses may implement this method to flush any buffers used by the device.
def flush
end
end
end
lumberjack-1.0.13/lib/lumberjack/formatter/ 0000755 0000041 0000041 00000000000 13260145344 020635 5 ustar www-data www-data lumberjack-1.0.13/lib/lumberjack/formatter/string_formatter.rb 0000644 0000041 0000041 00000000317 13260145344 024554 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
class Formatter
# Format an object by calling +to_s+ on it.
class StringFormatter
def call(obj)
obj.to_s
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/formatter/exception_formatter.rb 0000644 0000041 0000041 00000000646 13260145344 025251 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
class Formatter
# Format an exception including the backtrace.
class ExceptionFormatter
def call(exception)
message = "#{exception.class.name}: #{exception.message}"
message << "#{Lumberjack::LINE_SEPARATOR} #{exception.backtrace.join("#{Lumberjack::LINE_SEPARATOR} ")}" if exception.backtrace
message
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/formatter/pretty_print_formatter.rb 0000644 0000041 0000041 00000001053 13260145344 026007 0 ustar www-data www-data # frozen_string_literals: true
require 'pp'
require 'stringio'
module Lumberjack
class Formatter
# Format an object with it's pretty print method.
class PrettyPrintFormatter
attr_accessor :width
# Create a new formatter. The maximum width of the message can be specified with the width
# parameter (defaults to 79 characters).
def initialize(width = 79)
@width = width
end
def call(obj)
s = StringIO.new
PP.pp(obj, s)
s.string.chomp
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/formatter/inspect_formatter.rb 0000644 0000041 0000041 00000000326 13260145344 024713 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
class Formatter
# Format an object by calling +inspect+ on it.
class InspectFormatter
def call(obj)
obj.inspect
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/logger.rb 0000644 0000041 0000041 00000024617 13260145344 020450 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
# Logger is a thread safe logging object. It has a compatible API with the Ruby
# standard library Logger class, the Log4r gem, and ActiveSupport::BufferedLogger.
#
# === Example
#
# logger = Lumberjack::Logger.new
# logger.info("Starting processing")
# logger.debug("Processing options #{options.inspect}")
# logger.fatal("OMG the application is on fire!")
#
# Log entries are written to a logging Device if their severity meets or exceeds the log level.
#
# Devices may use buffers internally and the log entries are not guaranteed to be written until you call
# the +flush+ method. Sometimes this can result in problems when trying to track down extraordinarily
# long running sections of code since it is likely that none of the messages logged before the long
# running code will appear in the log until the entire process finishes. You can set the +:flush_seconds+
# option on the constructor to force the device to be flushed periodically. This will create a new
# monitoring thread, but its use is highly recommended.
#
# Each log entry records the log message and severity along with the time it was logged, the
# program name, process id, and unit of work id. The message will be converted to a string, but
# otherwise, it is up to the device how these values are recorded. Messages are converted to strings
# using a Formatter associated with the logger.
class Logger
include Severity
# The time that the device was last flushed.
attr_reader :last_flushed_at
# The name of the program associated with log messages.
attr_writer :progname
# The device being written to.
attr_reader :device
# Set +silencer+ to false to disable silencing the log.
attr_accessor :silencer
# Create a new logger to log to a Device.
#
# The +device+ argument can be in any one of several formats.
#
# If it is a Device object, that object will be used.
# If it has a +write+ method, it will be wrapped in a Device::Writer class.
# If it is :null, it will be a Null device that won't record any output.
# Otherwise, it will be assumed to be file path and wrapped in a Device::LogFile class.
#
# This method can take the following options:
#
# * :level - The logging level below which messages will be ignored.
# * :progname - The name of the program that will be recorded with each log entry.
# * :flush_seconds - The maximum number of seconds between flush calls.
# * :roll - If the log device is a file path, it will be a Device::DateRollingLogFile if this is set.
# * :max_size - If the log device is a file path, it will be a Device::SizeRollingLogFile if this is set.
#
# All other options are passed to the device constuctor.
def initialize(device = STDOUT, options = {})
@thread_settings = {}
options = options.dup
self.level = options.delete(:level) || INFO
self.progname = options.delete(:progname)
max_flush_seconds = options.delete(:flush_seconds).to_f
@device = open_device(device, options)
@_formatter = Formatter.new
@last_flushed_at = Time.now
@silencer = true
create_flusher_thread(max_flush_seconds) if max_flush_seconds > 0
end
# Get the Formatter object used to convert messages into strings.
def formatter
@_formatter
end
# Get the level of severity of entries that are logged. Entries with a lower
# severity level will be ignored.
def level
thread_local_value(:lumberjack_logger_level) || @level
end
# Add a message to the log with a given severity. The message can be either
# passed in the +message+ argument or supplied with a block. This method
# is not normally called. Instead call one of the helper functions
# +fatal+, +error+, +warn+, +info+, or +debug+.
#
# The severity can be passed in either as one of the Severity constants,
# or as a Severity label.
#
# === Example
#
# logger.add(Lumberjack::Severity::ERROR, exception)
# logger.add(Lumberjack::Severity::INFO, "Request completed")
# logger.add(:warn, "Request took a long time")
# logger.add(Lumberjack::Severity::DEBUG){"Start processing with options #{options.inspect}"}
def add(severity, message = nil, progname = nil)
severity = Severity.label_to_level(severity) if severity.is_a?(String) || severity.is_a?(Symbol)
return unless severity && severity >= level
time = Time.now
if message.nil?
if block_given?
message = yield
else
message = progname
progname = nil
end
end
message = @_formatter.format(message)
progname ||= self.progname
entry = LogEntry.new(time, severity, message, progname, $$, Lumberjack.unit_of_work_id)
begin
device.write(entry)
rescue => e
$stderr.puts("#{e.class.name}: #{e.message}#{' at ' + e.backtrace.first if e.backtrace}")
$stderr.puts(entry.to_s)
end
nil
end
alias_method :log, :add
# Flush the logging device. Messages are not guaranteed to be written until this method is called.
def flush
device.flush
@last_flushed_at = Time.now
nil
end
# Close the logging device.
def close
flush
@device.close if @device.respond_to?(:close)
end
# Log a +FATAL+ message. The message can be passed in either the +message+ argument or in a block.
def fatal(message = nil, progname = nil, &block)
add(FATAL, message, progname, &block)
end
# Return +true+ if +FATAL+ messages are being logged.
def fatal?
level <= FATAL
end
# Log an +ERROR+ message. The message can be passed in either the +message+ argument or in a block.
def error(message = nil, progname = nil, &block)
add(ERROR, message, progname, &block)
end
# Return +true+ if +ERROR+ messages are being logged.
def error?
level <= ERROR
end
# Log a +WARN+ message. The message can be passed in either the +message+ argument or in a block.
def warn(message = nil, progname = nil, &block)
add(WARN, message, progname, &block)
end
# Return +true+ if +WARN+ messages are being logged.
def warn?
level <= WARN
end
# Log an +INFO+ message. The message can be passed in either the +message+ argument or in a block.
def info(message = nil, progname = nil, &block)
add(INFO, message, progname, &block)
end
# Return +true+ if +INFO+ messages are being logged.
def info?
level <= INFO
end
# Log a +DEBUG+ message. The message can be passed in either the +message+ argument or in a block.
def debug(message = nil, progname = nil, &block)
add(DEBUG, message, progname, &block)
end
# Return +true+ if +DEBUG+ messages are being logged.
def debug?
level <= DEBUG
end
# Log a message when the severity is not known. Unknown messages will always appear in the log.
# The message can be passed in either the +message+ argument or in a block.
def unknown(message = nil, progname = nil, &block)
add(UNKNOWN, message, progname, &block)
end
alias_method :<<, :unknown
# Set the minimum level of severity of messages to log.
def level=(severity)
if severity.is_a?(Integer)
@level = severity
else
@level = Severity.label_to_level(severity)
end
end
# Silence the logger by setting a new log level inside a block. By default, only +ERROR+ or +FATAL+
# messages will be logged.
#
# === Example
#
# logger.level = Lumberjack::Severity::INFO
# logger.silence do
# do_something # Log level inside the block is +ERROR+
# end
def silence(temporary_level = ERROR, &block)
if silencer
push_thread_local_value(:lumberjack_logger_level, temporary_level, &block)
else
yield
end
end
# Set the program name that is associated with log messages. If a block
# is given, the program name will be valid only within the block.
def set_progname(value, &block)
if block
push_thread_local_value(:lumberjack_logger_progname, value, &block)
else
self.progname = value
end
end
# Get the program name associated with log messages.
def progname
thread_local_value(:lumberjack_logger_progname) || @progname
end
private
# Set a local value for a thread tied to this object.
def set_thread_local_value(name, value) #:nodoc:
values = Thread.current[name]
unless values
values = {}
Thread.current[name] = values
end
if value.nil?
values.delete(self)
Thread.current[name] = nil if values.empty?
else
values[self] = value
end
end
# Get a local value for a thread tied to this object.
def thread_local_value(name) #:nodoc:
values = Thread.current[name]
values[self] if values
end
# Set a local value for a thread tied to this object within a block.
def push_thread_local_value(name, value) #:nodoc:
save_val = thread_local_value(name)
set_thread_local_value(name, value)
begin
yield
ensure
set_thread_local_value(name, save_val)
end
end
# Open a logging device.
def open_device(device, options) #:nodoc:
if device.is_a?(Device)
device
elsif device.respond_to?(:write) && device.respond_to?(:flush)
Device::Writer.new(device, options)
elsif device == :null
Device::Null.new
else
device = device.to_s
if options[:roll]
Device::DateRollingLogFile.new(device, options)
elsif options[:max_size]
Device::SizeRollingLogFile.new(device, options)
else
Device::LogFile.new(device, options)
end
end
end
# Create a thread that will periodically call flush.
def create_flusher_thread(flush_seconds) #:nodoc:
if flush_seconds > 0
begin
logger = self
Thread.new do
loop do
begin
sleep(flush_seconds)
logger.flush if Time.now - logger.last_flushed_at >= flush_seconds
rescue => e
STDERR.puts("Error flushing log: #{e.inspect}")
end
end
end
end
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/severity.rb 0000644 0000041 0000041 00000001024 13260145344 021026 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
# The standard severity levels for logging messages.
module Severity
UNKNOWN = 5
FATAL = 4
ERROR = 3
WARN = 2
INFO = 1
DEBUG = 0
SEVERITY_LABELS = %w(DEBUG INFO WARN ERROR FATAL UNKNOWN).freeze
class << self
def level_to_label(severity)
SEVERITY_LABELS[severity] || SEVERITY_LABELS.last
end
def label_to_level(label)
SEVERITY_LABELS.index(label.to_s.upcase) || UNKNOWN
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/log_entry.rb 0000644 0000041 0000041 00000002040 13260145344 021155 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
# An entry in a log is a data structure that captures the log message as well as
# information about the system that logged the message.
class LogEntry
attr_accessor :time, :message, :severity, :progname, :pid, :unit_of_work_id
TIME_FORMAT = "%Y-%m-%dT%H:%M:%S".freeze
def initialize(time, severity, message, progname, pid, unit_of_work_id)
@time = time
@severity = (severity.is_a?(Integer) ? severity : Severity.label_to_level(severity))
@message = message
@progname = progname
@pid = pid
@unit_of_work_id = unit_of_work_id
end
def severity_label
Severity.level_to_label(severity)
end
def to_s
buf = "[#{time.strftime(TIME_FORMAT)}.#{(time.usec / 1000.0).round.to_s.rjust(3, '0')} #{severity_label} #{progname}(#{pid})"
if unit_of_work_id
buf << " #"
buf << unit_of_work_id
end
buf << "] "
buf << message
end
def inspect
to_s
end
end
end
lumberjack-1.0.13/lib/lumberjack/rack/ 0000755 0000041 0000041 00000000000 13260145344 017552 5 ustar www-data www-data lumberjack-1.0.13/lib/lumberjack/rack/request_id.rb 0000644 0000041 0000041 00000001353 13260145344 022245 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
module Rack
# Support for using the Rails ActionDispatch request id in the log.
# The format is expected to be a random UUID and only the first chunk is used for terseness
# if the abbreviated argument is true.
class RequestId
REQUEST_ID = "action_dispatch.request_id".freeze
def initialize(app, abbreviated = false)
@app = app
@abbreviated = abbreviated
end
def call(env)
request_id = env[REQUEST_ID]
if request_id && @abbreviated
request_id = request_id.split('-'.freeze, 2).first
end
Lumberjack.unit_of_work(request_id) do
@app.call(env)
end
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/rack/unit_of_work.rb 0000644 0000041 0000041 00000000413 13260145344 022602 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
module Rack
class UnitOfWork
def initialize(app)
@app = app
end
def call(env)
Lumberjack.unit_of_work do
@app.call(env)
end
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/device/ 0000755 0000041 0000041 00000000000 13260145344 020071 5 ustar www-data www-data lumberjack-1.0.13/lib/lumberjack/device/log_file.rb 0000644 0000041 0000041 00000001177 13260145344 022204 0 ustar www-data www-data # frozen_string_literals: true
require 'fileutils'
module Lumberjack
class Device
# This is a logging device that appends log entries to a file.
class LogFile < Writer
EXTERNAL_ENCODING = "ascii-8bit"
# The absolute path of the file being logged to.
attr_reader :path
# Create a logger to the file at +path+. Options are passed through to the Writer constructor.
def initialize(path, options = {})
@path = File.expand_path(path)
FileUtils.mkdir_p(File.dirname(@path))
super(File.new(@path, 'a', :encoding => EXTERNAL_ENCODING), options)
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/device/size_rolling_log_file.rb 0000644 0000041 0000041 00000003720 13260145344 024760 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
class Device
# This is a log device that appends entries to a file and rolls the file when it reaches a specified
# size threshold. When a file is rolled, it will have an number extension appended to the file name.
# For example, if the log file is named production.log, the first time it is rolled it will be renamed
# production.log.1, then production.log.2, etc.
class SizeRollingLogFile < RollingLogFile
attr_reader :max_size
# Create an new log device to the specified file. The maximum size of the log file is specified with
# the :max_size option. The unit can also be specified: "32K", "100M", "2G" are all valid.
def initialize(path, options = {})
@manual = options[:manual]
@max_size = options[:max_size]
if @max_size.is_a?(String)
if @max_size.match(/^(\d+(\.\d+)?)([KMG])?$/i)
@max_size = $~[1].to_f
units = $~[3].to_s.upcase
case units
when "K"
@max_size *= 1024
when "M"
@max_size *= 1024 ** 2
when "G"
@max_size *= 1024 ** 3
end
@max_size = @max_size.round
else
raise ArgumentError.new("illegal value for :max_size (#{@max_size})")
end
end
super
end
def archive_file_suffix
next_archive_number.to_s
end
def roll_file?
@manual || stream.stat.size > @max_size
rescue SystemCallError
false
end
protected
# Calculate the next archive file name extension.
def next_archive_number # :nodoc:
max = 0
Dir.glob("#{path}.*").each do |filename|
if filename.match(/\.\d+$/)
suffix = filename.split('.').last.to_i
max = suffix if suffix > max
end
end
max + 1
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/device/writer.rb 0000644 0000041 0000041 00000012020 13260145344 021725 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
class Device
# This logging device writes log entries as strings to an IO stream. By default, messages will be buffered
# and written to the stream in a batch when the buffer is full or when +flush+ is called.
class Writer < Device
DEFAULT_FIRST_LINE_TEMPLATE = "[:time :severity :progname(:pid) #:unit_of_work_id] :message".freeze
DEFAULT_ADDITIONAL_LINES_TEMPLATE = "#{Lumberjack::LINE_SEPARATOR}> [#:unit_of_work_id] :message".freeze
# The size of the internal buffer. Defaults to 32K.
attr_reader :buffer_size
# Internal buffer to batch writes to the stream.
class Buffer # :nodoc:
attr_reader :size
def initialize
@values = []
@size = 0
end
def <<(string)
@values << string
@size += string.size
end
def empty?
@values.empty?
end
def pop!
popped = @values
clear
popped
end
def clear
@values = []
@size = 0
end
end
# Create a new device to write log entries to a stream. Entries are converted to strings
# using a Template. The template can be specified using the :template option. This can
# either be a Proc or a string that will compile into a Template object.
#
# If the template is a Proc, it should accept an LogEntry as its only argument and output a string.
#
# If the template is a template string, it will be used to create a Template. The
# :additional_lines and :time_format options will be passed through to the
# Template constuctor.
#
# The default template is "[:time :severity :progname(:pid) #:unit_of_work_id] :message"
# with additional lines formatted as "\n [#:unit_of_work_id] :message". The unit of
# work id will only appear if it is present.
#
# The size of the internal buffer in bytes can be set by providing :buffer_size (defaults to 32K).
def initialize(stream, options = {})
@lock = Mutex.new
@stream = stream
@stream.sync = true if @stream.respond_to?(:sync=)
@buffer = Buffer.new
@buffer_size = (options[:buffer_size] || 0)
template = (options[:template] || DEFAULT_FIRST_LINE_TEMPLATE)
if template.respond_to?(:call)
@template = template
else
additional_lines = (options[:additional_lines] || DEFAULT_ADDITIONAL_LINES_TEMPLATE)
@template = Template.new(template, :additional_lines => additional_lines, :time_format => options[:time_format])
end
end
# Set the buffer size in bytes. The device will only be physically written to when the buffer size
# is exceeded.
def buffer_size=(value)
@buffer_size = value
flush
end
# Write an entry to the stream. The entry will be converted into a string using the defined template.
def write(entry)
string = @template.call(entry)
unless string.nil?
string = string.encode("UTF-8".freeze, invalid: :replace, undef: :replace)
@lock.synchronize do
@buffer << string
end
end
flush if @buffer.size >= buffer_size
end
# Close the underlying stream.
def close
flush
stream.close
end
# Flush the underlying stream.
def flush
lines = nil
@lock.synchronize do
before_flush
lines = @buffer.pop!
end
unless lines.empty?
out = "#{lines.join(Lumberjack::LINE_SEPARATOR)}#{Lumberjack::LINE_SEPARATOR}"
begin
begin
stream.write(out)
rescue IOError => e
# This condition can happen if another thread closed the stream in the `before_flush` call.
# Synchronizing will handle the race condition, but since it's an exceptional case we don't
# to lock the thread on every stream write call.
@lock.synchronize do
if stream.closed?
raise e
else
stream.write(out)
end
end
end
stream.flush rescue nil
rescue => e
$stderr.write("#{e.class.name}: #{e.message}#{' at ' + e.backtrace.first if e.backtrace}")
$stderr.write(out)
$stderr.flush
end
end
end
protected
# Callback method that will be executed before data is written to the stream. Subclasses
# can override this method if needed. This method will be called in a mutex lock.
def before_flush
end
# Set the underlying stream.
def stream=(stream)
@stream = stream
end
# Get the underlying stream.
def stream
@stream
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/device/rolling_log_file.rb 0000644 0000041 0000041 00000010256 13260145344 023730 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
class Device
# This is an abstract class for a device that appends entries to a file and periodically archives
# the existing file and starts a one. Subclasses must implement the roll_file? and archive_file_suffix
# methods.
#
# The :keep option can be used to specify a maximum number of rolled log files to keep.
# Older files will be deleted based on the time they were created. The default is to keep all files.
#
# The :min_roll_check option can be used to specify the number of seconds between checking
# the file to determine if it needs to be rolled. The default is to check at most once per second.
class RollingLogFile < LogFile
attr_reader :path
attr_accessor :keep
def initialize(path, options = {})
@path = File.expand_path(path)
@keep = options[:keep]
super(path, options)
@file_inode = stream.lstat.ino rescue nil
@@rolls = []
@next_stat_check = Time.now.to_f
@min_roll_check = (options[:min_roll_check] || 1.0).to_f
end
# Returns a suffix that will be appended to the file name when it is archived.. The suffix should
# change after it is time to roll the file. The log file will be renamed when it is rolled.
def archive_file_suffix
raise NotImplementedError
end
# Return +true+ if the file should be rolled.
def roll_file?
raise NotImplementedError
end
# Roll the log file by renaming it to the archive file name and then re-opening a stream to the log
# file path. Rolling a file is safe in multi-threaded or multi-process environments.
def roll_file! #:nodoc:
do_once(stream) do
archive_file = "#{path}.#{archive_file_suffix}"
stream.flush
current_inode = File.stat(path).ino rescue nil
if @file_inode && current_inode == @file_inode && !File.exist?(archive_file) && File.exist?(path)
begin
File.rename(path, archive_file)
after_roll
cleanup_files!
rescue SystemCallError
# Ignore rename errors since it indicates the file was already rolled
end
end
reopen_file
end
rescue => e
$stderr.write("Failed to roll file #{path}: #{e.inspect}\n#{e.backtrace.join("\n")}\n")
end
protected
# This method will be called after a file has been rolled. Subclasses can
# implement code to reset the state of the device. This method is thread safe.
def after_roll
end
# Handle rolling the file before flushing.
def before_flush # :nodoc:
if @min_roll_check <= 0.0 || Time.now.to_f >= @next_stat_check
@next_stat_check += @min_roll_check
path_inode = File.lstat(path).ino rescue nil
if path_inode != @file_inode
@file_inode = path_inode
reopen_file
else
roll_file! if roll_file?
end
end
end
private
def reopen_file
old_stream = stream
new_stream = File.open(path, 'a', encoding: EXTERNAL_ENCODING)
new_stream.sync = true if buffer_size > 0
@file_inode = new_stream.lstat.ino rescue nil
self.stream = new_stream
old_stream.close
end
end
def cleanup_files!
if keep
files = Dir.glob("#{path}.*").collect{|f| [f, File.ctime(f)]}.sort{|a,b| b.last <=> a.last}.collect{|a| a.first}
if files.size > keep
files[keep, files.length].each do |f|
File.delete(f)
end
end
end
end
def do_once(file)
begin
file.flock(File::LOCK_EX)
rescue SystemCallError
# Most likely can't lock file because the stream is closed
return
end
begin
verify = file.lstat rescue nil
# Execute only if the file we locked is still the same one that needed to be rolled
yield if verify && verify.ino == @file_inode && verify.size > 0
ensure
file.flock(File::LOCK_UN) rescue nil
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/device/null.rb 0000644 0000041 0000041 00000000524 13260145344 021371 0 ustar www-data www-data # frozen_string_literals: true
require 'date'
module Lumberjack
class Device
# This is a logging device that produces no output. It can be useful in
# testing environments when log file output is not useful.
class Null < Device
def initialize(*args)
end
def write(entry)
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/device/date_rolling_log_file.rb 0000644 0000041 0000041 00000004133 13260145344 024722 0 ustar www-data www-data # frozen_string_literals: true
require 'date'
module Lumberjack
class Device
# This log device will append entries to a file and roll the file periodically by date. Files
# are rolled at midnight and can be rolled daily, weekly, or monthly. Archive file names will
# have the date appended to them in the format ".YYYY-MM-DD" for daily, ".week-of-YYYY-MM-DD" for weekly
# and ".YYYY-MM" for monthly. It is not guaranteed that log messages will break exactly on the
# roll period as buffered entries will always be written to the same file.
class DateRollingLogFile < RollingLogFile
# Create a new logging device to the specified file. The period to roll the file is specified
# with the :roll option which may contain a value of :daily, :weekly,
# or :monthly.
def initialize(path, options = {})
@manual = options[:manual]
@file_date = Date.today
if options[:roll] && options[:roll].to_s.match(/(daily)|(weekly)|(monthly)/i)
@roll_period = $~[0].downcase.to_sym
options.delete(:roll)
else
raise ArgumentError.new("illegal value for :roll (#{options[:roll].inspect})")
end
super
end
def archive_file_suffix
case @roll_period
when :weekly
"#{@file_date.strftime('week-of-%Y-%m-%d')}"
when :monthly
"#{@file_date.strftime('%Y-%m')}"
else
"#{@file_date.strftime('%Y-%m-%d')}"
end
end
def roll_file?
if @manual
true
else
date = Date.today
if date.year > @file_date.year
true
elsif @roll_period == :daily && date.yday > @file_date.yday
true
elsif @roll_period == :weekly && date.cweek != @file_date.cweek
true
elsif @roll_period == :monthly && date.month > @file_date.month
true
else
false
end
end
end
protected
def after_roll
@file_date = Date.today
end
end
end
end
lumberjack-1.0.13/lib/lumberjack/template.rb 0000644 0000041 0000041 00000006160 13260145344 020775 0 ustar www-data www-data # frozen_string_literals: true
module Lumberjack
# A template converts entries to strings. Templates can contain the following place holders to
# reference log entry values:
#
# * :time
# * :severity
# * :progname
# * :unit_of_work_id
# * :message
class Template
TEMPLATE_ARGUMENT_ORDER = %w(:time :severity :progname :pid :unit_of_work_id :message).freeze
DEFAULT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S."
MILLISECOND_FORMAT = "%03d"
MICROSECOND_FORMAT = "%06d"
# Create a new template from the markup. The +first_line+ argument is used to format only the first
# line of a message. Additional lines will be added to the message unformatted. If you wish to format
# the additional lines, use the :additional_lines options to specify a template. Note that you'll need
# to provide the line separator character in this template if you want to keep the message on multiple lines.
#
# The time will be formatted as YYYY-MM-DDTHH:MM:SSS.SSS by default. If you wish to change the format, you
# can specify the :time_format option which can be either a time format template as documented in
# +Time#strftime+ or the values +:milliseconds+ or +:microseconds+ to use the standard format with the
# specified precision.
#
# Messages will have white space stripped from both ends.
def initialize(first_line, options = {})
@first_line_template = compile(first_line)
additional_lines = options[:additional_lines] || "#{Lumberjack::LINE_SEPARATOR}:message"
@additional_line_template = compile(additional_lines)
# Formatting the time is relatively expensive, so only do it if it will be used
@template_include_time = first_line.include?(":time") || additional_lines.include?(":time")
@time_format = options[:time_format] || :milliseconds
end
# Convert an entry into a string using the template.
def call(entry)
lines = entry.message.strip.split(Lumberjack::LINE_SEPARATOR)
formatted_time = format_time(entry.time) if @template_include_time
message = @first_line_template % [formatted_time, entry.severity_label, entry.progname, entry.pid, entry.unit_of_work_id, lines.shift]
lines.each do |line|
message << @additional_line_template % [formatted_time, entry.severity_label, entry.progname, entry.pid, entry.unit_of_work_id, line]
end
message
end
private
def format_time(time) #:nodoc:
if @time_format.is_a?(String)
time.strftime(@time_format)
elsif @time_format == :milliseconds
time.strftime(DEFAULT_TIME_FORMAT) << MILLISECOND_FORMAT % (time.usec / 1000.0).round
else
time.strftime(DEFAULT_TIME_FORMAT) << MICROSECOND_FORMAT % time.usec
end
end
# Compile the template string into a value that can be used with sprintf.
def compile(template) #:nodoc:
template.gsub(/:[a-z0-9_]+/) do |match|
position = TEMPLATE_ARGUMENT_ORDER.index(match)
if position
"%#{position + 1}$s"
else
match
end
end
end
end
end
lumberjack-1.0.13/lumberjack.gemspec 0000644 0000041 0000041 00000005625 13260145344 017441 0 ustar www-data www-data #########################################################
# This file has been automatically generated by gem2tgz #
#########################################################
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = "lumberjack"
s.version = "1.0.13"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Brian Durand"]
s.date = "2018-03-29"
s.description = "A simple, powerful, and very fast logging utility that can be a drop in replacement for Logger or ActiveSupport::BufferedLogger. Provides support for automatically rolling log files even with multiple processes writing the same log file."
s.email = ["bbdurand@gmail.com"]
s.files = ["MIT_LICENSE", "README.md", "Rakefile", "VERSION", "lib/lumberjack.rb", "lib/lumberjack/device.rb", "lib/lumberjack/device/date_rolling_log_file.rb", "lib/lumberjack/device/log_file.rb", "lib/lumberjack/device/null.rb", "lib/lumberjack/device/rolling_log_file.rb", "lib/lumberjack/device/size_rolling_log_file.rb", "lib/lumberjack/device/writer.rb", "lib/lumberjack/formatter.rb", "lib/lumberjack/formatter/exception_formatter.rb", "lib/lumberjack/formatter/inspect_formatter.rb", "lib/lumberjack/formatter/pretty_print_formatter.rb", "lib/lumberjack/formatter/string_formatter.rb", "lib/lumberjack/log_entry.rb", "lib/lumberjack/logger.rb", "lib/lumberjack/rack.rb", "lib/lumberjack/rack/request_id.rb", "lib/lumberjack/rack/unit_of_work.rb", "lib/lumberjack/severity.rb", "lib/lumberjack/template.rb", "spec/device/date_rolling_log_file_spec.rb", "spec/device/log_file_spec.rb", "spec/device/null_spec.rb", "spec/device/rolling_log_file_spec.rb", "spec/device/size_rolling_log_file_spec.rb", "spec/device/writer_spec.rb", "spec/formatter/exception_formatter_spec.rb", "spec/formatter/inspect_formatter_spec.rb", "spec/formatter/pretty_print_formatter_spec.rb", "spec/formatter/string_formatter_spec.rb", "spec/formatter_spec.rb", "spec/log_entry_spec.rb", "spec/logger_spec.rb", "spec/lumberjack_spec.rb", "spec/rack/request_id_spec.rb", "spec/rack/unit_of_work_spec.rb", "spec/severity_spec.rb", "spec/spec_helper.rb", "spec/template_spec.rb"]
s.homepage = "https://github.com/bdurand/lumberjack"
s.licenses = ["MIT"]
s.require_paths = ["lib"]
s.rubygems_version = "1.8.23"
s.summary = "A simple, powerful, and very fast logging utility that can be a drop in replacement for Logger or ActiveSupport::BufferedLogger."
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q, ["~> 3.0"])
s.add_development_dependency(%q, ["~> 0.8"])
else
s.add_dependency(%q, ["~> 3.0"])
s.add_dependency(%q, ["~> 0.8"])
end
else
s.add_dependency(%q, ["~> 3.0"])
s.add_dependency(%q, ["~> 0.8"])
end
end
lumberjack-1.0.13/VERSION 0000644 0000041 0000041 00000000007 13260145344 015012 0 ustar www-data www-data 1.0.13
lumberjack-1.0.13/README.md 0000644 0000041 0000041 00000020366 13260145344 015233 0 ustar www-data www-data # Lumberjack
Lumberjack is a simple, powerful, and fast logging implementation in Ruby. It uses nearly the same API as the Logger class in the Ruby standard library and as ActiveSupport::BufferedLogger in Rails.
## Usage
This code aims to be extremely simple to use. The core interface it the Lumberjack::Logger which is used to log messages (which can be any object) with a specified Severity. Each logger has a level associated with it and messages are only written if their severity is greater than or equal to the level.
```ruby
logger = Lumberjack::Logger.new("logs/application.log") # Open a new log file with INFO level
logger.info("Begin request")
logger.debug(request.params) # Message not written unless the level is set to DEBUG
begin
# do something
rescue => exception
logger.error(exception)
raise
end
logger.info("End request")
```
This is all you need to know to log messages.
## Features
### Meta data
When messages are added to the log, additional data about the message is kept in a Lumberjack::LogEntry. This means you don't need to worry about adding the time or process id to your log messages as they will be automatically recorded.
The following information is recorded for each message:
* severity - The severity recorded for the message.
* time - The time at which the message was recorded.
* program name - The name of the program logging the message. This can be either set for all messages or customized with each message.
* process id - The process id (pid) of the process that logged the message.
* unit of work id - The unique 12 byte hexadecimal number generated for a unit of work.
### Units Of Work
A unit of work can be used to tie together all log messages within a block. This can be very useful to isolate a group of messages that represent one path through the system. For instance, in a web application, a single request represents a natural unit of work, and when you are looking through a log file, it is useful to see the entire set of log entries as a unit instead of interspersed with messages from other concurrent requests.
```ruby
# All log entries in this block will get a common unit of work id.
Lumberjack.unit_of_work do
logger.info("Begin request")
yield
logger.info("End request")
end
```
### Pluggable Devices
When a Logger logs a LogEntry, it sends it to a Lumberjack::Device. Lumberjack comes with a variety of devices for logging to IO streams or files.
* Lumberjack::Device::Writer - Writes log entries to an IO stream.
* Lumberjack::Device::LogFile - Writes log entries to a file.
* Lumberjack::Device::DateRollingLogFile - Writes log entries to a file that will automatically roll itself based on date.
* Lumberjack::Device::SizeRollingLogFile - Writes log entries to a file that will automatically roll itself based on size.
* Lumberjack::Device::Null - This device produces no output and is intended for testing environments.
If you'd like to send you log to a different kind of output, you just need to extend the Device class and implement the +write+ method. Or check out these plugins:
* [lumberjack_syslog_device](https://github.com/bdurand/lumberjack_syslog_device) - send your log messages to the system wide syslog service
* [lumberjack_multi-device](https://github.com/astevens/lumberjack_multi-device) - send log messages to multiple devices
* [lumberjack_mongo_device](https://github.com/bdurand/lumberjack_mongo_device) - store your log messages to a [MongoDB](http://www.mongodb.org/) NoSQL data store
* [lumberjack-couchdb-driver](https://github.com/narkisr/lumberjack-couchdb-driver) - store your log messages to a [CouchDB](http://couchdb.apache.org/) NoSQL data store
* [lumberjack_heroku_device](https://github.com/tonycoco/lumberjack_heroku_device) - log to Heroku's logging system
### Customize Formatting
When a message is logged, it is first converted into a string. You can customize how it is converted by adding mappings to a Formatter.
```ruby
logger.formatter.add(Hash, :pretty_print) # use the Formatter::PrettyPrintFormatter for all Hashes
logger.formatter.add(MyClass){|obj| "#{obj.class}@#{obj.id}"} # use a block to provide a custom format
```
If you use the built in devices, you can also customize the Template used to format the LogEntry.
```ruby
# Change the format of the time in the log
Lumberjack::Logger.new("application.log", :time_format => "%m/%d/%Y %H:%M:%S")
# Use a simple template that only includes the time and the message
Lumberjack::Logger.new("application.log", :template => ":time - :message")
# Use a custom template as a block that only includes the first character of the severity
template = lambda{|e| "#{e.severity_label[0, 1]} #{e.time} - #{e.message}"}
Lumberjack::Logger.new("application.log", :template => template)
```
### Buffered Performance
The logger has hooks for devices that support buffering to increase performance by batching physical writes. Log entries are not guaranteed to be written until the Lumberjack::Logger#flush method is called.
You can use the :flush_seconds option on the logger to periodically flush the log. This is usually a good idea so you can more easily debug hung processes. Without periodic flushing, a process that hangs may never write anything to the log because the messages are sitting in a buffer. By turning on periodic flushing, the logged messages will be written which can greatly aid in debugging the problem.
The built in stream based logging devices use an internal buffer. The size of the buffer (in bytes) can be set with the :buffer_size options when initializing a logger. The default behavior is to not to buffer.
```ruby
# Set buffer to flush after 8K has been written to the log.
logger = Lumberjack::Logger.new("application.log", :buffer_size => 8192)
# Turn off buffering so entries are immediately written to disk.
logger = Lumberjack::Logger.new("application.log", :buffer_size => 0)
```
### Automatic Log Rolling
The built in devices include two that can automatically roll log files based either on date or on file size. When a log file is rolled, it will be renamed with a suffix and a new file will be created to receive new log entries. This can keep your log files from growing to unusable sizes and removes the need to schedule an external process to roll the files.
There is a similar feature in the standard library Logger class, but the implementation here is safe to use with multiple processes writing to the same log file.
## Examples
These example are for Rails applications, but there is no dependency on Rails for using this gem. Most of the examples are applicable to any Ruby application.
In a Rails application you can replace the default production logger by adding this to your config/environments/production.rb file:
```ruby
# Use the ActionDispatch request id as the unit of work id. This will use just the first chunk of the request id.
# If you want to use an abbreviated request id for terseness, change the last argument to `true`
config.middleware.insert_after ActionDispatch::RequestId, Lumberjack::Rack::RequestId, false
# Use a custom unit of work id to each request
# config.middleware.insert(0, Lumberjack::Rack::UnitOfWork)
# Change the logger to use Lumberjack
log_file_path = Rails.root + "log" + "#{Rails.env}.log"
config.logger = Lumberjack::Logger.new(log_file, :level => :warn)
```
To set up a logger to roll every day at midnight, you could use this code (you can also specify :weekly or :monthly):
```ruby
config.logger = Lumberjack::Logger.new(log_file_path, :roll => :daily)
```
To set up a logger to roll log files when they get to 100Mb, you could use this:
```ruby
config.logger = Lumberjack::Logger.new(log_file_path, :max_size => 100.megabytes)
```
To change the log message format, you could use this code:
```ruby
config.logger = Lumberjack::Logger.new(log_file_path, :template => ":time - :message")
```
To change the log message format to output JSON, you could use this code (this example requires the multi-json gem):
```ruby
config.logger = Lumberjack::Logger.new(log_file_path, :template => lambda{|e| MultiJson.dump(e)})
```
To send log messages to syslog instead of to a file, you could use this (require the lumberjack_syslog_device gem):
```ruby
config.logger = Lumberjack::Logger.new(Lumberjack::SyslogDevice.new)
```