loggability 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/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
+