unilogger 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/README +49 -0
- data/Rakefile +2 -0
- data/lib/unilogger.rb +13 -0
- data/lib/unilogger/builder.rb +63 -0
- data/lib/unilogger/log_file_emitter.rb +41 -0
- data/lib/unilogger/logger.rb +92 -0
- data/lib/unilogger/mongo_emitter.rb +18 -0
- data/lib/unilogger/redis_emitter.rb +19 -0
- data/lib/unilogger/version.rb +3 -0
- data/test/logger.yml.erb +28 -0
- data/test/test.rb +14 -0
- data/unilogger.gemspec +22 -0
- metadata +91 -0
data/Gemfile
ADDED
data/README
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
Unified logger that writes to a log file.
|
2
|
+
Could eventually also write to a redis queue, mongo collection, and hoptoad.
|
3
|
+
|
4
|
+
Unilogger is composed at runtime of a Logger instance which delegates log messages to a set of emitters.
|
5
|
+
The emitters do the actual work of sending log messages to stderr, a file, redis, or elsewhere.
|
6
|
+
|
7
|
+
Unilogger will initialize itself from a logger.yml or logger.yml.erb configuration file, if present.
|
8
|
+
|
9
|
+
The configuration file should contain an entry named after the current environment, e.g.
|
10
|
+
"development", "test", "production", etc.
|
11
|
+
|
12
|
+
Logger options
|
13
|
+
--------------
|
14
|
+
level: overall level; messages of severity less than this will not be emitted regardless of the level of the emitter.
|
15
|
+
emitters: a list of emitters.
|
16
|
+
|
17
|
+
Standard (log file) emitter
|
18
|
+
---------------------------
|
19
|
+
kind: LogFileEmitter
|
20
|
+
logdev: "stderr", "stdout", or a filename
|
21
|
+
shift_age: "daily", "weekly" or "monthly" to rotate based on the calendar
|
22
|
+
if rotating based on file size, number of files to keep
|
23
|
+
default 7
|
24
|
+
shift_size: a file size, to rotate based on the file size
|
25
|
+
default 1048576
|
26
|
+
|
27
|
+
Redis emitter
|
28
|
+
-------------
|
29
|
+
kind: RedisEmitter
|
30
|
+
|
31
|
+
|
32
|
+
Example logger.yml
|
33
|
+
------------------
|
34
|
+
stderr: &stderr
|
35
|
+
logger:
|
36
|
+
logdev: stderr
|
37
|
+
|
38
|
+
standard: &standard
|
39
|
+
logger:
|
40
|
+
logdev: "<%= ENV["RACK_ENV"] || "development" %>.log"
|
41
|
+
shift_age: daily
|
42
|
+
|
43
|
+
development:
|
44
|
+
level: 0
|
45
|
+
emitters: [ *standard ]
|
46
|
+
|
47
|
+
test:
|
48
|
+
level: 1
|
49
|
+
emitters: [ *stderr ]
|
data/Rakefile
ADDED
data/lib/unilogger.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "unilogger/logger"
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module Unilogger
|
6
|
+
class Builder
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# options must include
|
11
|
+
# env => "development", "test", "production", etc.
|
12
|
+
# root => parent of config and log directories
|
13
|
+
def build( options )
|
14
|
+
env = options[:env] || ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
|
15
|
+
root = options[:root]
|
16
|
+
|
17
|
+
if File.exist?( yml = "#{root}/config/logger.yml" ) then
|
18
|
+
cfg = YAML.load(IO.read( yml )) [env]
|
19
|
+
elsif File.exist?( yml = "#{root}/config/logger.yml.erb" )
|
20
|
+
cfg = YAML.load( (ERB.new( IO.read( yml ) ).result) ) [env]
|
21
|
+
else
|
22
|
+
cfg = { "level" => "debug", "emitters" => [ { "logger" => { "logdev" => "stderr" } } ] }
|
23
|
+
end
|
24
|
+
|
25
|
+
Unilogger::Builder.new( cfg ).logger
|
26
|
+
end
|
27
|
+
|
28
|
+
def as_level( level, default_level = ::Logger::Severity::INFO )
|
29
|
+
return default_level if level.nil? || level.size == 0
|
30
|
+
return level if level.kind_of?(Integer)
|
31
|
+
::Logger::Severity.const_get( level.to_s.upcase )
|
32
|
+
end
|
33
|
+
|
34
|
+
def as_factory( kind )
|
35
|
+
kind = "LogFileEmitter" if kind =~ /^logger$/i
|
36
|
+
factory = Unilogger.const_get(kind) rescue Object.const_get(kind) rescue nil
|
37
|
+
raise "unknown kind of emitter (#{kind})" if ! factory
|
38
|
+
return factory
|
39
|
+
end
|
40
|
+
|
41
|
+
end # class
|
42
|
+
|
43
|
+
def initialize( configuration )
|
44
|
+
@configuration = configuration
|
45
|
+
end
|
46
|
+
|
47
|
+
def logger
|
48
|
+
# accept debug...fatal, 0...4, default info
|
49
|
+
level = self.class.as_level( @configuration["level"] )
|
50
|
+
|
51
|
+
# emitters
|
52
|
+
emitters = @configuration["emitters"].map do |options|
|
53
|
+
options = options.first
|
54
|
+
kind = options.first
|
55
|
+
factory = self.class.as_factory( kind )
|
56
|
+
factory.build( options.last )
|
57
|
+
end
|
58
|
+
|
59
|
+
Logger.new( level, emitters )
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module Unilogger
|
4
|
+
|
5
|
+
class LogFileEmitter
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def build( options )
|
9
|
+
logdev = options.delete("logdev")
|
10
|
+
case logdev
|
11
|
+
when nil, /stderr/i
|
12
|
+
new( STDERR, :logdev => "STDERR" )
|
13
|
+
when /stdout/i
|
14
|
+
new( STDOUT, :logdev => "STDOUT" )
|
15
|
+
else
|
16
|
+
new( logdev, options.merge( :logdev => logdev ) )
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# options includes a JSON representation of logdev
|
22
|
+
def initialize( logdev, options = {} )
|
23
|
+
@options = options
|
24
|
+
@logdev = ::Logger::LogDevice.new( logdev, options )
|
25
|
+
end
|
26
|
+
|
27
|
+
def emit( details, message, options )
|
28
|
+
if options && options.size > 0 then
|
29
|
+
options = options.map { |k,v| [k, v.to_json].join(":") }.join(",")
|
30
|
+
message = "#{message}; #{options}"
|
31
|
+
end
|
32
|
+
@logdev.write "#{details[:time].to_i} [#{details[:pid]}:#{details[:fiber]}] #{details[:pri_sym]} #{message}\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
def as_json
|
36
|
+
{ :log_file => @options }
|
37
|
+
end
|
38
|
+
|
39
|
+
end # LogFileEmitter
|
40
|
+
|
41
|
+
end # Unilogger
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "fiber" unless RUBY_VERSION =~ /^1\.8/
|
2
|
+
require "json"
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
module Unilogger
|
6
|
+
|
7
|
+
# Adds pid and timestamp to each log entry.
|
8
|
+
# Implements IO API (<<) which is equivalent to info()
|
9
|
+
# Implements Logger API (debug, info, warn, error, fatal)
|
10
|
+
class Logger
|
11
|
+
|
12
|
+
include ::Logger::Severity
|
13
|
+
attr :level, true
|
14
|
+
attr :emitters
|
15
|
+
|
16
|
+
def initialize( level = DEBUG, emitters = [] )
|
17
|
+
@level = level
|
18
|
+
@emitters = emitters.dup
|
19
|
+
end
|
20
|
+
|
21
|
+
def emit( details, message, options )
|
22
|
+
@emitters.each { |e| e.emit( details, message, options ) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def <<( message )
|
26
|
+
details = { :pri_sym => :INFO, :pri_num => Logger::Severity::INFO, :pid => Process.pid, :time => Time.now }
|
27
|
+
self.emit( details, message, nil )
|
28
|
+
end
|
29
|
+
|
30
|
+
# level methods may take 0..2 arguments, or a block parameter
|
31
|
+
# argument 1 or the block is stringified as the primary message
|
32
|
+
# argument 2 is a set of named parameters (e.g. a hash)
|
33
|
+
::Logger::Severity.constants.each do |const|
|
34
|
+
|
35
|
+
# const e.g. INFO; label e.g. info; priority e.g. 1
|
36
|
+
label = const.to_s.downcase.to_sym
|
37
|
+
priority = ::Logger::Severity.const_get(const)
|
38
|
+
|
39
|
+
# def info( message, options = {}, &block ) ...
|
40
|
+
define_method( label ) do |*args,&block|
|
41
|
+
raise ArgumentError if args.size > 2
|
42
|
+
if @level <= priority then
|
43
|
+
details = { :pri_sym => const, :pri_num => priority, :pid => Process.pid, :time => Time.now, :fiber => 0 } if RUBY_VERSION =~ /^1\.8/
|
44
|
+
details = { :pri_sym => const, :pri_num => priority, :pid => Process.pid, :time => Time.now, :fiber => Fiber.current.object_id } unless RUBY_VERSION =~ /^1\.8/
|
45
|
+
case args.size
|
46
|
+
when 0
|
47
|
+
message = block.call if block
|
48
|
+
options = nil
|
49
|
+
when 1
|
50
|
+
message = args.first
|
51
|
+
options = nil
|
52
|
+
when 2
|
53
|
+
message = args.first
|
54
|
+
options = args.last
|
55
|
+
end
|
56
|
+
self.emit( details, message.to_s, options )
|
57
|
+
end
|
58
|
+
@level <= priority
|
59
|
+
end # info()
|
60
|
+
|
61
|
+
# def info?
|
62
|
+
define_method( "#{label}?".to_sym ) do
|
63
|
+
@level <= priority
|
64
|
+
end # info?
|
65
|
+
|
66
|
+
end # each
|
67
|
+
|
68
|
+
def as_json
|
69
|
+
{ :level => @level, :emitters => as_json_helper(@emitters) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def as_json_helper( it )
|
73
|
+
if it.nil? then
|
74
|
+
it
|
75
|
+
elsif it.respond_to?(:as_json) then
|
76
|
+
it.as_json
|
77
|
+
elsif it.kind_of?(Hash) then
|
78
|
+
it.inject({}) { |a,i| a[i.first] = as_json_helper(i.last); a }
|
79
|
+
elsif it.kind_of?(Array) || it.kind_of?(Enumerable)
|
80
|
+
it.map { |i| as_json_helper(i) }
|
81
|
+
else
|
82
|
+
it
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_json
|
87
|
+
as_json.to_json
|
88
|
+
end
|
89
|
+
|
90
|
+
end # Logger
|
91
|
+
|
92
|
+
end # Unilogger
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "logger"
|
2
|
+
require "redis"
|
3
|
+
require "redis/namespace"
|
4
|
+
|
5
|
+
module Unilogger
|
6
|
+
|
7
|
+
class RedisEmitter
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def build( options )
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def emit( details, message, options )
|
15
|
+
end
|
16
|
+
|
17
|
+
end # RedisEmitter
|
18
|
+
|
19
|
+
end # Unilogger
|
data/test/logger.yml.erb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
stderr: &stderr
|
2
|
+
LogFileEmitter:
|
3
|
+
logdev: stderr
|
4
|
+
|
5
|
+
standard: &standard
|
6
|
+
LogFileEmitter:
|
7
|
+
logdev: "./log/<%= ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development" %>.log"
|
8
|
+
shift_age: daily
|
9
|
+
|
10
|
+
development:
|
11
|
+
level: debug
|
12
|
+
emitters: [ *standard ]
|
13
|
+
|
14
|
+
test:
|
15
|
+
level: info
|
16
|
+
emitters: [ *stderr ]
|
17
|
+
|
18
|
+
beta:
|
19
|
+
level: info
|
20
|
+
emitters: [ *standard ]
|
21
|
+
|
22
|
+
staging:
|
23
|
+
level: info
|
24
|
+
emitters: [ *standard ]
|
25
|
+
|
26
|
+
production:
|
27
|
+
level: info
|
28
|
+
emitters: [ *standard ]
|
data/test/test.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root = File.expand_path( "../..", __FILE__ )
|
4
|
+
$: << root + "/lib"
|
5
|
+
require "unilogger"
|
6
|
+
|
7
|
+
logger = Unilogger::Builder.build :root => root
|
8
|
+
puts logger.to_json
|
9
|
+
|
10
|
+
logger.debug "Starting."
|
11
|
+
logger.info "started", :root => root
|
12
|
+
logger.warn "nothing to do"
|
13
|
+
logger.error "end of file"
|
14
|
+
logger.fatal "exiting"
|
data/unilogger.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "unilogger/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "unilogger"
|
7
|
+
s.version = Unilogger::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Mark Lanett"]
|
10
|
+
s.email = ["mark.lanett@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = "Unified logger that writes to a log file, redis queue, mongo collection, and hoptoad."
|
13
|
+
|
14
|
+
s.rubyforge_project = "unilogger"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency "json"
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unilogger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Mark Lanett
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-04-04 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: json
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
description:
|
34
|
+
email:
|
35
|
+
- mark.lanett@gmail.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files: []
|
41
|
+
|
42
|
+
files:
|
43
|
+
- .gitignore
|
44
|
+
- Gemfile
|
45
|
+
- README
|
46
|
+
- Rakefile
|
47
|
+
- lib/unilogger.rb
|
48
|
+
- lib/unilogger/builder.rb
|
49
|
+
- lib/unilogger/log_file_emitter.rb
|
50
|
+
- lib/unilogger/logger.rb
|
51
|
+
- lib/unilogger/mongo_emitter.rb
|
52
|
+
- lib/unilogger/redis_emitter.rb
|
53
|
+
- lib/unilogger/version.rb
|
54
|
+
- test/logger.yml.erb
|
55
|
+
- test/test.rb
|
56
|
+
- unilogger.gemspec
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: ""
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
version: "0"
|
82
|
+
requirements: []
|
83
|
+
|
84
|
+
rubyforge_project: unilogger
|
85
|
+
rubygems_version: 1.3.7
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: Unified logger that writes to a log file, redis queue, mongo collection, and hoptoad.
|
89
|
+
test_files:
|
90
|
+
- test/logger.yml.erb
|
91
|
+
- test/test.rb
|