logsaber 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.markdown +156 -0
- data/Rakefile +1 -0
- data/lib/logsaber/version.rb +3 -0
- data/lib/logsaber.rb +100 -0
- data/logsaber.gemspec +21 -0
- data/spec/logsaber_spec.rb +107 -0
- data/spec/spec_helper.rb +27 -0
- metadata +74 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Anthony Cook
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
Logsaber
|
2
|
+
=========
|
3
|
+
|
4
|
+
A logger for a more civilized age.
|
5
|
+
|
6
|
+
Philosophy/Why Logsaber?
|
7
|
+
-------------------------
|
8
|
+
|
9
|
+
Logsaber is a lot like Ruby's built in Logger class,
|
10
|
+
but it is based on the real world experience of how I actually use loggers.
|
11
|
+
|
12
|
+
The biggest difference is Logsaber's intelligent output.
|
13
|
+
|
14
|
+
### Intelligent Logging
|
15
|
+
|
16
|
+
If you pass a single string argument to an event method Logsaber will just log that string without any frills.
|
17
|
+
|
18
|
+
But if you pass it an object like an Array:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
array = [1,2,3]
|
22
|
+
$log.info array
|
23
|
+
```
|
24
|
+
|
25
|
+
...it will inspect the array and the output will reflect the intent:
|
26
|
+
|
27
|
+
```
|
28
|
+
2013-03-02 21:08:30.797 [ INFO] 32981 | OBJ : [1, 2, 3]
|
29
|
+
```
|
30
|
+
|
31
|
+
Even better, if you pass in two arguments, like this:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
$log.info 'using environment', ENV['MYAPP_ENV']
|
35
|
+
```
|
36
|
+
|
37
|
+
...the first will be treated as a label, and the second as an object to be inspected:
|
38
|
+
|
39
|
+
```
|
40
|
+
2013-03-02 20:11:22.630 [ INFO] 31395 | using environment : development
|
41
|
+
```
|
42
|
+
|
43
|
+
If you pass in a block:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
@log.info :heavy, 'this could be resource intensive' do
|
47
|
+
10000.times.to_a.last
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
...Logsaber will intelligently evaluate it and format your output sanely:
|
52
|
+
|
53
|
+
```
|
54
|
+
2013-03-02 21:20:04.715 [ INFO] 32981 | heavy : \"this could be resource intensive\" | 9999
|
55
|
+
```
|
56
|
+
|
57
|
+
Also, since blocks are lazy loaded, they won't be evaluated at all if the severity is below the log level threshold, this is really important if your debug output is resource intensive.
|
58
|
+
|
59
|
+
### Ruby Logger Limitations Surpassed
|
60
|
+
|
61
|
+
There's also some complaints about the native Logger than I address:
|
62
|
+
|
63
|
+
1. You can't specify the log level on instantiation
|
64
|
+
- Logsaber lets you set the log level when you create it:
|
65
|
+
`$log = Logsaber.create file, :warn`
|
66
|
+
- But you can still change the default later:
|
67
|
+
`$log.level = :info`
|
68
|
+
2. You must specify the "progname" for every event
|
69
|
+
- Logsaber lets you set the app name when you create it:
|
70
|
+
`$log = Logsaber.create file, :warn, 'MyApp'`
|
71
|
+
- Or change it to something else at any time:
|
72
|
+
`$log.appname = 'SomethingElse'`
|
73
|
+
|
74
|
+
Installation
|
75
|
+
------------
|
76
|
+
|
77
|
+
Add this line to your application's Gemfile:
|
78
|
+
|
79
|
+
gem 'logomatic'
|
80
|
+
|
81
|
+
And then execute:
|
82
|
+
|
83
|
+
$ bundle
|
84
|
+
|
85
|
+
Or install it yourself as:
|
86
|
+
|
87
|
+
$ gem install logomatic
|
88
|
+
|
89
|
+
Setup
|
90
|
+
-----
|
91
|
+
|
92
|
+
Give it a filename and it will log to a file:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
$log = Logsaber.create './log/my_app.log'
|
96
|
+
```
|
97
|
+
|
98
|
+
Give it an IO and it will log to it:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
$log = Logsaber.create $stdout
|
102
|
+
```
|
103
|
+
|
104
|
+
Even give it a StringIO and it will log to that:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
require 'stringio'
|
108
|
+
stringio = StringIO.create
|
109
|
+
|
110
|
+
$log = Logsaber.create stringio
|
111
|
+
```
|
112
|
+
|
113
|
+
You can also set the log level on initialization (it's :info by default):
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
$log = Logsaber.create $stdout, :debug
|
117
|
+
```
|
118
|
+
|
119
|
+
And you can optionally specify a program name:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
$log = Logsaber.create $stdout, :info, 'MyApp'
|
123
|
+
```
|
124
|
+
|
125
|
+
Usage
|
126
|
+
-----
|
127
|
+
|
128
|
+
Then you can use any of the logging commands:
|
129
|
+
|
130
|
+
`debug`, `info`, `warn`, `error`, or `fatal`
|
131
|
+
|
132
|
+
like this:
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
$log.warn 'Something might be amiss here'
|
136
|
+
```
|
137
|
+
|
138
|
+
or this:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
$log.error 'PEBKAC', @user
|
142
|
+
```
|
143
|
+
|
144
|
+
Contributing
|
145
|
+
------------
|
146
|
+
|
147
|
+
1. Fork it
|
148
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
149
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
150
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
151
|
+
5. Create new Pull Request
|
152
|
+
|
153
|
+
Author
|
154
|
+
======
|
155
|
+
|
156
|
+
Anthony M. Cook 2013
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/logsaber.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'logsaber/version'
|
2
|
+
|
3
|
+
class Logsaber
|
4
|
+
def self.create new_output = $stdout, new_level = :info, new_appname = nil
|
5
|
+
log = self.new
|
6
|
+
|
7
|
+
log.output =
|
8
|
+
if new_output.is_a? String then
|
9
|
+
File.new new_output, 'a'
|
10
|
+
else
|
11
|
+
new_output
|
12
|
+
end
|
13
|
+
|
14
|
+
log.level = new_level
|
15
|
+
log.appname = new_appname
|
16
|
+
|
17
|
+
log
|
18
|
+
end
|
19
|
+
attr_accessor :output, :level, :appname, :time_format
|
20
|
+
|
21
|
+
DEFAULT_TIME_FORMAT ||= '%Y-%m-%d %H:%M:%S.%L'
|
22
|
+
SEVERITY_LEVELS ||= [:debug, :info, :warn, :error, :fatal]
|
23
|
+
|
24
|
+
SEVERITY_LEVELS.each do |method_name|
|
25
|
+
eval <<-END_OF_METHOD
|
26
|
+
def #{method_name} *args, &block
|
27
|
+
log :#{method_name}, *args, &block
|
28
|
+
end
|
29
|
+
END_OF_METHOD
|
30
|
+
end
|
31
|
+
|
32
|
+
def level= new_level
|
33
|
+
@level = new_level if SEVERITY_LEVELS.include? new_level
|
34
|
+
end
|
35
|
+
|
36
|
+
def time_format
|
37
|
+
@time_format ||= DEFAULT_TIME_FORMAT
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def log severity, *details
|
43
|
+
return unless loggable? severity
|
44
|
+
label, info, object = extract_details details, block_given?
|
45
|
+
|
46
|
+
if block_given? then
|
47
|
+
result = yield
|
48
|
+
|
49
|
+
info << ' | ' unless info.empty?
|
50
|
+
info << result.to_s
|
51
|
+
|
52
|
+
object = result
|
53
|
+
end
|
54
|
+
|
55
|
+
message = format severity, "#{label} : #{info}"
|
56
|
+
output.puts message
|
57
|
+
output.flush
|
58
|
+
|
59
|
+
object
|
60
|
+
end
|
61
|
+
|
62
|
+
def extract_details details, given_block
|
63
|
+
primary, secondary, object = details
|
64
|
+
|
65
|
+
if details.length == 2 then
|
66
|
+
[primary.to_s, secondary.inspect, object || secondary]
|
67
|
+
elsif given_block then
|
68
|
+
[primary, secondary.to_s, object]
|
69
|
+
elsif [String, Numeric].any?{|klass| primary.is_a? klass} then
|
70
|
+
['MSG', primary, object || primary]
|
71
|
+
else
|
72
|
+
['OBJ', primary.to_s, object || primary]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def loggable? severity
|
77
|
+
SEVERITY_LEVELS.index(severity) >= SEVERITY_LEVELS.index(level)
|
78
|
+
end
|
79
|
+
|
80
|
+
def format severity, contents
|
81
|
+
%Q{#{timestamp} [#{severity_info severity}] #{process_info} | #{contents}}
|
82
|
+
end
|
83
|
+
|
84
|
+
def process_info
|
85
|
+
pid = Process.pid.to_s
|
86
|
+
appname? ? "#{appname}:#{pid}" : pid
|
87
|
+
end
|
88
|
+
|
89
|
+
def severity_info severity
|
90
|
+
severity.to_s.upcase.rjust 5
|
91
|
+
end
|
92
|
+
|
93
|
+
def timestamp
|
94
|
+
Time.now.strftime time_format
|
95
|
+
end
|
96
|
+
|
97
|
+
def appname?
|
98
|
+
!!@appname
|
99
|
+
end
|
100
|
+
end
|
data/logsaber.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'logsaber/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "logsaber"
|
8
|
+
gem.version = Logsaber::VERSION
|
9
|
+
gem.authors = ["Anthony Cook"]
|
10
|
+
gem.email = ["anthonymichaelcook@gmail.com"]
|
11
|
+
gem.description = %q{A logger for a more civilized age. Intelligent logs for your applications.}
|
12
|
+
gem.summary = %q{A logger for a more civilized age.}
|
13
|
+
gem.homepage = "http://github.com/acook/logsaber"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_development_dependency 'uspec'
|
21
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
test_string = 'foo'
|
4
|
+
|
5
|
+
spec 'will output to a file, given a filename' do
|
6
|
+
filename = './test_file.log'
|
7
|
+
begin
|
8
|
+
File.delete filename
|
9
|
+
rescue Errno::ENOENT => e
|
10
|
+
# ignore if file doesn't exist
|
11
|
+
end
|
12
|
+
|
13
|
+
log = Logsaber.create filename
|
14
|
+
log.info test_string
|
15
|
+
|
16
|
+
contents = File.open(filename, 'r').gets
|
17
|
+
contents.include?(test_string).tap{File.delete filename}
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'stringio'
|
21
|
+
spec 'can use a StringIO' do
|
22
|
+
stringio = StringIO.new
|
23
|
+
|
24
|
+
log = Logsaber.create stringio
|
25
|
+
log.info test_string
|
26
|
+
|
27
|
+
stringio.string.include? test_string
|
28
|
+
end
|
29
|
+
|
30
|
+
spec 'can use an IO' do
|
31
|
+
output = capture do
|
32
|
+
log = Logsaber.create $stdout
|
33
|
+
log.info test_string
|
34
|
+
end
|
35
|
+
|
36
|
+
output.include?(test_string) || output
|
37
|
+
end
|
38
|
+
|
39
|
+
@log = Logsaber.create
|
40
|
+
|
41
|
+
spec 'has shortcut methods' do
|
42
|
+
methods = [:debug, :info, :warn, :error, :fatal]
|
43
|
+
has = methods.map do |m|
|
44
|
+
@log.respond_to?(m) && m
|
45
|
+
end
|
46
|
+
|
47
|
+
has.all? || methods - has
|
48
|
+
end
|
49
|
+
|
50
|
+
spec 'can tell you the current minimum log level' do
|
51
|
+
@log.level == :info || @log.level
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
@output = StringIO.new
|
56
|
+
@log = Logsaber.create @output
|
57
|
+
|
58
|
+
def self.clear_log
|
59
|
+
@output.truncate 0
|
60
|
+
@output.rewind
|
61
|
+
end
|
62
|
+
|
63
|
+
def format label, info
|
64
|
+
"[ INFO] #{Process.pid} | #{label} : #{info}"
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
spec 'basic usage' do
|
69
|
+
clear_log
|
70
|
+
|
71
|
+
@log.info test_string
|
72
|
+
@output.string.include? format('MSG', test_string)
|
73
|
+
end
|
74
|
+
|
75
|
+
spec ' with details usage' do
|
76
|
+
clear_log
|
77
|
+
|
78
|
+
@log.info :test_string, test_string
|
79
|
+
@output.string.include? format(:test_string, test_string.inspect)
|
80
|
+
end
|
81
|
+
|
82
|
+
spec 'object usage' do
|
83
|
+
clear_log
|
84
|
+
|
85
|
+
array = [1,2,3]
|
86
|
+
@log.info array
|
87
|
+
@output.string.include? format('OBJ', array.inspect)
|
88
|
+
end
|
89
|
+
|
90
|
+
spec 'basic block usage' do
|
91
|
+
clear_log
|
92
|
+
|
93
|
+
@log.info 'label' do
|
94
|
+
'block'
|
95
|
+
end
|
96
|
+
@output.string.include?(format('label', 'block')) || @output.string
|
97
|
+
end
|
98
|
+
|
99
|
+
spec 'block with details usage' do
|
100
|
+
clear_log
|
101
|
+
|
102
|
+
@log.info 'label', 'details' do
|
103
|
+
'block'
|
104
|
+
end
|
105
|
+
@output.string.include?(format('label', '"details" | block')) || @output.string
|
106
|
+
end
|
107
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
Bundler.require :development, :test
|
4
|
+
|
5
|
+
require 'uspec'
|
6
|
+
|
7
|
+
Dir.chdir File.dirname(__FILE__)
|
8
|
+
|
9
|
+
require_relative '../lib/logsaber'
|
10
|
+
|
11
|
+
extend Uspec
|
12
|
+
|
13
|
+
def self.capture
|
14
|
+
readme, writeme = IO.pipe
|
15
|
+
pid = fork do
|
16
|
+
$stdout.reopen writeme
|
17
|
+
readme.close
|
18
|
+
|
19
|
+
yield
|
20
|
+
end
|
21
|
+
|
22
|
+
writeme.close
|
23
|
+
output = readme.read
|
24
|
+
Process.waitpid(pid)
|
25
|
+
|
26
|
+
output
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: logsaber
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Anthony Cook
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: uspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: A logger for a more civilized age. Intelligent logs for your applications.
|
31
|
+
email:
|
32
|
+
- anthonymichaelcook@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- Gemfile
|
39
|
+
- LICENSE.txt
|
40
|
+
- README.markdown
|
41
|
+
- Rakefile
|
42
|
+
- lib/logsaber.rb
|
43
|
+
- lib/logsaber/version.rb
|
44
|
+
- logsaber.gemspec
|
45
|
+
- spec/logsaber_spec.rb
|
46
|
+
- spec/spec_helper.rb
|
47
|
+
homepage: http://github.com/acook/logsaber
|
48
|
+
licenses: []
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.8.25
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: A logger for a more civilized age.
|
71
|
+
test_files:
|
72
|
+
- spec/logsaber_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
has_rdoc:
|