tagged_logger 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.markdown CHANGED
@@ -2,12 +2,48 @@
2
2
 
3
3
  ## Version 0.2.5
4
4
 
5
- * If somebody is not ready to define any logging rules, but still want to use #logger everywhere, use new _init()_ method:
5
+ * If somebody is not ready to define any logging rules, but still want to use *#logger()* everywhere:
6
6
 
7
- _TaggedLogger.init_
7
+ TaggedLogger.init
8
8
 
9
- * Runs about 3 times faster by executing code responsible for rule matching to the first _#logger()_ call and "precompiling" it.
10
- So rather than iterating over **all** rules on every _#logger_ call only subset of rules specific to _#logger()_ callee are executed.
9
+ * Runs about 3 times faster by executing code responsible for rule matching on the first *#logger()* call and "precompiling" it.
10
+ So rather than iterating over **all** rules on every _#logger_ call only subset of rules specific to *#logger()* callee get executed.
11
11
 
12
12
  * Code simplified
13
13
 
14
+ ## Version 0.3.0
15
+
16
+ * The DSL for defining rules is changed - instead of
17
+
18
+ output /A/ => ...
19
+
20
+ use one of:
21
+
22
+ debug /A/, :to => ...
23
+ info /A/, :to => ...
24
+ warn /A/, :to => ...
25
+ error /A/, :to => ...
26
+ fatal /A/, :to => ...
27
+
28
+ and instead of
29
+
30
+ output /A/ do
31
+ #...
32
+ end
33
+
34
+ use
35
+
36
+ debug /A/ do #or #info, #warn, #error, #fatal
37
+ #...
38
+ end
39
+
40
+
41
+ These rules are more specific since they name logging level explictly which makes DSL shorter,
42
+ also such form has allowed to make some optimization.
43
+
44
+ * The *#format()* could be used inside *rules* block:
45
+
46
+ TaggedLogger.rules do
47
+ format { |level, tag, msg| "#{level}-#{tag}: #{msg}\n"}
48
+ info /.*/, :to => Logger.new(STDOUT)
49
+ end
data/README.html CHANGED
@@ -11,11 +11,11 @@
11
11
 
12
12
  <h2 id='what_is_it_for'>What is it for?</h2>
13
13
 
14
- <p>Everytime you want to log something, simply write:</p>
14
+ <p>Every time you want to log something, simply write:</p>
15
15
 
16
- <pre><code>logger.debug(&quot;verbose debug information&quot;) #warn, #info, #error, #fatal also work</code></pre>
16
+ <pre><code>logger.debug(&quot;verbose debug information&quot;) #warn, #info, #error, #fatal also works</code></pre>
17
17
 
18
- <p>and do not worry about what kind of logger you use and how your code accesses it. You may confugure these things later, one day writing to STDOUT works for you, another day you&#8217;ll need something more sofisticated - like several log files each serving different components, and for different audience.</p>
18
+ <p>and do not worry about what kind of logger you use and how your code accesses it. You may configure these things later, one day writing to STDOUT works for you, another day you&#8217;ll need something more sophisticated, like several log files each serving different components and for different audience.</p>
19
19
 
20
20
  <h2 id='installation'>Installation</h2>
21
21
 
@@ -23,69 +23,108 @@
23
23
 
24
24
  <h2 id='usage'>Usage</h2>
25
25
 
26
- <p>The simplest way to turn logging on for every class whose methods calls <em>#logger</em></p>
26
+ <p>After specifying some logging rules:</p>
27
27
 
28
28
  <pre><code>TaggedLogger.rules do
29
- out_everything_to Logger.new(STDOUT)
29
+ info A, :to =&gt; Logger.new(&quot;log_for_A_class.log&quot;) #1 rule
30
+ error /.*/, :to =&gt; Logger.new(ERROR) #2 rule
31
+ info /.*/, :to =&gt; Logger.new(STDOUT) #3 rule
30
32
  end</code></pre>
31
33
 
32
- <p>From now on you may call <em>#logger</em> from anywhere, for example:</p>
34
+ <p>the following will happen:</p>
33
35
 
34
- <pre><code>class IWantToLogThis
35
- def foo
36
- logger.warn &quot;foo&quot;
37
- end
38
- end</code></pre>
36
+ <ol>
37
+ <li>
38
+ <p>The <em>#logger()</em> method becomes available <strong>everywhere</strong>, so it is completely safe to have a code like:</p>
39
+
40
+ <pre><code> class A
41
+ def foo
42
+ logger.info(&quot;Something interesting happened in A#foo&quot;) #goes to STDOUT and to &#39;log_for_A_class.log&#39; file
43
+ logger.debug(&quot;I want to see some details.&quot;) #goes nowhere
44
+ end
45
+ end
46
+ logger.error(&quot;#logger is available everywhere&quot;) #goes to STDERR
47
+ class B
48
+ logger.warn(&quot;#logger is available everywhere&quot;) #goes to STDOUT
49
+ end </code></pre>
50
+ </li>
51
+
52
+ <li>
53
+ <p>The A&#8217;s <em>logger.info()</em> output will show up in two destinations:</p>
54
+
55
+ <ul>
56
+ <li>
57
+ <p>in STDOUT, as defined by rule #3</p>
58
+ </li>
59
+
60
+ <li>
61
+ <p>in &#8216;log_for_A_class.log&#8217; file, as defined by rule #1</p>
62
+ </li>
63
+ </ul>
64
+ </li>
39
65
 
40
- <p>and after execution of:</p>
66
+ <li>
67
+ <p>From <strong>wherever</strong> it gets called from:</p>
41
68
 
42
- <pre><code>IWantToLogThis.new.foo</code></pre>
69
+ <pre><code> logger.error(&quot;ERROR&quot;) #will print &#39;ERROR&#39; in standard error
70
+ logger.info(&quot;INFO&quot;) #will print &#39;INFO&#39; in standard output
71
+ logger.debug(&quot;DEBUG&quot;) #will not print anything, since there is no &#39;debug&#39; rule</code></pre>
72
+ </li>
73
+ </ol>
43
74
 
44
- <p>you will see somewhat like:</p>
75
+ <p>The <em>#logger()</em> returns some object having methods: <em>#debug(), #info(), #warn(), #error() and #fatal()</em>. These method have generated on first <em>#logger()</em> call and contain only necessary code to meet rules. It means, for example, that if no rules defined all these methods do nothing. It is done for performance reasons, I like to log a lot and I do not want calls like <em>#logger.debug()</em> slowing down production code.</p>
45
76
 
46
- <pre><code>W, [2009-12-20T21:45:02.443524 #37166] WARN -- : IWantToLogThis: foo</code></pre>
77
+ <p>The simplest way to have a <em>#logger()</em> available everywhere without specifying any rules is:</p>
78
+
79
+ <pre><code>TaggedLogger.init</code></pre>
80
+
81
+ <p>No rules specified, therefore whenever you call <em>logger.debug()</em> (or alike) you actually paying for just an empty method execution. You may specify rules later, now you may stay focused on code you are writing.</p>
47
82
 
48
83
  <p>You may define your own formatting:</p>
49
84
 
50
85
  <pre><code>TaggedLogger.rules do
51
- out /.*/ do |level, tag, msg|
52
- puts &quot;#{level}-#{tag}: #{msg}&quot;
53
- end
86
+ format {|level, tag, message| &quot;#{level}-#{tag}: #{msg}&quot;}
54
87
  end</code></pre>
55
88
 
56
- <p>The &#8216;/.*/&#8217; says that rule is applied for every class (i.e. class name matches /.*/). The <em>level</em> and <em>msg</em> semantics should be obvious, they are severity threshold and being logged message, but <em>tag</em> is something not so obvious - it is class name whose method calls <em>#logger</em>. In example above tag is &#8220;IWantToLogThis&#8221; since <em>IWantToLogThis#foo</em> calls <em>#logger</em>. Note the <em>#logger</em> does not have <em>tag</em> parameter - tags defined automatically and they are always class names.</p>
89
+ <p>Each <em>#format()</em> call overrides previous format. If you are wondering what the heck the &#8216;tag&#8217; is - the answer is simple. The tag is a class name whose method calls <em>#logger()</em>. This is what allows to specify rules for classes or namespaces and this is what the <em>tagged_logger</em> plugin is named after.</p>
57
90
 
58
- <p>If you decided to have separate log files for classes <em>Network</em> and <em>Database</em>:</p>
91
+ <p>Lets see how you may use it. For example you want to have separate log files for classes <em>Network</em> and <em>Database</em>:</p>
59
92
 
60
93
  <pre><code>TaggedLogger.rules do
61
- out Network =&gt; Logger.new(&quot;network.log&quot;)
62
- out Database =&gt; Logger.new(&quot;database.log&quot;)
94
+ debug Network, :to =&gt; Logger.new(&quot;network.log&quot;)
95
+ debug Database, :to =&gt; Logger.new(&quot;database.log&quot;)
63
96
  end</code></pre>
64
97
 
65
98
  <p>In case you want to define common log for several classes:</p>
66
99
 
67
100
  <pre><code>TaggedLogger.rules do
68
- out [Ftp, Http, Sockets] =&gt; Logger.new(&quot;network.log&quot;)
101
+ debug [Ftp, Http, Sockets], :to =&gt; Logger.new(&quot;network.log&quot;)
69
102
  end </code></pre>
70
103
 
71
104
  <p>Or if you want to have all these classes showing up under common tag <em>Network</em> in standard output:</p>
72
105
 
73
106
  <pre><code>TaggedLogger.rules do
74
- out_everything_to Logger.new(STDOUT)
107
+ info /.*/, :to =&gt; Logger.new(STDOUT)
75
108
  rename [Ftp, Http, Sockets] =&gt; :Network
76
109
  end</code></pre>
77
110
 
78
111
  <p>You may also use regular expressions in your rules:</p>
79
112
 
80
113
  <pre><code>TaggedLogger.rules do
81
- out /Active::/ =&gt; Logger.new(&quot;active.log&quot;)
114
+ info /Active::/, :to =&gt; Logger.new(&quot;active.log&quot;)
82
115
  end</code></pre>
83
116
 
84
117
  <h2 id='license'>License</h2>
85
118
 
86
- <p>TaggedLogger is released under the MIT license.</p>
119
+ <p><em>TaggedLogger</em> is released under the MIT license.</p>
120
+
121
+ <h2 id='shortcomings'>Shortcomings</h2>
87
122
 
88
- <h2 id='shortcumings'>Shortcumings</h2>
123
+ <p>The <em>#info(), #debug(), #warn(), #error(), #fatal()</em> rules when have form like <em>:to =&gt; logger</em>, the <em>logger</em> <strong>has</strong> to be an object of standard library <em>Logger</em> class. If you need to use different sort of logger the more general rules form could is:</p>
89
124
 
90
- <p>Was not tested with <em>log4r</em></p>
125
+ <pre><code>TaggedLogger.rules do
126
+ info /Whatever/ do |level, tag, message|
127
+ #do your special logging here
128
+ end
129
+ end</code></pre>
91
130
  </body></html>
data/README.markdown CHANGED
@@ -4,7 +4,7 @@ Detaches **what** is logged from **how** it is logged.
4
4
  ## What is it for?
5
5
  Every time you want to log something, simply write:
6
6
 
7
- logger.debug("verbose debug information") #warn, #info, #error, #fatal also work
7
+ logger.debug("verbose debug information") #warn, #info, #error, #fatal also works
8
8
 
9
9
  and do not worry about what kind of logger you use and how your code accesses it.
10
10
  You may configure these things later, one day writing to STDOUT works for you, another
@@ -16,75 +16,106 @@ different components and for different audience.
16
16
 
17
17
  ## Usage
18
18
 
19
- The simplest way to turn logging on for every class whose methods calls *#logger*
20
-
19
+ After specifying some logging rules:
20
+
21
21
  TaggedLogger.rules do
22
- output_everything_to Logger.new(STDOUT)
22
+ info A, :to => Logger.new("log_for_A_class.log") #1 rule
23
+ error /.*/, :to => Logger.new(ERROR) #2 rule
24
+ info /.*/, :to => Logger.new(STDOUT) #3 rule
23
25
  end
24
26
 
25
- From now on you may call *#logger* from anywhere, for example:
26
-
27
- class IWantToLogThis
28
- def foo
29
- logger.warn "foo"
30
- end
31
- end
27
+ the following will happen:
28
+
29
+ 1. The *#logger()* method becomes available **everywhere**, so it is completely safe to have a code like:
30
+
31
+ class A
32
+ def foo
33
+ logger.info("Something interesting happened in A#foo") #goes to STDOUT and to 'log_for_A_class.log' file
34
+ logger.debug("I want to see some details.") #goes nowhere
35
+ end
36
+ end
37
+ logger.error("#logger is available everywhere") #goes to STDERR
38
+ class B
39
+ logger.warn("#logger is available everywhere") #goes to STDOUT
40
+ end
41
+
42
+ 2. The A's *logger.info()* output will show up in two destinations:
43
+
44
+ - in STDOUT, as defined by rule #3
45
+
46
+ - in 'log_for_A_class.log' file, as defined by rule #1
32
47
 
33
- and after execution of:
48
+ 3. From **wherever** it gets called from:
34
49
 
35
- IWantToLogThis.new.foo
50
+ logger.error("ERROR") #will print 'ERROR' in standard error
51
+ logger.info("INFO") #will print 'INFO' in standard output
52
+ logger.debug("DEBUG") #will not print anything, since there is no 'debug' rule
36
53
 
37
- you will see somewhat like:
38
54
 
39
- W, [2009-12-20T21:45:02.443524 #37166] WARN -- : IWantToLogThis: foo
55
+ The *#logger()* returns some object having methods: *#debug(), #info(), #warn(), #error() and #fatal()*.
56
+ These methods have generated on first *#logger()* call and contain only necessary code to meet rules.
57
+ It means, for example, that if no rules defined all these methods do nothing. It is done for performance
58
+ reasons, I like to log a lot and I do not want calls like *#logger.debug()* slowing down production code.
59
+
60
+
61
+ The simplest way to have a *#logger()* available everywhere without specifying any rules is:
62
+
63
+ TaggedLogger.init
64
+
65
+ No rules specified, therefore whenever you call *logger.debug()* (or alike) you actually paying for just an empty method
66
+ execution. You may specify rules later, now you may stay focused on code you are writing.
40
67
 
41
68
 
42
69
  You may define your own formatting:
43
70
 
44
71
  TaggedLogger.rules do
45
- output /.*/ do |level, tag, msg|
46
- puts "#{level}-#{tag}: #{msg}"
47
- end
72
+ format {|level, tag, message| "#{level}-#{tag}: #{msg}"}
48
73
  end
49
74
 
50
- The '/.\*/' says that rule is applied for every class (i.e. class name matches /.\*/).
51
- The *level* and *msg* semantics should be obvious, they are severity threshold
52
- and being logged message, but *tag* is something not so obvious - it is class name whose
53
- method calls *#logger*. In example above tag is "IWantToLogThis" since *IWantToLogThis#foo*
54
- calls *#logger*. Note the *#logger* does not have *tag* parameter - tags defined automatically
55
- and they are always class names.
56
-
75
+ Each *#format()* call overrides previous format.
76
+ If you are wondering what the heck the 'tag' is - the answer is simple. The tag is a class name
77
+ whose method calls *#logger()*. This is what allows to specify rules for classes or namespaces and this
78
+ is what the *tagged_logger* plugin is named after.
57
79
 
58
- If you decided to have separate log files for classes *Network* and *Database*:
80
+ Lets see how you may use it. For example you want to have separate log files
81
+ for classes *Network* and *Database*:
59
82
 
60
83
  TaggedLogger.rules do
61
- output Network => Logger.new("network.log")
62
- output Database => Logger.new("database.log")
84
+ debug Network, :to => Logger.new("network.log")
85
+ debug Database, :to => Logger.new("database.log")
63
86
  end
64
87
 
65
88
  In case you want to define common log for several classes:
66
89
 
67
90
  TaggedLogger.rules do
68
- output [Ftp, Http, Sockets] => Logger.new("network.log")
91
+ debug [Ftp, Http, Sockets], :to => Logger.new("network.log")
69
92
  end
70
93
 
71
94
  Or if you want to have all these classes showing up under common
72
95
  tag *Network* in standard output:
73
96
 
74
97
  TaggedLogger.rules do
75
- output_everything_to Logger.new(STDOUT)
98
+ info /.*/, :to => Logger.new(STDOUT)
76
99
  rename [Ftp, Http, Sockets] => :Network
77
100
  end
78
101
 
79
102
  You may also use regular expressions in your rules:
80
103
 
81
104
  TaggedLogger.rules do
82
- output /Active::/ => Logger.new("active.log")
105
+ info /Active::/, :to => Logger.new("active.log")
83
106
  end
84
107
 
85
108
 
86
109
  ## License
87
- TaggedLogger is released under the MIT license.
110
+ *TaggedLogger* is released under the MIT license.
88
111
 
89
112
  ## Shortcomings
90
- Was not tested with *log4r*
113
+ The *#info(), #debug(), #warn(), #error(), #fatal()* rules when having form like *:to => logger*, the
114
+ *logger* **has** to be an object of standard library *Logger* class. If you need to use different sort of logger
115
+ the more general rules form is:
116
+
117
+ TaggedLogger.rules do
118
+ info /Whatever/ do |level, tag, message|
119
+ #do your special logging here
120
+ end
121
+ end
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require 'rake/rdoctask'
4
4
 
5
5
  Rake::TestTask.new do |t|
6
6
  t.libs << "test"
7
- t.test_files = FileList['test/test.rb']
7
+ t.test_files = FileList['test/test.rb', 'test/test_examples.rb']
8
8
  t.verbose = true
9
9
  end
10
10
 
@@ -16,7 +16,7 @@ begin
16
16
  s.email = "fkocherga@gmail.com"
17
17
  s.homepage = "http://github.com/fkocherga/tagged_logger"
18
18
  s.authors = ["Fedor Kocherga"]
19
- s.test_files = ['test/test.rb']
19
+ s.test_files = ['test/test.rb', 'test/test_examples.rb']
20
20
  end
21
21
  Jeweler::GemcutterTasks.new
22
22
  rescue LoadError
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.5
1
+ 0.3.0
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'tagged_logger'
3
+ require 'logger'
4
+
5
+ puts "\n<<<#{File.basename(__FILE__, ".rb")}>>> \n".upcase
6
+
7
+ TaggedLogger.rules do
8
+ format { |level, tag, msg| "#{level}-#{tag}: #{msg}\n"}
9
+ info /.*/, :to => Logger.new(STDOUT)
10
+ end
11
+
12
+ logger.info("message")
@@ -2,18 +2,36 @@ require 'rubygems'
2
2
  require 'tagged_logger'
3
3
  require 'logger'
4
4
 
5
+ puts "\n<<<#{File.basename(__FILE__, ".rb")}>>> \n".upcase
6
+
5
7
  class LogFoo
6
8
  def foo
7
- logger.info("foo")
9
+ logger.info("#{self.class}#foo")
8
10
  end
9
11
  end
10
12
 
11
- Ftp = Class.new LogFoo
12
- Http = Class.new LogFoo
13
- Sockets = Class.new LogFoo
13
+ module Network
14
+ Ftp = Class.new LogFoo
15
+ Http = Class.new LogFoo
16
+ Sockets = Class.new LogFoo
17
+ end
18
+ include Network
19
+
20
+ def run
21
+ [Network::Ftp, Network::Http, Network::Sockets, LogFoo].each { |c| c.new.foo }
22
+ end
14
23
 
15
24
  TaggedLogger.rules do
16
- output [Ftp, Http, Sockets] => Logger.new(open("network.log", "w"))
25
+ stdout = Logger.new(STDOUT)
26
+ puts "- Only logging from within classes Ftp, Http and Sockets will be shown in output (no LogFoo):"
27
+ info [Ftp, Http, Sockets], :to => stdout
28
+ run
29
+ puts "\n- Same result, but regex syntax:"
30
+ info /(Ftp|Http|Sockets)/, :to => stdout
31
+ run
32
+ puts "\n- Or even shorter:"
33
+ info /Network::/, :to => stdout
34
+ run
17
35
  end
18
36
 
19
- [Ftp, Http, Sockets].each { |c| c.new.foo }
37
+
@@ -2,20 +2,25 @@ require 'rubygems'
2
2
  require 'tagged_logger'
3
3
  require 'logger'
4
4
 
5
- module LogFoo
5
+ puts "\n<<<#{File.basename(__FILE__, ".rb")}>>> \n".upcase
6
+
7
+ class LogFoo
6
8
  def foo
7
9
  logger.info("#{self.class}#foo")
8
10
  end
9
11
  end
10
12
 
11
- Ftp = Class.new { include LogFoo }
12
- Http = Class.new { include LogFoo }
13
- Sockets = Class.new { include LogFoo }
13
+ Ftp = Class.new LogFoo
14
+ Http = Class.new LogFoo
15
+ Sockets = Class.new LogFoo
14
16
 
15
17
  TaggedLogger.rules do
18
+ format { |level, tag, message| "#{tag}: #{message}\n"}
19
+ puts "- Only logging from within classes Ftp, Http and Sockets will be shown in output (no LogFoo)"
20
+ puts " tag is also printed and it is 'Network' after renaming took place:"
16
21
  rename [Ftp, Http, Sockets] => :Network
17
- output :Network => Logger.new(STDOUT)
22
+ info :Network, :to => Logger.new(STDOUT)
18
23
  end
19
24
 
20
- [Ftp, Http, Sockets].each { |c| c.new.foo }
25
+ [Ftp, Http, Sockets, LogFoo].each { |c| c.new.foo }
21
26
 
@@ -2,19 +2,22 @@ require 'rubygems'
2
2
  require 'tagged_logger'
3
3
  require 'logger'
4
4
 
5
- class LogFoo
5
+ puts "\n<<<#{File.basename(__FILE__, ".rb")}>>> \n".upcase
6
+
7
+ class Some
6
8
  def foo
7
- logger.info("foo")
9
+ logger.info("Some information")
8
10
  end
9
11
  end
10
12
 
11
- Database = Class.new LogFoo
12
- Network = Class.new LogFoo
13
13
 
14
14
  TaggedLogger.rules do
15
- output Network => Logger.new(open("network.log", "w"))
16
- output Database => Logger.new(open("database.log", "w"))
15
+ format { |level, tag, message| "#{tag}: #{message}\n"}
16
+ puts "- Only logging for Some class is shown in STDOUT"
17
+ puts "- Run this script again by adding '2>/dev/null' and see the difference:"
18
+ info /.*/, :to => Logger.new(STDERR)
19
+ info Some, :to => Logger.new(STDOUT)
17
20
  end
18
21
 
19
- Database.new.foo
20
- Network.new.foo
22
+ Some.new.foo
23
+ logger.info "INFO"
@@ -2,6 +2,8 @@ require 'rubygems'
2
2
  require 'tagged_logger'
3
3
  require 'logger'
4
4
 
5
+ puts "\n<<<#{File.basename(__FILE__, ".rb")}>>> \n".upcase
6
+
5
7
  module Active
6
8
  class Base
7
9
  def foo
@@ -11,7 +13,8 @@ module Active
11
13
  end
12
14
 
13
15
  TaggedLogger.rules do
14
- output /Active::/ => Logger.new(open("active.log", "w"))
16
+ puts "- The recommended way to specify logging for all classes within same namespace:"
17
+ info /Active::/, :to => Logger.new(STDOUT)
15
18
  end
16
19
 
17
20
  Active::Base.new.foo
@@ -2,17 +2,18 @@ require 'rubygems'
2
2
  require 'tagged_logger'
3
3
  require 'logger'
4
4
 
5
+ puts "\n<<<#{File.basename(__FILE__, ".rb")}>>> \n".upcase
6
+
5
7
  TaggedLogger.rules do
6
- output_everything_to Logger.new(STDOUT)
8
+ info /.*/, :to => Logger.new(STDOUT)
7
9
  end
8
10
 
9
11
  class A
10
12
  def foo
11
- logger.warn "foo"
13
+ logger.warn "WARNING message" #will be printed since treshold is set to 'info'
12
14
  end
13
- logger.debug "A parsed"
15
+ logger.debug "DEBUG message" #will not be printed since we have only 'info' rule
14
16
  end
15
17
 
16
-
17
- logger.info("Hello word!")
18
+ logger.info("INFO message") #will be printed
18
19
  A.new.foo
data/lib/tagged_logger.rb CHANGED
@@ -10,16 +10,18 @@ class TaggedLogger
10
10
  @rename_rules = Dictionary.new
11
11
  @tag_blocks = Dictionary.new
12
12
  ObjectSpace.each_object(ClassSpecificLogger) { |obj| obj.detach }
13
+ init
13
14
  end
14
15
 
15
16
  def rules(&block)
17
+ add_logger_generator_in_Kernel
16
18
  instance_eval(&block)
17
19
  end
18
20
 
19
- def blocks_for(tag)
21
+ def blocks_for(level, tag)
20
22
  blocks = []
21
23
  tag_aliases(tag) do |tag_alias|
22
- tag_blocks(tag_alias) do |tag_block|
24
+ tag_blocks(level, tag_alias) do |tag_block|
23
25
  blocks << [tag_alias, tag_block]
24
26
  end
25
27
  end
@@ -27,46 +29,63 @@ class TaggedLogger
27
29
  end
28
30
 
29
31
  def init
30
- add_logger_generator_in_Kernel
32
+ rules {}
31
33
  end
32
34
 
33
- def output_everything_to(logger = nil, &block)
34
- if logger && block
35
- raise ArgumentError, "Either block or logger has to be specified, but not both."
36
- end
37
- output /.*/ => logger if logger
38
- output &block if block
39
- end
35
+ def debug(what, where = {}, &block) output(:debug, what, where, &block) end
36
+ def info(what, where = {}, &block) output(:info, what, where, &block) end
37
+ def warn(what, where = {}, &block) output(:wanr, what, where, &block) end
38
+ def error(what, where = {}, &block) output(:error, what, where, &block) end
39
+ def fatal(what, where = {}, &block) output(:fatal, what, where, &block) end
40
40
 
41
- def output(spec, &block)
42
- add_logger_generator_in_Kernel
43
- if spec.is_a? Hash
44
- spec.each do |what, where|
45
- @tag_blocks[tag_matcher(what)] = lambda { |level, tag, msg | where.send(level, "#{tag}: #{msg}\n") }
46
- end
47
- raise ArgumentError "No block should be specified in this 'output' rule" if block
48
- else
49
- raise ArgumentError "Block has to be specified in this 'output' rule" unless block
50
- @tag_blocks[tag_matcher(spec)] = block
51
- end
41
+ def format(&block)
42
+ @formatter = block
52
43
  end
53
-
44
+
45
+ def formatter
46
+ @formatter = lambda { |level, tag, message| "#{message}\n"} unless @formatter
47
+ @formatter
48
+ end
49
+
54
50
  def rename(renames)
55
51
  renames.each { |from, to| @rename_rules[tag_matcher(from)] = to }
56
52
  end
57
53
 
58
54
  private
55
+ def output(level, what, where, &block)
56
+ logger = where[:to]
57
+ code = nil
58
+ if logger
59
+ #todo: hack - what about other logger classes?
60
+ logger.formatter = lambda {|severity, datetime, progname, msg| "#{msg}"} if logger.is_a? Logger
61
+ write = lambda { |level, tag, msg | logger.send(level, formatter.call(level, tag, msg)) }
62
+ code = block ? lambda { |l,t,m| write.call(l,t,m); block.call(l,t,m) } : write
63
+ elsif block
64
+ code = block
65
+ else
66
+ raise ArgumentError "Should be called with block or :to => <logger>"
67
+ end
68
+ @tag_blocks[tag_matcher(what, level)] = code
69
+ end
70
+
59
71
  class TagMatcher
60
72
  attr_reader :match_spec
73
+ LEVELS = { :debug => 1, :info => 2, :warn => 3, :error => 4, :fatal => 5}
61
74
 
62
- def initialize(match_spec)
75
+ def initialize(match_spec, level = nil)
76
+ @level = level || :debug
63
77
  @match_spec = match_spec
64
78
  end
65
79
 
66
- def match?(tag)
80
+ def match?(tag, level = nil)
81
+ return false if level && !above_treshold(level)
67
82
  self.class.match?(@match_spec, tag)
68
83
  end
69
84
 
85
+ def above_treshold(level)
86
+ LEVELS[@level] <= LEVELS[level]
87
+ end
88
+
70
89
  def self.match?(spec, tag)
71
90
  t = tag.to_s
72
91
  case spec
@@ -82,8 +101,8 @@ class TaggedLogger
82
101
  end
83
102
  end #TagMatcher
84
103
 
85
- def tag_matcher(tag)
86
- TagMatcher.new(tag)
104
+ def tag_matcher(tag, level = nil)
105
+ TagMatcher.new(tag, level)
87
106
  end
88
107
 
89
108
  def tag_aliases(tag, &block)
@@ -92,9 +111,9 @@ class TaggedLogger
92
111
  block.call(current_name)
93
112
  end
94
113
 
95
- def tag_blocks(tag, &block)
114
+ def tag_blocks(level, tag, &block)
96
115
  @tag_blocks.each do |matcher, block|
97
- yield block if matcher.match? tag
116
+ yield block if matcher.match?(tag, level)
98
117
  end
99
118
  end
100
119
 
@@ -122,10 +141,10 @@ class TaggedLogger
122
141
 
123
142
  def initialize(klass)
124
143
  @klass = klass
125
- blocks = TaggedLogger.blocks_for(klass.to_s)
126
- [:debug, :warn, :info, :error, :fatal].each do |method|
127
- eigenclass.send(:define_method, method) do |msg|
128
- blocks.each { |(tag_alias, block)| block.call(method, tag_alias, msg) }
144
+ [:debug, :warn, :info, :error, :fatal].each do |level|
145
+ blocks = TaggedLogger.blocks_for(level, klass.to_s)
146
+ eigenclass.send(:define_method, level) do |msg|
147
+ blocks.each { |(tag_alias, block)| block.call(level, tag_alias, msg) }
129
148
  end
130
149
  end
131
150
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{tagged_logger}
8
- s.version = "0.2.5"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Fedor Kocherga"]
12
- s.date = %q{2010-01-13}
12
+ s.date = %q{2010-01-16}
13
13
  s.email = %q{fkocherga@gmail.com}
14
14
  s.extra_rdoc_files = [
15
15
  "README.html",
@@ -22,16 +22,18 @@ Gem::Specification.new do |s|
22
22
  "README.markdown",
23
23
  "Rakefile",
24
24
  "VERSION",
25
+ "examples/changing_format.rb",
25
26
  "examples/one_log_per_classes.rb",
26
27
  "examples/one_tag_per_classes.rb",
27
28
  "examples/per_class_customization.rb",
28
29
  "examples/rule_with_regex.rb",
29
- "examples/simplest_customization.rb",
30
30
  "examples/simplest_usage.rb",
31
31
  "lib/tagged_logger.rb",
32
32
  "tagged_logger.gemspec",
33
33
  "tagged_logger.rb",
34
+ "test/expected_examples_output.txt",
34
35
  "test/test.rb",
36
+ "test/test_examples.rb",
35
37
  "todo.txt"
36
38
  ]
37
39
  s.homepage = %q{http://github.com/fkocherga/tagged_logger}
@@ -40,7 +42,8 @@ Gem::Specification.new do |s|
40
42
  s.rubygems_version = %q{1.3.5}
41
43
  s.summary = %q{Detaches _what_ is logged from _how_ it is logged.}
42
44
  s.test_files = [
43
- "test/test.rb"
45
+ "test/test.rb",
46
+ "test/test_examples.rb"
44
47
  ]
45
48
 
46
49
  if s.respond_to? :specification_version then
@@ -0,0 +1,39 @@
1
+
2
+ <<<CHANGING_FORMAT>>>
3
+ info-Object: message
4
+
5
+ <<<ONE_LOG_PER_CLASSES>>>
6
+ - Only logging from within classes Ftp, Http and Sockets will be shown in output (no LogFoo):
7
+ Network::Ftp#foo
8
+ Network::Http#foo
9
+ Network::Sockets#foo
10
+
11
+ - Same result, but regex syntax:
12
+ Network::Ftp#foo
13
+ Network::Http#foo
14
+ Network::Sockets#foo
15
+
16
+ - Or even shorter:
17
+ Network::Ftp#foo
18
+ Network::Http#foo
19
+ Network::Sockets#foo
20
+
21
+ <<<ONE_TAG_PER_CLASSES>>>
22
+ - Only logging from within classes Ftp, Http and Sockets will be shown in output (no LogFoo)
23
+ tag is also printed and it is 'Network' after renaming took place:
24
+ Network: Ftp#foo
25
+ Network: Http#foo
26
+ Network: Sockets#foo
27
+
28
+ <<<PER_CLASS_CUSTOMIZATION>>>
29
+ - Only logging for Some class is shown in STDOUT
30
+ - Run this script again by adding '2>/dev/null' and see the difference:
31
+ Some: Some information
32
+
33
+ <<<RULE_WITH_REGEX>>>
34
+ - The recommended way to specify logging for all classes within same namespace:
35
+ foo
36
+
37
+ <<<SIMPLEST_USAGE>>>
38
+ INFO message
39
+ WARNING message
data/test/test.rb CHANGED
@@ -18,134 +18,140 @@ end
18
18
  class TaggedLoggerTest < Test::Unit::TestCase
19
19
  include RR::Adapters::TestUnit
20
20
 
21
-
22
- context "@standard_logger with stub output @out1;" do
21
+ context "stub output device @@stub_out;" do
23
22
  setup do
24
- @out1 = TestLogDevice.new
25
- @standard_logger = Logger.new(@out1)
26
- @formatter = lambda {|severity, datetime, progname, msg| "#{msg}"}
27
- @standard_logger.formatter = @formatter
23
+ @@stub_out = TestLogDevice.new
28
24
  end
29
25
 
30
26
  teardown do
31
- Kernel.class_eval do
32
- remove_method :logger
33
- end
27
+ Kernel.class_eval { remove_method :logger }
34
28
  end
35
29
 
36
- should "be able to intialize with minimal effort" do
30
+ should "be able to intialize with minimal effort, without defining any rules" do
37
31
  TaggedLogger.init
38
- dont_allow(@out1).write
32
+ dont_allow(@@stub_out).write
39
33
  assert Class.new.methods.include? "logger"
40
34
  assert_nothing_raised { Class.new.logger }
41
35
  end
42
36
 
43
- context "everything gets logged to @out1;" do
37
+ context "everything gets logged to @@stub_out;" do
44
38
  setup do
45
- logger = @standard_logger
46
39
  TaggedLogger.rules do
47
40
  reset
48
- output_everything_to logger
41
+ format {|level, tag, msg| "#{tag}: #{msg}"}
42
+ debug /.*/, :to => Logger.new(@@stub_out)
49
43
  end
50
44
  end
51
45
 
52
- should " be possible to log by .logger.debug/info/warn/error/fatal()" do
46
+ should "write output for every of #debug, #info, #warn, #error, #fatal call" do
53
47
  NewClass = Class.new
54
48
  obj = NewClass.new
55
- %w[debug info warn error fatal].each do |method|
56
- assert obj.logger.methods.include? method
57
- obj.logger.send(method.intern, method)
58
- assert_equal "#{obj.class}: #{method}\n", @out1.to_s
59
- end
49
+ TaggedLogger.rules { format { |level, tag, msg| "#{msg}" } }
50
+
51
+ mock(@@stub_out).write("debug")
52
+ obj.logger.debug("debug")
53
+ mock(@@stub_out).write("info")
54
+ obj.logger.info("info")
55
+ mock(@@stub_out).write("warn")
56
+ obj.logger.warn("warn")
57
+ mock(@@stub_out).write("error")
58
+ obj.logger.error("error")
59
+ mock(@@stub_out).write("fatal")
60
+ obj.logger.fatal("fatal")
60
61
  end
61
62
 
62
- context "class A and class B with logging;" do
63
+ context "class A and class B with logger.info() in their method #foo;" do
63
64
  setup do
64
- module Foo
65
- def foo
66
- logger.info("foo")
67
- end
68
- end
65
+ module Foo; def foo; logger.info("foo"); end; end
69
66
  class A;include Foo; end
70
67
  class B;include Foo; end
71
68
  @a = A.new
72
69
  @b = B.new
73
70
  end
74
-
71
+
75
72
  should "be possible to replace tags for A, B classes with single tag TEST making rules for A and B obsolete" do
76
73
  TaggedLogger.rules do
77
74
  rename [A,B] => :TEST
78
75
  end
79
-
76
+ mock(@@stub_out).write("TEST: foo")
80
77
  @a.foo
81
- assert_equal "TEST: foo\n", @out1.to_s
82
- @out1.clear
83
-
78
+ mock(@@stub_out).write("TEST: foo")
84
79
  @b.foo
85
- assert_equal "TEST: foo\n", @out1.to_s
86
80
  end
87
81
 
88
- context "@logger2 with stub output @out2;" do
89
- setup do
90
- @out2 = TestLogDevice.new
91
- @logger2 = Logger.new(@out2)
92
- @logger2.formatter = @formatter
82
+ context "@logger2 with stub output @@stub_out_aux;" do
83
+ setup { @@stub_out_aux = TestLogDevice.new }
84
+
85
+ should "be possible to add logging to @@stub_out_aux for A" do
86
+ TaggedLogger.rules { debug A, :to => Logger.new(@@stub_out_aux) }
87
+ mock(@@stub_out).write("#{self.class}::A: foo")
88
+ mock(@@stub_out_aux).write("#{self.class}::A: foo")
89
+ @a.foo
90
+ mock(@@stub_out).write("#{self.class}::B: foo")
91
+ dont_allow(@@stub_out_aux).write
92
+ @b.foo
93
93
  end
94
94
 
95
- should "be possible to speialize logging for tag A by specifying another @logger2" do
96
- logger2 = @logger2
95
+ should "be possible to speialize logging for tag A by providing block" do
97
96
  TaggedLogger.rules do
98
- output A => logger2
97
+ debug A do |level, tag, msg|
98
+ @@stub_out_aux.write("#{level} #{tag} #{msg}")
99
+ end
99
100
  end
101
+ mock(@@stub_out_aux).write("info #{self.class}::A foo")
102
+ mock(@@stub_out).write("#{self.class}::A: foo")
100
103
  @a.foo
101
- assert_equal "#{self.class}::A: foo\n", @out1.to_s
102
- assert_equal "#{self.class}::A: foo\n", @out2.to_s
103
- @out2.clear
104
- @b.foo
105
- assert_equal "#{self.class}::B: foo\n", @out1.to_s
106
- assert_equal "", @out2.to_s
107
104
  end
108
-
109
- should "be possible to speialize logging for tag A by providing block" do
110
- logger2 = @logger2
105
+
106
+ should "be possible to provide block and logger together, and do actual writing by yielding" do
111
107
  TaggedLogger.rules do
112
- output A do |level, tag, msg|
113
- logger2.send(level, msg)
108
+ debug A, :to => Logger.new(@@stub_out_aux) do |level, tag, message|
109
+ @@stub_out_aux.write "post write callback"
114
110
  end
115
111
  end
112
+ mock(@@stub_out_aux).write("#{self.class}::A: foo")
113
+ mock(@@stub_out_aux).write("post write callback")
114
+ mock(@@stub_out).write("#{self.class}::A: foo")
116
115
  @a.foo
117
- assert_equal "#{self.class}::A: foo\n", @out1.to_s
118
- assert_equal "foo", @out2.to_s
119
- @out2.clear
120
- @b.foo
121
- assert_equal "#{self.class}::B: foo\n", @out1.to_s
122
- assert_equal "", @out2.to_s
123
116
  end
124
117
 
125
118
  should "be possible to replace tags for A, B classes with single tag TEST and specialize logging for it" do
126
- logger2 = @logger2
127
119
  TaggedLogger.rules do
128
120
  rename [A, B] => :TEST
129
- output :TEST => logger2
121
+ debug :TEST, :to => Logger.new(@@stub_out_aux)
130
122
  end
123
+ mock(@@stub_out).write("TEST: foo")
124
+ mock(@@stub_out_aux).write("TEST: foo")
131
125
  @a.foo
132
- assert_equal "TEST: foo\n", @out1.to_s
133
- assert_equal "TEST: foo\n", @out2.to_s
134
- @out1.clear
135
- @out2.clear
126
+ mock(@@stub_out).write("TEST: foo")
127
+ mock(@@stub_out_aux).write("TEST: foo")
136
128
  @b.foo
137
- assert_equal "TEST: foo\n", @out1.to_s
138
- assert_equal "TEST: foo\n", @out2.to_s
139
129
  end
140
130
 
141
131
  should "use default tag equal to class name for class methods" do
142
132
  def A.bar
143
133
  logger.info "bar"
144
134
  end
135
+ mock(@@stub_out).write("#{self.class}::A: bar")
145
136
  A.bar
146
- assert_equal "#{self.class}::A: bar\n", @out1.to_s
147
137
  end
148
- end
138
+
139
+ context "class A and class B with logger.debug() in their method #bar;" do
140
+ setup do
141
+ module Bar; def bar; logger.debug("bar"); end; end
142
+ class A;include Bar; end
143
+ class B;include Bar; end
144
+ end
145
+
146
+ should "not print debug messages if info level specified" do
147
+ TaggedLogger.rules do
148
+ info /A/, :to => Logger.new(@@stub_out_aux)
149
+ end
150
+ dont_allow(@@stub_out_aux).write
151
+ @a.bar
152
+ end
153
+ end
154
+ end
149
155
 
150
156
  end
151
157
  end
@@ -0,0 +1,8 @@
1
+ root_dir = File.dirname(File.expand_path(__FILE__)) + '/../'
2
+ examples_output = `find -s #{root_dir}examples -iname '*.rb' -exec ruby {} \\;`
3
+ expected_output = File.read("#{root_dir}test/expected_examples_output.txt")
4
+ if expected_output != examples_output
5
+ puts "FAILED: Examples do not produce same output as expected.\n"
6
+ raise StandardError, "FAILED"
7
+ end
8
+ puts "SUCCEEDED.\n"
data/todo.txt CHANGED
@@ -1,21 +1,7 @@
1
- - It is slow: logger on first call has to select only applicable rules and
2
- run only them afterwards rather than going via all rules each time. It will
3
- gain almost 300% in parsing speed on my app.
4
-
5
- - Replace output ... => ... with
6
- format ...
7
- output ... => do
8
- format ....
9
- end
10
-
11
- - output ... => ... - may be:
12
- debug ... => ...
13
- info ... => ...
14
-
15
- - Simplest configuration with warning
16
-
17
- - Switch tests on rr expectations
18
-
1
+ - logger.debug method (and alike) should accept block for performance reason
19
2
  - logger called within Base does not log when Derived is instantiated
20
-
21
3
  - writing messages in any encoding
4
+ - test against 'logging' framework
5
+ - test agains 1.8.6 and 1.9.1
6
+ - test with Rails
7
+ - when logger specified in :to => ... then lib must complain unless formatting and logging code to that specific logger class was not defined
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tagged_logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fedor Kocherga
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-13 00:00:00 -06:00
12
+ date: 2010-01-16 00:00:00 -06:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -29,16 +29,18 @@ files:
29
29
  - README.markdown
30
30
  - Rakefile
31
31
  - VERSION
32
+ - examples/changing_format.rb
32
33
  - examples/one_log_per_classes.rb
33
34
  - examples/one_tag_per_classes.rb
34
35
  - examples/per_class_customization.rb
35
36
  - examples/rule_with_regex.rb
36
- - examples/simplest_customization.rb
37
37
  - examples/simplest_usage.rb
38
38
  - lib/tagged_logger.rb
39
39
  - tagged_logger.gemspec
40
40
  - tagged_logger.rb
41
+ - test/expected_examples_output.txt
41
42
  - test/test.rb
43
+ - test/test_examples.rb
42
44
  - todo.txt
43
45
  - README.html
44
46
  has_rdoc: true
@@ -71,3 +73,4 @@ specification_version: 3
71
73
  summary: Detaches _what_ is logged from _how_ it is logged.
72
74
  test_files:
73
75
  - test/test.rb
76
+ - test/test_examples.rb
@@ -1,10 +0,0 @@
1
- require 'rubygems'
2
- require 'tagged_logger'
3
-
4
- TaggedLogger.rules do
5
- output /.*/ do |level, tag, msg|
6
- puts "#{level}-#{tag}: #{msg}"
7
- end
8
- end
9
-
10
- logger.info("message")