dlogger 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage/
10
+ tests/
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dlogger.gemspec
4
+ gemspec
5
+
6
+ gem 'eventmachine', '~> 1.0.0.beta'
7
+ gem 'yajl-ruby'
8
+
9
+ group(:test) do
10
+ gem 'schmurfy-bacon', '~> 1.2.1'
11
+ gem 'mocha', '~> 0.10.0'
12
+ gem 'simplecov'
13
+ gem 'schmurfy-em-spec'
14
+ gem 'guard'
15
+ gem 'guard-bacon'
16
+ gem 'rb-fsevent'
17
+ gem 'growl'
18
+ end
19
+
data/Guardfile ADDED
@@ -0,0 +1,15 @@
1
+
2
+ require 'erb'
3
+
4
+ # parameters:
5
+ # output => the formatted to use
6
+ # backtrace => number of lines, nil = everything
7
+ guard 'bacon', :output => "BetterOutput", :backtrace => 4 do
8
+ watch(%r{^lib/dlogger/(.+)\.rb$}) { |m| "specs/lib/#{m[1]}_spec.rb" }
9
+ watch(%r{specs/.+_spec\.rb$})
10
+ end
11
+
12
+ guard 'tilt' do
13
+ watch(%r{README.md.erb})
14
+ watch(%r{examples/.*\.rb}){ "README.md.erb" }
15
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Julien Ammous
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.md ADDED
@@ -0,0 +1,182 @@
1
+ # What is this ?
2
+
3
+ I was never happy with printf style logs, you rarely know at first what you need to put in them and how to format
4
+ them so you just drop a ruby logger and log anything. Your log messages quickly become a huge mess unless you
5
+ revise all of them when the project matures and this is a real pain...
6
+
7
+ Since I discovered logstash I started to think about what I would expect from an intelligent ruby logger allowing
8
+ me to embed the actual context of the log message WITH the message and not inside it so the informations can
9
+ easily be extracted without stupid operations like regexp matching.
10
+
11
+ I first found radar which does something similar but aimed at exception catching then I recently found ruby-cabin,
12
+ after some considerations I decided to try rolling out my own, I opened a text file and in 10min I had a basic
13
+ prototype and here we are !
14
+
15
+ # Simple example
16
+
17
+ ```ruby
18
+ require 'rubygems'
19
+ require 'bundler/setup'
20
+
21
+ require 'dlogger'
22
+
23
+ logger = DLogger::Logger.new
24
+ logger.add_output( DLogger::Output::Stdout.new )
25
+
26
+ logger.log("yeah it worked")
27
+
28
+ # => yeah it worked : {}
29
+ ```
30
+
31
+
32
+ # Features
33
+
34
+ ## Contexts
35
+
36
+ You can define hash keys and their values, they will be included in any log
37
+ sent within the block.
38
+
39
+ ``` ruby
40
+ require 'rubygems'
41
+ require 'bundler/setup'
42
+
43
+ require 'dlogger'
44
+
45
+ logger = DLogger::Logger.new
46
+ logger.add_output( DLogger::Output::Stdout.new )
47
+
48
+ logger.with_context(:user_id => 32) do
49
+ logger.log("My context is with me")
50
+ end
51
+
52
+ logger.log("but not here")
53
+
54
+ # => My context is with me : {:user_id=>32}
55
+ # => but not here : {}
56
+
57
+ ```
58
+
59
+ ## Nested contexts
60
+
61
+ You can use nested contexts in this case the inner contexts have a higher priority over
62
+ the ones above if they define the same keys.
63
+
64
+ ``` ruby
65
+ require 'rubygems'
66
+ require 'bundler/setup'
67
+
68
+ require 'dlogger'
69
+
70
+ logger = DLogger::Logger.new
71
+ logger.add_output( DLogger::Output::Stdout.new )
72
+
73
+ class TimeExtension < DLogger::Extension
74
+ def time
75
+ Time.now
76
+ end
77
+ end
78
+
79
+ logger.with_context(TimeExtension) do
80
+ logger.with_context(:user_id => 32) do
81
+ logger.log("My full context is with me")
82
+ end
83
+
84
+ logger.log("I only get the time here")
85
+ end
86
+
87
+ logger.log("but still nothing here")
88
+
89
+ # => My full context is with me : {:time=>2012-01-27 11:46:49 +0100, :user_id=>32}
90
+ # => I only get the time here : {:time=>2012-01-27 11:46:49 +0100}
91
+ # => but still nothing here : {}
92
+
93
+ ```
94
+
95
+
96
+ ## Dymanic context
97
+
98
+ In some cases it may be more handy to use a class rather than a static hash, for
99
+ example if use the thread local variables to store the user, session or anything.
100
+
101
+ ``` ruby
102
+ require 'rubygems'
103
+ require 'bundler/setup'
104
+
105
+ require 'dlogger'
106
+
107
+ logger = DLogger::Logger.new
108
+ logger.add_output( DLogger::Output::Stdout.new )
109
+
110
+ # any methods defined in your extension class will become
111
+ # a key in the resulting hash (except initialize).
112
+ class UserExtension < DLogger::Extension
113
+ def user_id
114
+ rand(43)
115
+ end
116
+ end
117
+
118
+ logger.with_context(UserExtension) do
119
+ logger.log("My context is with me")
120
+ logger.log("And will change")
121
+ end
122
+
123
+ logger.log("but not here")
124
+
125
+ # => My context is with me : {:user_id=>39}
126
+ # => And will change : {:user_id=>21}
127
+ # => but not here : {}
128
+
129
+ ```
130
+
131
+ ## Threads and Fibers friendly
132
+
133
+ Whether you use threads or fibers for concurrency you are good to go, the contexts are
134
+ store with Thread.current which is local to the current fiber (thread's root fiber if not using
135
+ Fiber explicitly).
136
+
137
+ ``` ruby
138
+ require 'rubygems'
139
+ require 'bundler/setup'
140
+
141
+ require 'dlogger'
142
+
143
+ logger = DLogger::Logger.new
144
+ logger.add_output( DLogger::Output::Stdout.new )
145
+
146
+ th1 = Thread.new do
147
+ logger.with_context(:user_id => "alice", :thread_id => 1) do
148
+ logger.log("I did it !")
149
+ sleep 0.2
150
+ logger.log("completed")
151
+ end
152
+ end
153
+
154
+ th2 = Thread.new do
155
+ logger.with_context(:user_id => "bob", :thread_id => 2) do
156
+ sleep 0.1
157
+ logger.log("Done !")
158
+ end
159
+
160
+ end
161
+
162
+ [th1, th2].each(&:join)
163
+
164
+ # => I did it ! : {:user_id=>"alice", :thread_id=>1}
165
+ # => Done ! : {:user_id=>"bob", :thread_id=>2}
166
+ # => completed : {:user_id=>"alice", :thread_id=>1}
167
+
168
+ ```
169
+
170
+
171
+ # Want to contribute ?
172
+
173
+ Fork and clone the repository then:
174
+
175
+ ``` bash
176
+ $ bundle
177
+ $ bundle exec guard
178
+ ```
179
+
180
+ After this any change to a file will automatically run the associated spec file, any change
181
+ to the README.md.erb or any of the examples will trigger a rebuild of the README.md file.
182
+
data/README.md.erb ADDED
@@ -0,0 +1,79 @@
1
+ # What is this ?
2
+
3
+ I was never happy with printf style logs, you rarely know at first what you need to put in them and how to format
4
+ them so you just drop a ruby logger and log anything. Your log messages quickly become a huge mess unless you
5
+ revise all of them when the project matures and this is a real pain...
6
+
7
+ Since I discovered logstash I started to think about what I would expect from an intelligent ruby logger allowing
8
+ me to embed the actual context of the log message WITH the message and not inside it so the informations can
9
+ easily be extracted without stupid operations like regexp matching.
10
+
11
+ I first found radar which does something similar but aimed at exception catching then I recently found ruby-cabin,
12
+ after some considerations I decided to try rolling out my own, I opened a text file and in 10min I had a basic
13
+ prototype and here we are !
14
+
15
+ # Simple example
16
+
17
+ ```ruby
18
+ <%= File.read('examples/basic.rb') %>
19
+
20
+ ```
21
+
22
+
23
+ # Features
24
+
25
+ ## Contexts
26
+
27
+ You can define hash keys and their values, they will be included in any log
28
+ sent within the block.
29
+
30
+ ``` ruby
31
+ <%= File.read('examples/hash_context.rb') %>
32
+
33
+ ```
34
+
35
+ ## Nested contexts
36
+
37
+ You can use nested contexts in this case the inner contexts have a higher priority over
38
+ the ones above if they define the same keys.
39
+
40
+ ``` ruby
41
+ <%= File.read('examples/nested_context.rb') %>
42
+
43
+ ```
44
+
45
+
46
+ ## Dymanic context
47
+
48
+ In some cases it may be more handy to use a class rather than a static hash, for
49
+ example if use the thread local variables to store the user, session or anything.
50
+
51
+ ``` ruby
52
+ <%= File.read('examples/dynamic_context.rb') %>
53
+
54
+ ```
55
+
56
+ ## Threads and Fibers friendly
57
+
58
+ Whether you use threads or fibers for concurrency you are good to go, the contexts are
59
+ store with Thread.current which is local to the current fiber (thread's root fiber if not using
60
+ Fiber explicitly).
61
+
62
+ ``` ruby
63
+ <%= File.read('examples/threads.rb') %>
64
+
65
+ ```
66
+
67
+
68
+ # Want to contribute ?
69
+
70
+ Fork and clone the repository then:
71
+
72
+ ``` bash
73
+ $ bundle
74
+ $ bundle exec guard
75
+ ```
76
+
77
+ After this any change to a file will automatically run the associated spec file, any change
78
+ to the README.md.erb or any of the examples will trigger a rebuild of the README.md file.
79
+
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ task :default => :test
5
+
6
+ task :readme do
7
+ require 'tilt'
8
+ require 'erb'
9
+
10
+ Dir.chdir( File.dirname(__FILE__) ) do
11
+ template = Tilt.new('README.md.erb')
12
+ File.write('README.md', template.render)
13
+ end
14
+
15
+ end
16
+
17
+ task :test do
18
+ require 'bacon'
19
+ ENV['COVERAGE'] = "1"
20
+ Dir[File.expand_path('../specs/**/*_spec.rb', __FILE__)].each do |file|
21
+ load(file)
22
+ end
23
+
24
+ end
25
+
data/dlogger.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/dlogger/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Julien Ammous"]
6
+ gem.email = ["schmurfy@gmail.com"]
7
+ gem.description = %q{Advanced logger allowing you to include metadata with your messages}
8
+ gem.summary = %q{Dynamic logger: add a context to your log messages}
9
+ gem.homepage = ""
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "dlogger"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Dlogger::VERSION
17
+
18
+ gem.add_development_dependency('rake')
19
+ gem.add_development_dependency('tilt')
20
+ gem.add_development_dependency('guard-tilt')
21
+ end
data/examples/basic.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'dlogger'
5
+
6
+ logger = DLogger::Logger.new
7
+ logger.add_output( DLogger::Output::Stdout.new )
8
+
9
+ logger.log("yeah it worked")
10
+
11
+ # => yeah it worked : {}
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'dlogger'
5
+
6
+ logger = DLogger::Logger.new
7
+ logger.add_output( DLogger::Output::Stdout.new )
8
+
9
+ # any methods defined in your extension class will become
10
+ # a key in the resulting hash (except initialize).
11
+ class UserExtension < DLogger::Extension
12
+ def user_id
13
+ rand(43)
14
+ end
15
+ end
16
+
17
+ logger.with_context(UserExtension) do
18
+ logger.log("My context is with me")
19
+ logger.log("And will change")
20
+ end
21
+
22
+ logger.log("but not here")
23
+
24
+ # => My context is with me : {:user_id=>39}
25
+ # => And will change : {:user_id=>21}
26
+ # => but not here : {}
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'dlogger'
5
+
6
+ logger = DLogger::Logger.new
7
+ logger.add_output( DLogger::Output::Stdout.new )
8
+
9
+ logger.with_context(:user_id => 32) do
10
+ logger.log("My context is with me")
11
+ end
12
+
13
+ logger.log("but not here")
14
+
15
+ # => My context is with me : {:user_id=>32}
16
+ # => but not here : {}
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'eventmachine'
3
+ require 'yajl'
4
+
5
+ require 'bundler/setup'
6
+
7
+ require 'dlogger'
8
+ require 'dlogger/outputs/em/logstash'
9
+
10
+ logger = DLogger::Logger.new
11
+ logger.add_output( DLogger::Output::Stdout.new )
12
+
13
+ EM::run do
14
+
15
+ logger.add_output( DLogger::Output::LogStash.new('127.0.0.1', 10000) )
16
+
17
+ n = 0
18
+ EM::add_periodic_timer(2) do
19
+ logger.log("yeah it worked", :n => n+= 1)
20
+ end
21
+ end
22
+
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'dlogger'
5
+
6
+ logger = DLogger::Logger.new
7
+ logger.add_output( DLogger::Output::Stdout.new )
8
+
9
+ class TimeExtension < DLogger::Extension
10
+ def time
11
+ Time.now
12
+ end
13
+ end
14
+
15
+ logger.with_context(TimeExtension) do
16
+ logger.with_context(:user_id => 32) do
17
+ logger.log("My full context is with me")
18
+ end
19
+
20
+ logger.log("I only get the time here")
21
+ end
22
+
23
+ logger.log("but still nothing here")
24
+
25
+ # => My full context is with me : {:time=>2012-01-27 11:46:49 +0100, :user_id=>32}
26
+ # => I only get the time here : {:time=>2012-01-27 11:46:49 +0100}
27
+ # => but still nothing here : {}
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'dlogger'
5
+
6
+ logger = DLogger::Logger.new
7
+ logger.add_output( DLogger::Output::Stdout.new )
8
+
9
+ th1 = Thread.new do
10
+ logger.with_context(:user_id => "alice", :thread_id => 1) do
11
+ logger.log("I did it !")
12
+ sleep 0.2
13
+ logger.log("completed")
14
+ end
15
+ end
16
+
17
+ th2 = Thread.new do
18
+ logger.with_context(:user_id => "bob", :thread_id => 2) do
19
+ sleep 0.1
20
+ logger.log("Done !")
21
+ end
22
+
23
+ end
24
+
25
+ [th1, th2].each(&:join)
26
+
27
+ # => I did it ! : {:user_id=>"alice", :thread_id=>1}
28
+ # => Done ! : {:user_id=>"bob", :thread_id=>2}
29
+ # => completed : {:user_id=>"alice", :thread_id=>1}
data/lib/dlogger.rb ADDED
@@ -0,0 +1,8 @@
1
+ require_relative "dlogger/version"
2
+ require_relative "dlogger/extension"
3
+ require_relative "dlogger/logger"
4
+
5
+ require_relative "dlogger/outputs/base"
6
+ require_relative "dlogger/outputs/stdout"
7
+ require_relative "dlogger/outputs/stdlib_logger"
8
+
@@ -0,0 +1,24 @@
1
+ module DLogger
2
+
3
+ class Extension
4
+ ##
5
+ # Class attribute, each child class
6
+ # with have its own.
7
+ def self.properties
8
+ @properties ||= []
9
+ end
10
+
11
+ ##
12
+ # Called by ruby each time a new method is added
13
+ # to this class or any of its children
14
+ #
15
+ # @param [Symbol] m method name
16
+ #
17
+ def self.method_added(m)
18
+ unless m == :initialize
19
+ self.properties << m
20
+ end
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,134 @@
1
+ require 'thread'
2
+
3
+ module DLogger
4
+ class Logger
5
+ def initialize(name = "_default")
6
+ @name = name
7
+ @mutex = Mutex.new
8
+ @outputs = []
9
+
10
+ # initialize context
11
+ context
12
+ end
13
+
14
+ ##
15
+ # Main entry point, log a message with
16
+ # its metadata.
17
+ #
18
+ # @param [String] msg the message
19
+ # @param [Hash] metadata Additional data
20
+ #
21
+ def log(msg, metadata = {})
22
+ @mutex.synchronize do
23
+ # clearing a small hash is slightly faster than creating a new
24
+ # one each time.
25
+ merged_metadata.clear
26
+
27
+ # first load context data
28
+ context.each do |ctx_data|
29
+ case ctx_data
30
+ when Hash
31
+ merged_metadata.merge!(ctx_data)
32
+
33
+ when Extension
34
+ ctx_data.class.properties.each do |attr_name|
35
+ merged_metadata[attr_name] = ctx_data.send(attr_name)
36
+ end
37
+
38
+ end
39
+ end
40
+
41
+ # then add our data
42
+ merged_metadata.merge!(metadata)
43
+
44
+ # and dispatch the result
45
+ dispatch(msg, merged_metadata)
46
+ end
47
+ end
48
+
49
+ # Helper methods to mimic the standard ruby logger interface.
50
+ %w(debug info error warn).each do |level|
51
+ define_method(level) do |msg, metadata = {}|
52
+ log(msg, metadata.merge(:severity => level.to_sym))
53
+ end
54
+ end
55
+
56
+ ##
57
+ # Register a new output, the only requirement is that
58
+ # the object passed reponds to the "dispatch" method.
59
+ #
60
+ # @param [Object] handler the handler
61
+ #
62
+ def add_output(handler)
63
+ @outputs << handler
64
+ end
65
+
66
+ ##
67
+ # Usually called by #with_context method but you can use
68
+ # it directly to push a context on the stack, it may be useful
69
+ # for asynchronous handling or just to push a global context.
70
+ #
71
+ # @param [Hash, Dlogger::Extension] context_data additional informations
72
+ # to include in logs
73
+ #
74
+ def push_context(context_data)
75
+ if context_data.is_a?(Class)
76
+ context << context_data.new
77
+ else
78
+ context << context_data
79
+ end
80
+ end
81
+
82
+ ##
83
+ # The exact opposite of #push_context, if you called it by hand
84
+ # you can remove the context for the stack by calling this method.
85
+ def pop_context
86
+ context.pop
87
+ end
88
+
89
+ ##
90
+ # add context data for any log sent within the given block.
91
+ #
92
+ # @param [Hash, Dlogger::Extension] context_data additional informations
93
+ # to include in logs
94
+ #
95
+ def with_context(context_data)
96
+ push_context(context_data)
97
+ yield
98
+ ensure
99
+ # remove context
100
+ pop_context
101
+ end
102
+
103
+
104
+ private
105
+
106
+ ##
107
+ # Store the context in fiber local variables, each
108
+ # Thread/Fiber gets its own.
109
+ def context
110
+ Thread.current["#{@name}_dlogger_contexts"] ||= []
111
+ end
112
+
113
+ ##
114
+ # Store the temporary hash used to merge contexts.
115
+ #
116
+ def merged_metadata
117
+ Thread.current["#{@name}_dlogger_merged_metadata"] ||= {}
118
+ end
119
+
120
+ ##
121
+ # Dispatch messages to all registered outputs.
122
+ #
123
+ # @param [String] msg the log message
124
+ # @param [Hash] metadata a hash including all the
125
+ # additional informations you want to make available
126
+ #
127
+ def dispatch(msg, metadata)
128
+ @outputs.each do |out|
129
+ out.dispatch(msg, metadata)
130
+ end
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,7 @@
1
+ module DLogger
2
+ module Output
3
+ class Base
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,36 @@
1
+ gem 'eventmachine'; require 'eventmachine'
2
+ gem 'yajl-ruby'; require 'yajl'
3
+
4
+ module DLogger
5
+ module Output
6
+
7
+ module LogStashHandler
8
+ def initialize(master)
9
+ @master = master
10
+ end
11
+
12
+ def unbind
13
+ EM::add_timer(0.2){ @master.connect }
14
+ @master = nil
15
+ end
16
+ end
17
+
18
+ class LogStash < Base
19
+ def initialize(host, port)
20
+ @host = host
21
+ @port = port
22
+ connect
23
+ end
24
+
25
+ def connect
26
+ @socket = EM::connect(@host, @port, LogStashHandler, self)
27
+ end
28
+
29
+ def dispatch(msg, metadata)
30
+ metadata = metadata.merge(:message => msg)
31
+ @socket.send_data( Yajl::Encoder.encode(metadata) + "\n" )
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ module DLogger
2
+ module Output
3
+
4
+ ##
5
+ # Output data to a standard ruby logger.
6
+ class StdlibLogger < Base
7
+
8
+ ##
9
+ # @param [Logger] logger a ruby logger
10
+ #
11
+ def initialize(logger, dump_metadata = true)
12
+ @logger = logger
13
+ @dump_metadata = dump_metadata
14
+ end
15
+
16
+ ##
17
+ # @see Logger::dispatch
18
+ #
19
+ def dispatch(msg, metadata)
20
+ severity = metadata.delete(:severity) || :debug
21
+ msg = @dump_metadata ? "#{msg} : #{metadata.inspect}" : msg
22
+ @logger.send(severity, msg)
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ require 'logger'
2
+
3
+ module DLogger
4
+ module Output
5
+
6
+ ##
7
+ # Outputs data to stdout.
8
+ #
9
+ class Stdout < Base
10
+
11
+ ##
12
+ # @param [Boolean] dump_metadata if true the output will
13
+ # include the metadata dumped with inspect
14
+ #
15
+ def initialize(dump_metadata = true)
16
+ @dump_metadata = dump_metadata
17
+ end
18
+
19
+ ##
20
+ # @see Logger::dispatch
21
+ #
22
+ def dispatch(msg, metadata)
23
+ if @dump_metadata
24
+ Kernel.puts("#{msg} : #{metadata.inspect}")
25
+ else
26
+ Kernel.puts(msg)
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,3 @@
1
+ module Dlogger
2
+ VERSION = "0.0.1"
3
+ end
data/specs/common.rb ADDED
@@ -0,0 +1,52 @@
1
+
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+
5
+ if (RUBY_VERSION >= "1.9") && ENV['COVERAGE']
6
+ require 'simplecov'
7
+
8
+ puts "[[ SimpleCov enabled ]]"
9
+
10
+ SimpleCov.start do
11
+ add_filter '/specs'
12
+ end
13
+ end
14
+
15
+ require 'bacon'
16
+ require 'mocha'
17
+
18
+ $LOAD_PATH.unshift( File.expand_path('../../lib', __FILE__) )
19
+ require 'dlogger'
20
+
21
+
22
+ Bacon.summary_at_exit
23
+
24
+ module Bacon
25
+ module MochaRequirementsCounter
26
+ def self.increment
27
+ Counter[:requirements] += 1
28
+ end
29
+ end
30
+
31
+ class Context
32
+ include Mocha::API
33
+
34
+ alias_method :it_before_mocha, :it
35
+
36
+ def it(description)
37
+ it_before_mocha(description) do
38
+ # TODO: find better than that...
39
+ 1.should == 1
40
+ begin
41
+ mocha_setup
42
+ yield
43
+ mocha_verify(MochaRequirementsCounter)
44
+ rescue Mocha::ExpectationError => e
45
+ raise Error.new(:failed, "#{e.message}\n#{e.backtrace[0...10].join("\n")}")
46
+ ensure
47
+ mocha_teardown
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,28 @@
1
+ require_relative '../common'
2
+
3
+ describe 'Extension' do
4
+ should 'record methods added' do
5
+ ext_class = Class.new(DLogger::Extension) do
6
+ def user; "user"; end
7
+ def var; 34; end
8
+ end
9
+
10
+ ext_class.properties.should == [:user, :var]
11
+ end
12
+
13
+ should 'keep separate lists for each subclass' do
14
+ ext_class1 = Class.new(DLogger::Extension) do
15
+ def var1; 1; end
16
+ def var2; 2; end
17
+ end
18
+
19
+ ext_class2 = Class.new(DLogger::Extension) do
20
+ def var45; 45; end
21
+ end
22
+
23
+ ext_class1.properties.should == [:var1, :var2]
24
+ ext_class2.properties.should == [:var45]
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,76 @@
1
+ require_relative "../common"
2
+
3
+ describe "Logger" do
4
+ before do
5
+ @logger = DLogger::Logger.new
6
+ end
7
+
8
+ should "dispatch msg with its to registered outputs" do
9
+
10
+ handler = mock('Handler')
11
+ handler.expects(:dispatch).with("the message", :id => 43, :user => 'bob')
12
+
13
+ @logger.add_output(handler)
14
+
15
+ @logger.log("the message", :id => 43, :user => "bob")
16
+ end
17
+
18
+ should 'mimic standard ruby logger interface' do
19
+ @logger.expects(:dispatch).with('the message', :severity => :debug)
20
+ @logger.debug("the message")
21
+
22
+ @logger.expects(:dispatch).with('the message', :severity => :info)
23
+ @logger.info("the message")
24
+
25
+ @logger.expects(:dispatch).with('the message', :severity => :warn)
26
+ @logger.warn("the message")
27
+
28
+ @logger.expects(:dispatch).with('the message', :severity => :error)
29
+ @logger.error("the message")
30
+ end
31
+
32
+ describe 'with hash context' do
33
+ should 'dispatch msg and merged data' do
34
+ @logger.expects(:dispatch).with('msg', :id => 43, :user => 'bob')
35
+
36
+ @logger.with_context(:user => 'bob') do
37
+ @logger.log('msg', :id => 43)
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ describe 'with extension context' do
44
+ should 'dispatch msg and merged data' do
45
+ ext = Class.new(DLogger::Extension) do
46
+ def user
47
+ 'alice'
48
+ end
49
+ end
50
+
51
+ @logger.expects(:dispatch).with('msg', :id => 56, :user => 'alice')
52
+
53
+ @logger.with_context(ext) do
54
+ @logger.log('msg', :id => 56)
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ describe 'with multiple contexts' do
61
+ should 'merge the contexts in defined order (last defined has greater priority)' do
62
+ @logger.expects(:dispatch).with('msg', :operation => 'destroy', :user => 'billy')
63
+
64
+ @logger.with_context(:user => 'bob') do
65
+ @logger.with_context(:user => 'billy') do
66
+ @logger.log('msg', :operation => "destroy")
67
+ end
68
+ end
69
+
70
+ @logger.expects(:dispatch).with('some text', {})
71
+ @logger.log('some text')
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,25 @@
1
+ require_relative "../../common"
2
+
3
+ describe "Stdlib Logger" do
4
+ before do
5
+ @out = DLogger::Output::StdlibLogger.new( Logger.new($stdout) )
6
+ end
7
+
8
+ should 'honor severity level' do
9
+ Logger.any_instance.expects(:info).with('message : {}')
10
+ @out.dispatch("message", :severity => :info)
11
+ end
12
+
13
+ should 'use debug as default for severity level' do
14
+ # default = debug
15
+ Logger.any_instance.expects(:debug).with('message : {}')
16
+ @out.dispatch("message", {})
17
+ end
18
+
19
+ should 'not log metadata when asked' do
20
+ out = DLogger::Output::StdlibLogger.new( Logger.new($stdout), false )
21
+ Logger.any_instance.expects(:debug).with('message')
22
+ out.dispatch("message", {})
23
+ end
24
+
25
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "../../common"
2
+
3
+ describe "Logger" do
4
+
5
+ should 'write messages to stdout' do
6
+ out = DLogger::Output::Stdout.new
7
+ Kernel.expects(:puts).with('message : {}')
8
+ out.dispatch("message", {})
9
+ end
10
+
11
+ should 'write messages to stdout without metadata' do
12
+ out = DLogger::Output::Stdout.new(false)
13
+ Kernel.expects(:puts).with('message')
14
+ out.dispatch("message", {})
15
+ end
16
+
17
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dlogger
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Julien Ammous
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &70201269940540 !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: *70201269940540
25
+ - !ruby/object:Gem::Dependency
26
+ name: tilt
27
+ requirement: &70201269939560 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70201269939560
36
+ - !ruby/object:Gem::Dependency
37
+ name: guard-tilt
38
+ requirement: &70201269938640 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70201269938640
47
+ description: Advanced logger allowing you to include metadata with your messages
48
+ email:
49
+ - schmurfy@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - Guardfile
57
+ - LICENSE
58
+ - README.md
59
+ - README.md.erb
60
+ - Rakefile
61
+ - dlogger.gemspec
62
+ - examples/basic.rb
63
+ - examples/dynamic_context.rb
64
+ - examples/hash_context.rb
65
+ - examples/logstash.rb
66
+ - examples/nested_context.rb
67
+ - examples/threads.rb
68
+ - lib/dlogger.rb
69
+ - lib/dlogger/extension.rb
70
+ - lib/dlogger/logger.rb
71
+ - lib/dlogger/outputs/base.rb
72
+ - lib/dlogger/outputs/em/logstash.rb
73
+ - lib/dlogger/outputs/stdlib_logger.rb
74
+ - lib/dlogger/outputs/stdout.rb
75
+ - lib/dlogger/version.rb
76
+ - specs/common.rb
77
+ - specs/lib/extension_spec.rb
78
+ - specs/lib/logger_spec.rb
79
+ - specs/lib/outputs/stdlib_logger_spec.rb
80
+ - specs/lib/outputs/stdout_spec.rb
81
+ homepage: ''
82
+ licenses: []
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ segments:
94
+ - 0
95
+ hash: -1333174357779250362
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ segments:
103
+ - 0
104
+ hash: -1333174357779250362
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 1.8.15
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: ! 'Dynamic logger: add a context to your log messages'
111
+ test_files: []