dlogger 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 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: []