loggability 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.rdoc ADDED
@@ -0,0 +1,4 @@
1
+ == v0.0.1 [2012-04-06] Michael Granger <ged@FaerieMUD.org>
2
+
3
+ First release.
4
+
data/Manifest.txt ADDED
@@ -0,0 +1,19 @@
1
+ ChangeLog
2
+ History.rdoc
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ bin/loggability
7
+ lib/loggability.rb
8
+ lib/loggability/constants.rb
9
+ lib/loggability/formatter.rb
10
+ lib/loggability/formatter/color.rb
11
+ lib/loggability/formatter/default.rb
12
+ lib/loggability/formatter/html.rb
13
+ lib/loggability/logger.rb
14
+ spec/lib/helpers.rb
15
+ spec/loggability/formatter/color_spec.rb
16
+ spec/loggability/formatter/html_spec.rb
17
+ spec/loggability/formatter_spec.rb
18
+ spec/loggability/logger_spec.rb
19
+ spec/loggability_spec.rb
data/README.rdoc ADDED
@@ -0,0 +1,200 @@
1
+ = loggability
2
+
3
+ home :: http://deveiate.org/projects/loggability
4
+ code :: http://bitbucket.org/ged/loggability
5
+ docs :: http://deveiate.org/code/loggability
6
+ github :: http://github.com/ged/loggability
7
+
8
+
9
+ == Description
10
+
11
+ A composable logging system built on the standard Logger library.
12
+
13
+ You can add Loggability to large libraries and systems, then hook everything
14
+ up later when you know where you want logs to be written, at what level of
15
+ severity, and in which format.
16
+
17
+ An example:
18
+
19
+ # Load a bunch of libraries
20
+ require 'strelka'
21
+ require 'inversion'
22
+ require 'treequel'
23
+ require 'loggability'
24
+
25
+ # Now tell everything that's using Loggability to log to an HTML
26
+ # log file at INFO level
27
+ Loggability.write_to( '/usr/local/www/htdocs/log.html' )
28
+ Loggability.format_as( :html )
29
+ Loggability.level = :info
30
+
31
+
32
+ == Prerequisites
33
+
34
+ * Ruby 1.9.3 or better, Rubinius 2.0 or better
35
+
36
+ It will probably work under any other interpreter in which Logger works, but
37
+ it's only tested in the above.
38
+
39
+
40
+ == Installation
41
+
42
+ $ gem install loggability
43
+
44
+
45
+ == Usage
46
+
47
+ Loggability is split up into two parts: {log hosts}[rdoc-ref:Loggability::LogHost]
48
+ and {log clients}[rdoc-ref:Loggability::LogClient]. A <b>log
49
+ host</b> is an object that contains a Logger instance that will be used to
50
+ log stuff. A <b>log client</b> is an object that will write logging messages to a
51
+ particular <b>log host</b>'s Logger.
52
+
53
+ Both parts require that you extend the object with Loggability.
54
+
55
+ === Setting Up A 'Log Host'
56
+
57
+ To install a Logger into an object, you use the +log_as+ declaration with a
58
+ Symbol that will be used as the key for the object's Logger:
59
+
60
+ module MyProject
61
+ extend Loggability
62
+ log_as :my_project
63
+ end
64
+
65
+ After declaring itself as a log host, it will have an associated Loggability::Logger
66
+ object that's a wrapper around a Logger instance:
67
+
68
+ MyProject.logger
69
+ # => #<Loggability::Logger:0x4e0c :my_project ...>
70
+
71
+ Since it's still a Logger object, you can call all the regular Logger methods:
72
+
73
+ MyProject.logger.level = Logger::WARN
74
+
75
+ MyProject.logger.debug("Created logger")
76
+ MyProject.logger.info("Program started")
77
+ MyProject.logger.warn("Nothing to do!")
78
+
79
+ begin
80
+ File.each_line(path) do |line|
81
+ unless line =~ /^(\w+) = (.*)$/
82
+ MyProject.logger.error("Line in wrong format: #{line}")
83
+ end
84
+ end
85
+ rescue => err
86
+ MyProject.logger.fatal("Caught exception; exiting")
87
+ MyProject.logger.fatal(err)
88
+ end
89
+
90
+ or use a few new convenience methods for changing the logging level:
91
+
92
+ MyProject.logger.level = :debug
93
+
94
+ ...installing a different formatter:
95
+
96
+ MyProject.logger.format_as( :html )
97
+
98
+ ...changing the output destination:
99
+
100
+ log_messages = []
101
+ MyProject.logger.output_to( log_messages )
102
+
103
+ ...{and more}[rdoc-ref:Loggability::Logger].
104
+
105
+
106
+ === Setting Up A 'Log Client'
107
+
108
+ To add an object that will log to your log host, after you <tt>extend Loggability</tt>,
109
+ use the +log_to+ declaration to hook up the object (and instances of the object if
110
+ you use +log_to+ in a Class) to the log host you specify:
111
+
112
+ class MyProject::Server
113
+ extend Loggability
114
+ log_to :my_project
115
+
116
+ def initialize( config={} )
117
+ self.log.debug "Creating a server with config: %p" % [ config ]
118
+ #...
119
+ end
120
+ end
121
+
122
+ You can fetch any object's Logger through the Loggability object:
123
+
124
+ Loggability[ MyProject ]
125
+ # => #<Loggability::Logger:0x007f88ca3bf510 ...>
126
+
127
+ Loggability[ MyProject::Server ]
128
+ # => #<Loggability::Logger:0x007f88ca3bf510 ...>
129
+
130
+ Calling the object's <tt>#log</tt> method will return a Proxy for its host's Logger object that will include the object's name in the log messages 'progname'.
131
+
132
+ You can also use the <b>log host</b> itself as the argument to +log_to+:
133
+
134
+ class MyProject::Client
135
+ extend Loggability
136
+ log_to MyProject
137
+ end
138
+
139
+
140
+ === Aggregate Logging
141
+
142
+ If you have several <b>log hosts</b>, and you want to affect them all simultaneously,
143
+ you can do that using the aggregate functions of Loggability. They're the same as the
144
+ methods on Loggability::Logger:
145
+
146
+ # Set all logs to log at INFO level
147
+ Loggability.level = :info
148
+
149
+ # Write HTML logs
150
+ Loggability.format_with( :html )
151
+
152
+ # Log everything to the same logfile
153
+ Loggability.output_to( "/tmp/my_project_log.html" )
154
+
155
+
156
+ == Contributing
157
+
158
+ You can check out the current development source with
159
+ Mercurial[http://bitbucket.org/ged/loggability], or if you prefer Git, via
160
+ {its Github mirror}[https://github.com/ged/loggability].
161
+
162
+ After checking out the source, run:
163
+
164
+ $ rake newb
165
+
166
+ This task will install any missing dependencies, run the tests/specs,
167
+ and generate the API documentation.
168
+
169
+
170
+ == License
171
+
172
+ Copyright (c) 2012, Michael Granger
173
+ All rights reserved.
174
+
175
+ Redistribution and use in source and binary forms, with or without
176
+ modification, are permitted provided that the following conditions are met:
177
+
178
+ * Redistributions of source code must retain the above copyright notice,
179
+ this list of conditions and the following disclaimer.
180
+
181
+ * Redistributions in binary form must reproduce the above copyright notice,
182
+ this list of conditions and the following disclaimer in the documentation
183
+ and/or other materials provided with the distribution.
184
+
185
+ * Neither the name of the author/s, nor the names of the project's
186
+ contributors may be used to endorse or promote products derived from this
187
+ software without specific prior written permission.
188
+
189
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
190
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
192
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
193
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
194
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
195
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
196
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
197
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
198
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
199
+
200
+
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'rake/clean'
4
+
5
+ begin
6
+ require 'hoe'
7
+ rescue LoadError
8
+ abort "This Rakefile requires 'hoe' (gem install hoe)"
9
+ end
10
+
11
+ Hoe.plugin :mercurial
12
+ Hoe.plugin :signing
13
+ Hoe.plugin :deveiate
14
+
15
+ Hoe.plugins.delete :rubyforge
16
+
17
+ hoespec = Hoe.spec 'loggability' do
18
+ self.readme_file = 'README.rdoc'
19
+ self.history_file = 'History.rdoc'
20
+ self.extra_rdoc_files = FileList[ '*.rdoc' ]
21
+
22
+ self.developer 'Michael Granger', 'ged@FaerieMUD.org'
23
+
24
+ self.dependency 'pluginfactory', '~> 1.0'
25
+
26
+ self.dependency 'hoe-deveiate', '~> 0.1', :developer
27
+ self.dependency 'simplecov', '~> 0.6', :developer
28
+
29
+ self.spec_extras[:licenses] = ["Ruby"]
30
+ self.spec_extras[:rdoc_options] = ['-f', 'fivefish', '-t', 'Loggability Toolkit']
31
+ self.require_ruby_version( '>=1.9.3' )
32
+ self.hg_sign_tags = true if self.respond_to?( :hg_sign_tags= )
33
+ self.check_history_on_release = true if self.respond_to?( :check_history_on_release= )
34
+
35
+ self.rdoc_locations << "deveiate:/usr/local/www/public/code/#{remote_rdoc_dir}"
36
+ end
37
+
38
+ ENV['VERSION'] ||= hoespec.spec.version.to_s
39
+
40
+ # Ensure the specs pass before checking in
41
+ task 'hg:precheckin' => [ :check_history, :check_manifest, :spec ]
42
+
43
+
44
+ desc "Build a coverage report"
45
+ task :coverage do
46
+ ENV["COVERAGE"] = 'yes'
47
+ Rake::Task[:spec].invoke
48
+ end
49
+
data/bin/loggability ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ abort "you need to write me"
@@ -0,0 +1,223 @@
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
4
+
5
+ require 'logger'
6
+ require 'date'
7
+
8
+
9
+ # A mixin that provides a top-level logging subsystem based on Logger.
10
+ module Loggability
11
+
12
+ # Package version constant
13
+ VERSION = '0.0.1'
14
+
15
+ # VCS revision
16
+ REVISION = %q$Revision: 7b3fcf97718a $
17
+
18
+ # The key for the global logger (Loggability's own logger)
19
+ GLOBAL_KEY = :__global__
20
+
21
+ # The methods that are delegated across all loggers
22
+ AGGREGATE_METHODS = [ :level=, :output_to, :write_to, :format_with, :format_as, :formatter= ]
23
+
24
+
25
+ require 'loggability/constants'
26
+ include Loggability::Constants
27
+
28
+ require 'loggability/logger'
29
+
30
+
31
+ ##
32
+ # The Hash of modules that have a Logger, keyed by the name they register with
33
+ class << self; attr_reader :log_hosts; end
34
+ @log_hosts = {}
35
+
36
+
37
+ ### Return the library's version string
38
+ def self::version_string( include_buildnum=false )
39
+ vstring = "%s %s" % [ self.name, VERSION ]
40
+ vstring << " (build %s)" % [ REVISION[/: ([[:xdigit:]]+)/, 1] || '0' ] if include_buildnum
41
+ return vstring
42
+ end
43
+
44
+
45
+ ### Register the specified +host+ as a log host. It should already have been extended
46
+ ### with LogHostMethods.
47
+ def self::register_loghost( host )
48
+ key = host.log_host_key
49
+ if self.log_hosts.key?( key )
50
+ self.logger.warn "Replacing existing log host for %p (%p) with %p" %
51
+ [ key, self.log_hosts[key], host ]
52
+ end
53
+
54
+ self.logger.debug "Registering %p log host: %p" % [ key, host ] if self.logger
55
+ self.log_hosts[ key ] = host
56
+ end
57
+
58
+
59
+ ### Return the Loggability::Logger for the loghost associated with +logclient+.
60
+ def self::[]( logclient )
61
+ key = logclient.log_host_key if logclient.respond_to?( :log_host_key )
62
+ key ||= GLOBAL_KEY
63
+
64
+ return self.log_hosts[ key ].logger
65
+ end
66
+
67
+
68
+ ### Clear out all log hosts except for ones which start with '_'. This is intended
69
+ ### to be used for testing.
70
+ def self::clear_loghosts
71
+ self.log_hosts.delete_if {|key,_| !key.to_s.start_with?('_') }
72
+ end
73
+
74
+
75
+ #
76
+ # :section: Aggregate Methods
77
+ #
78
+
79
+ ### Call the method with the given +methodname+ across the loggers of all loghosts with
80
+ ### the given +arg+ and/or +block+.
81
+ def self::aggregate( methodname, arg, &block )
82
+ Loggability.log_hosts.values.each do |loghost|
83
+ loghost.logger.send( methodname, arg, &block )
84
+ end
85
+ end
86
+
87
+
88
+ ##
89
+ # :method: level=
90
+ # :call-seq:
91
+ # level = newlevel
92
+ #
93
+ # Aggregate method: set the log level on all loggers to +newlevel+. See
94
+ # Loggability::Logger#level= for more info.
95
+
96
+ ##
97
+ # :method: output_to
98
+ # :call-seq:
99
+ # output_to( destination )
100
+ # write_to( destination )
101
+ #
102
+ # Aggregate method: set all loggers to log to +destination+. See Loggability::Logger#output_to
103
+ # for more info.
104
+
105
+ ##
106
+ # :method: format_with
107
+ # :call-seq:
108
+ # format_with( formatter )
109
+ # format_as( formatter )
110
+ # formatter = formatter
111
+ #
112
+ # Aggregate method: set all loggers to log with the given +formatter+. See
113
+ # Loggability::Logger#format_with for more info.
114
+ AGGREGATE_METHODS.each do |meth|
115
+ block = self.method( :aggregate ).to_proc.curry[ meth ]
116
+ Loggability.singleton_class.send( :define_method, meth, &block )
117
+ end
118
+
119
+
120
+ # Extension for 'log hosts'. A <b>log host</b> is an object that hosts a Loggability::Logger
121
+ # object, and is typically the top of some kind of hierarchy, like a namespace
122
+ # module for a project:
123
+ #
124
+ # module MyProject
125
+ #
126
+ # end
127
+ #
128
+ # This module isn't mean to be used directly -- it's installed via the Loggability#log_as
129
+ # declaration, which also does some other initialization that you'll likely want.
130
+ #
131
+ #
132
+ module LogHost
133
+
134
+ # The logger that will be used when the logging subsystem is reset
135
+ attr_accessor :default_logger
136
+
137
+ # The logger that's currently in effect
138
+ attr_accessor :logger
139
+ alias_method :log, :logger
140
+ alias_method :log=, :logger=
141
+
142
+ # The key associated with the logger for this host
143
+ attr_accessor :log_host_key
144
+
145
+ end # module LogHost
146
+
147
+
148
+ #
149
+ # :section: LogHost API
150
+ #
151
+
152
+ ### Register as a log host associated with the given +key+, add the methods from
153
+ ### LogHost, and install a Loggability::Logger.
154
+ def log_as( key )
155
+ self.extend( Loggability::LogHost )
156
+ self.log_host_key = key.to_sym
157
+ self.logger = self.default_logger = Loggability::Logger.new
158
+ Loggability.register_loghost( self )
159
+ end
160
+
161
+ # Install a global logger in Loggability itself
162
+ extend( Loggability::LogHost )
163
+ self.log_host_key = GLOBAL_KEY
164
+ self.logger = self.default_logger = Loggability::Logger.new
165
+ Loggability.register_loghost( self )
166
+
167
+
168
+
169
+ # Methods to install for objects which call +log_to+.
170
+ module LogClient
171
+
172
+ ##
173
+ # The key of the log host this client targets
174
+ attr_accessor :log_host_key
175
+
176
+ ### Return the Loggability::Logger object associated with the log host the
177
+ ### client is logging to.
178
+ ### :TODO: Use delegation for efficiency.
179
+ def log
180
+ @__log ||= Loggability[ self ].proxy_for( self )
181
+ end
182
+
183
+ # Stuff that gets added to instances of Classes that are log hosts.
184
+ module InstanceMethods
185
+
186
+ ### Fetch the key of the log host the instance of this client targets
187
+ def log_host_key
188
+ return self.class.log_host_key
189
+ end
190
+
191
+
192
+ ### Delegate to the class's logger.
193
+ def log
194
+ @__log ||= Loggability[ self.class ].proxy_for( self )
195
+ end
196
+
197
+ end # module InstanceMethods
198
+
199
+ end # module LogClient
200
+
201
+
202
+ #
203
+ # :section: LogClient API
204
+ #
205
+
206
+ ### Register as a <b>log client</b> that will log to to the given +loghost+, which can be
207
+ ### either the +key+ the host registered with, or the log host object itself. Log messages
208
+ ### can be written to the loghost via the LogClient API, which is automatically included.
209
+ def log_to( loghost )
210
+ self.extend( Loggability::LogClient )
211
+ self.log_host_key = if loghost.respond_to?( :log_host_key )
212
+ loghost.log_host_key
213
+ else
214
+ loghost.to_sym
215
+ end
216
+
217
+ # For objects that also can be instantiated
218
+ include( Loggability::LogClient::InstanceMethods ) if self.is_a?( Class )
219
+ end
220
+
221
+
222
+ end # module Strelka
223
+