logsaber 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create ruby-1.9.3-p385@logsaber
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - rbx-19mode
6
+ - ruby-head
7
+ script: bundle exec ruby spec/log_spec.rb
8
+ before_install:
9
+ - gem update bundler
10
+ before_script:
11
+ - bundle exec gem list
data/README.markdown CHANGED
@@ -3,10 +3,17 @@ Logsaber
3
3
 
4
4
  A logger for a more civilized age.
5
5
 
6
- Philosophy/Why Logsaber?
7
- -------------------------
6
+ - Edge Logsaber README: https://github.com/acook/logsaber#readme
7
+ - Release version README: http://rubydoc.info/gems/logsaber/file/README.markdown
8
8
 
9
- Logsaber is a lot like Ruby's built in Logger class,
9
+ [![Build Status](https://travis-ci.org/acook/logsaber.png?branch=master)](https://travis-ci.org/acook/logsaber)
10
+ [![Code Climate](https://codeclimate.com/github/acook/logsaber.png)](https://codeclimate.com/github/acook/logsaber)
11
+ [![Dependency Status](https://gemnasium.com/acook/logsaber.png)](https://gemnasium.com/acook/logsaber)
12
+
13
+ Philosophy / Why Logsaber?
14
+ --------------------------
15
+
16
+ Logsaber is a lot like Ruby's built in [Logger](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html) class,
10
17
  but it is based on the real world experience of how I actually use loggers.
11
18
 
12
19
  The biggest difference is Logsaber's intelligent output.
@@ -51,40 +58,59 @@ end
51
58
  ...Logsaber will intelligently evaluate it and format your output sanely:
52
59
 
53
60
  ```
54
- 2013-03-02 21:20:04.715 [ INFO] 32981 | heavy : \"this could be resource intensive\" | 9999
61
+ 2013-03-02 21:20:04.715 [ INFO] 32981 | heavy : "this could be resource intensive" | 9999
55
62
  ```
56
63
 
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.
64
+ Also, since blocks are lazy loaded, they won't be evaluated at all if the severity is below the log level threshold,
65
+ this is really important if your debug output is resource intensive.
58
66
 
59
67
  ### Ruby Logger Limitations Surpassed
60
68
 
61
69
  There's also some complaints about the native Logger than I address:
62
70
 
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'`
71
+ 1. You can't specify the log level on instantiation.
73
72
 
74
- Installation
75
- ------------
73
+ Logsaber lets you set the log level when you create it:
74
+
75
+ ```ruby
76
+ $log = Logsaber.create level: :warn
77
+ ```
78
+
79
+ But you can still change the default later:
76
80
 
77
- Add this line to your application's Gemfile:
81
+ ```ruby
82
+ $log.level = :info
83
+ ```
78
84
 
79
- gem 'logomatic'
85
+ 2. You must specify the "progname" for every event.
80
86
 
81
- And then execute:
87
+ Logsaber lets you set your app's name when you create it:
82
88
 
83
- $ bundle
89
+ ```ruby
90
+ $log = Logsaber.create appname: 'MyApp'
91
+ ```
92
+
93
+ Or change it to something else at any time:
94
+
95
+ ```ruby
96
+ $log.appname = 'SomethingElse'
97
+ ```
98
+
99
+ ...and the output will look like this:
100
+
101
+ ```
102
+ 2013-03-03 16:50:43.595 [ INFO] SomethingElse:8881 | MSG : ohai
103
+ ```
104
+
105
+ Installation
106
+ ------------
84
107
 
85
- Or install it yourself as:
108
+ Use [Bundler](http://gembundler.com):
86
109
 
87
- $ gem install logomatic
110
+ ```ruby
111
+ # in your Gemfile
112
+ gem 'logsaber'
113
+ ```
88
114
 
89
115
  Setup
90
116
  -----
@@ -95,13 +121,13 @@ Give it a filename and it will log to a file:
95
121
  $log = Logsaber.create './log/my_app.log'
96
122
  ```
97
123
 
98
- Give it an IO and it will log to it:
124
+ Or you log to an IO (`$stdout` is the default, good for debugging):
99
125
 
100
126
  ```ruby
101
127
  $log = Logsaber.create $stdout
102
128
  ```
103
129
 
104
- Even give it a StringIO and it will log to that:
130
+ It can even log to a StringIO (good for test environments):
105
131
 
106
132
  ```ruby
107
133
  require 'stringio'
@@ -110,16 +136,22 @@ stringio = StringIO.create
110
136
  $log = Logsaber.create stringio
111
137
  ```
112
138
 
113
- You can also set the log level on initialization (it's :info by default):
139
+ You can also set the log level on initialization (it's `:info` by default):
140
+
141
+ ```ruby
142
+ $log = Logsaber.create level: :debug
143
+ ```
144
+
145
+ And you can optionally specify the name of your app (which is `nil` by default, it's displayed next to the pid in the output):
114
146
 
115
147
  ```ruby
116
- $log = Logsaber.create $stdout, :debug
148
+ $log = Logsaber.create appname: 'MyApp'
117
149
  ```
118
150
 
119
- And you can optionally specify a program name:
151
+ Example with all options:
120
152
 
121
153
  ```ruby
122
- $log = Logsaber.create $stdout, :info, 'MyApp'
154
+ Logsaber.create 'my_app.log', level: :debug, appname: 'MyApp'
123
155
  ```
124
156
 
125
157
  Usage
@@ -135,12 +167,20 @@ like this:
135
167
  $log.warn 'Something might be amiss here'
136
168
  ```
137
169
 
138
- or this:
170
+ or like this:
139
171
 
140
172
  ```ruby
141
173
  $log.error 'PEBKAC', @user
142
174
  ```
143
175
 
176
+ or maybe:
177
+
178
+ ```ruby
179
+ @log.debug "What is this I don't even." do
180
+ big_data.inspect
181
+ end
182
+ ```
183
+
144
184
  Contributing
145
185
  ------------
146
186
 
data/lib/logsaber.rb CHANGED
@@ -1,100 +1,11 @@
1
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
2
+ require 'logsaber/formatter'
3
+ require 'logsaber/log'
4
+ require 'logsaber/options'
5
+ require 'logsaber/entry'
6
+
7
+ module Logsaber
8
+ def self.create *args, &block
9
+ Log.create *args, &block
99
10
  end
100
11
  end
@@ -0,0 +1,66 @@
1
+ module Logsaber
2
+ class Entry
3
+ def self.create all_details, &block
4
+ all_details << block.call if block
5
+ new all_details.length, all_details.shift, Array(all_details)
6
+ end
7
+
8
+ def initialize length, primary, secondary
9
+ @length, @primary, @secondary = length, primary, secondary
10
+ end
11
+ attr :length, :primary, :secondary
12
+
13
+ def parse
14
+ [text, value]
15
+ end
16
+
17
+ protected
18
+
19
+ def value
20
+ secondary.last || primary
21
+ end
22
+
23
+ def text
24
+ "#{label} : #{info}"
25
+ end
26
+
27
+ def label
28
+ if !secondary.empty? then
29
+ view primary
30
+ elsif primary.is_a? String then
31
+ 'MSG'
32
+ else
33
+ 'OBJ'
34
+ end
35
+ end
36
+
37
+ def info
38
+ if secondary.empty? then
39
+ view primary
40
+ else
41
+ details
42
+ end
43
+ end
44
+
45
+ def details
46
+ secondary.map{|item| analyze item }.join ' | '
47
+ end
48
+
49
+ def view object
50
+ viewable?(object) ? object.to_s : object.inspect
51
+ end
52
+
53
+ def analyze object
54
+ analyzeable?(object) ? object : object.inspect
55
+ end
56
+
57
+ def viewable? object
58
+ object && (analyzeable?(object) || object.is_a?(Symbol))
59
+ end
60
+
61
+ def analyzeable? object
62
+ object.is_a?(String) && !object.empty?
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,63 @@
1
+ module Logsaber
2
+ class Formatter
3
+ DEFAULT_TIME_FORMAT ||= '%Y-%m-%d %H:%M:%S.%L'
4
+
5
+ def initialize color = nil
6
+ @color = color
7
+ end
8
+ attr_accessor :time_format, :log, :color
9
+
10
+ def set_log new_log
11
+ self.log = new_log
12
+ self
13
+ end
14
+
15
+ def time_format
16
+ @time_format ||= DEFAULT_TIME_FORMAT
17
+ end
18
+
19
+ def color!
20
+ @color = SimpleColor.new
21
+ end
22
+
23
+ def color?
24
+ !!@color
25
+ end
26
+
27
+ def format severity, contents
28
+ index = log.class::SEVERITY_LEVELS.index(severity)
29
+ text = layout severity, contents
30
+
31
+ if color? then
32
+ color.colorize index, text
33
+ else
34
+ text
35
+ end
36
+ end
37
+
38
+ def layout severity, contents
39
+ %(#{timestamp} [#{severity_info severity}] #{process_info} | #{contents})
40
+ end
41
+
42
+ def timestamp
43
+ Time.now.strftime time_format
44
+ end
45
+
46
+ def severity_info severity
47
+ severity.to_s.upcase.rjust 5
48
+ end
49
+
50
+ def process_info
51
+ pid = Process.pid.to_s
52
+ appname? ? "#{appname}:#{pid}" : pid
53
+ end
54
+
55
+ def appname?
56
+ !!appname
57
+ end
58
+
59
+ def appname
60
+ log.appname
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,75 @@
1
+ module Logsaber
2
+ class Log
3
+ class << self
4
+ def create *args
5
+ default_options = {
6
+ output: $stdout,
7
+ level: :info,
8
+ appname: nil,
9
+ formatter: Logsaber::Formatter.new
10
+ }
11
+ options = Options.extract_from args, default_options, :output
12
+
13
+ self.new *options
14
+ end
15
+ end
16
+
17
+ SEVERITY_LEVELS ||= [:debug, :info, :warn, :error, :fatal, :off]
18
+
19
+ def initialize output, level, appname, formatter
20
+ @output = outputize output
21
+ @level = level.to_sym
22
+ @appname = appname
23
+ @formatter = formatter.set_log self
24
+
25
+ unless SEVERITY_LEVELS.include? @level then
26
+ raise "Invalid level: #{level.inspect}.\nUse one of: #{SEVERITY_LEVELS}"
27
+ end
28
+ end
29
+
30
+ attr_accessor :output, :level, :appname, :formatter
31
+
32
+ SEVERITY_LEVELS.each do |method_name|
33
+ next if method_name == :off
34
+
35
+ eval <<-END_OF_METHOD
36
+ def #{method_name} *args, &block
37
+ log :#{method_name}, *args, &block
38
+ end
39
+ END_OF_METHOD
40
+ end
41
+
42
+ protected
43
+
44
+ def log severity, *details, &block
45
+ return unless loggable? severity
46
+ message, object = Entry.create(details, &block).parse
47
+
48
+ out format(severity, message)
49
+ object
50
+ end
51
+
52
+ def out text
53
+ output.puts text
54
+ output.flush
55
+ end
56
+
57
+ def format *args
58
+ formatter.format *args
59
+ end
60
+
61
+ def loggable? severity
62
+ SEVERITY_LEVELS.index(severity) >= SEVERITY_LEVELS.index(level)
63
+ end
64
+
65
+ def outputize new_output
66
+ if new_output.is_a? String then
67
+ File.new new_output, 'a'
68
+ elsif new_output.respond_to? :puts then
69
+ new_output
70
+ else
71
+ raise "invalid output object: #{new_output.inspect}"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,15 @@
1
+ module Logsaber
2
+ class Options < OpenStruct
3
+ def self.extract_from args, defaults = {}, primary = nil
4
+ options = args.last.is_a?(Hash) ? args.pop : Hash.new
5
+ options[primary] = args.shift if primary && args.first
6
+
7
+ new defaults.merge(options)
8
+ end
9
+
10
+ def to_a
11
+ @table.values
12
+ end
13
+ alias_method :to_ary, :to_a
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module Logsaber
2
+ class SimpleColor
3
+ attr_accessor colors
4
+
5
+ def colorize index, text = nil, &block
6
+ "#{color index}#{text || yield}#{esc 0}"
7
+ end
8
+
9
+ def color index
10
+ esc colors[index]
11
+ end
12
+
13
+ def colors
14
+ @colors = [31, 0, 33, 31, '31;1']
15
+ end
16
+
17
+ def esc seq
18
+ "\e[#{seq}m"
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
- class Logsaber
2
- VERSION = "0.0.1"
1
+ module Logsaber
2
+ VERSION = "1.0.0"
3
3
  end
@@ -72,11 +72,11 @@ spec 'basic usage' do
72
72
  @output.string.include? format('MSG', test_string)
73
73
  end
74
74
 
75
- spec ' with details usage' do
75
+ spec 'with details usage' do
76
76
  clear_log
77
77
 
78
78
  @log.info :test_string, test_string
79
- @output.string.include? format(:test_string, test_string.inspect)
79
+ @output.string.include? format(:test_string, test_string)
80
80
  end
81
81
 
82
82
  spec 'object usage' do
@@ -102,6 +102,50 @@ spec 'block with details usage' do
102
102
  @log.info 'label', 'details' do
103
103
  'block'
104
104
  end
105
- @output.string.include?(format('label', '"details" | block')) || @output.string
105
+ @output.string.include?(format('label', 'details | block')) || @output.string
106
106
  end
107
107
 
108
+ spec 'accepts appname during creation' do
109
+ clear_log
110
+
111
+ log = Logsaber.create output: @output, appname: 'MyAwesomeApp'
112
+
113
+ log.info 'ohai'
114
+ @output.string.include?("[ INFO] MyAwesomeApp:#{Process.pid} | MSG : ohai") || @output.string
115
+ end
116
+
117
+ spec 'setting level to :off will prevent any logging' do
118
+ output = capture do
119
+ log = Logsaber.create level: :off
120
+
121
+ log.debug 'debug'
122
+ log.info 'info'
123
+ log.warn 'warn'
124
+ log.error 'error'
125
+ log.fatal 'fatal'
126
+ end
127
+
128
+ output.empty? || output
129
+ end
130
+
131
+ spec 'there is no "off" method' do
132
+ !@log.respond_to? :off
133
+ end
134
+
135
+ spec 'timestamp format is mutable' do
136
+ clear_log
137
+
138
+ @log.formatter.time_format = 'xxx%Yxxx'
139
+ @log.info test_string
140
+ @log.formatter.time_format = Logsaber::Formatter::DEFAULT_TIME_FORMAT
141
+
142
+ match = @output.string.match /(xxx\d\d\d\dxxx) (#{Regexp.escape format('MSG', test_string)})/
143
+ !match[0].nil? && !match[1].nil? || match rescue @output.string
144
+ end
145
+
146
+ spec 'Log#log allows many items' do
147
+ clear_log
148
+
149
+ @log.info :foo, '1', '2', '3'
150
+ @output.string.include? format('foo', '1 | 2 | 3')
151
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logsaber
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-04 00:00:00.000000000 Z
12
+ date: 2013-03-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: uspec
@@ -35,14 +35,21 @@ extensions: []
35
35
  extra_rdoc_files: []
36
36
  files:
37
37
  - .gitignore
38
+ - .rvmrc
39
+ - .travis.yml
38
40
  - Gemfile
39
41
  - LICENSE.txt
40
42
  - README.markdown
41
43
  - Rakefile
42
44
  - lib/logsaber.rb
45
+ - lib/logsaber/entry.rb
46
+ - lib/logsaber/formatter.rb
47
+ - lib/logsaber/log.rb
48
+ - lib/logsaber/options.rb
49
+ - lib/logsaber/simple_color.rb
43
50
  - lib/logsaber/version.rb
44
51
  - logsaber.gemspec
45
- - spec/logsaber_spec.rb
52
+ - spec/log_spec.rb
46
53
  - spec/spec_helper.rb
47
54
  homepage: http://github.com/acook/logsaber
48
55
  licenses: []
@@ -69,6 +76,5 @@ signing_key:
69
76
  specification_version: 3
70
77
  summary: A logger for a more civilized age.
71
78
  test_files:
72
- - spec/logsaber_spec.rb
79
+ - spec/log_spec.rb
73
80
  - spec/spec_helper.rb
74
- has_rdoc: