logjam 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,265 @@
1
+ LogJam
2
+ ======
3
+ LogJam is a library that attempts to allow for the aggregation the distributing
4
+ logger facilities across a range of classes. Goals in creating this library
5
+ were...
6
+
7
+ * Easy of use. Fall back on defaults as much as possible and allow the
8
+ functionality to be integrated and used with the least amount of work.
9
+
10
+ * Flexibility. After easy of use is taken into consideration it should be
11
+ possible to use the library in a more advanced fashion if that is called
12
+ for.
13
+
14
+ * Minimize the code to use it. It shouldn't require a great deal of code to
15
+ deploy or use the facilities and there should be no code required to pass
16
+ entities such as loggers around.
17
+
18
+ * Usable in libraries. I found myself writing a lot of common logging code
19
+ when writing libraries and application and wanted to abstract that out. I
20
+ wanted to minimize the burden this placed on library users at the same
21
+ time.
22
+
23
+ Configuration & Setup
24
+ ---------------------
25
+ The LogJam library is configured through a call to the configure() method on the
26
+ LogJam module. This method accepts a single parameter that may be either a
27
+ String, a Hash, an IO object or nil.
28
+
29
+ If you pass a String in this is expected to contain the path and name of a file
30
+ that contains the logging configuration. Logging configuration files can be
31
+ provided in either YAML or JSON format. If you pass an IO object this is
32
+ expected to be the source of the configuration and, again, this configuration
33
+ should be in either YAML or JSON format. In either case, where a String or IO
34
+ is specified, the data read must be translatable into a Hash, which brings us
35
+ to the other type that the configure() method accepts as a parameter.
36
+
37
+ A Hash passed to the configure() method is expected to contain a set of values
38
+ that can be converted into a logging set up. Passing an empty Hash will result
39
+ in set up containing a single, universal logger attached to the standard output
40
+ stream being created and used by all classes that have LogJam functionality.
41
+
42
+ The Hash passed to the configure() method can contain two keys that the library
43
+ will recognise and use. The first of these is 'loggers', in either Symbol or
44
+ String form. The value under the loggers key is expected to be an Array
45
+ containing zero or more logger definitions. A logger definition is a Hash in
46
+ which the following keys are recognised (either as Strings or Symbols)...
47
+
48
+ Key Description
49
+ ----------------------- --------------------------------------------------
50
+ default A boolean indicating whether this logger is the
51
+ default (i.e. the one to be used when no other
52
+ fits the bill).
53
+ datetime_format The date/time format to be used by the logger. See
54
+ the documentation for the standard Ruby Logger
55
+ class for more details.
56
+ file The path and name of the file that logging details
57
+ will be written to. Two special values are
58
+ recognised in this value. STDOUT and STDERR are
59
+ translated to mean the standard output or error
60
+ streams respectively.
61
+ level The logging level to set on the logger.
62
+ max_size When rotation is set to an integer value this value
63
+ can be set to indicate the maximum permitted file
64
+ size for a log file.
65
+ name The name to associate with the logger. This allows
66
+ loggers to be tied to classes or for the creation
67
+ of aliases that tie multiple names to a single
68
+ logger.
69
+ rotation The frequency with which the log file is rotated.
70
+ This may be an integer to indicate how many old log
71
+ files are retained or may be a String such as
72
+ "daily", "weekly" or "monthly".
73
+
74
+ A note on logger names. Logger names (including alias names) aren't hierarchical
75
+ and should be unique.
76
+
77
+ The second key recognised in the configuration Hash is 'aliases', again as
78
+ either a String or Symbol. The value under the aliases key is expected to be a
79
+ Hash that maps String keys to String values. The key values are expected to be
80
+ alias names and the mapped values are expected to be the name of a logger
81
+ declared in the logger section. An entry in the aliases Hash creates an alias
82
+ for an existing logger under a different name.
83
+
84
+ If you pass nil to the configure method it behaves in a slightly different
85
+ fashion. In this case the method searches for a configuration file that it can
86
+ use given a default set of files names (basically logging.yaml, logging.yml and
87
+ logging.json in the current working directory and in a subdirectory of the
88
+ current working directory called config). The first of these files that it finds
89
+ it attempts to use as configuration for the logging set up.
90
+
91
+ See the end of this document for some example configurations.
92
+
93
+ Logging With The Library
94
+ ------------------------
95
+ The stated goals of the LogJam library are to avoid the need to pass Logger
96
+ instances around while still allowing potentially complex configuration with a
97
+ minimum of code. The first step in this process has been covered in the
98
+ Configuration & Setup section in which it's explained how to configure logging
99
+ from a single Hash or file. This section will provide details on how to deploy
100
+ loggers to various classes.
101
+
102
+ The LogJam library works by providing an extension to any class that uses it
103
+ that provides access to the logger to be used by the class. To make use of this
104
+ functionality a class must call the apply() method of the LogJam module. A
105
+ typical call to this method might look like...
106
+
107
+ LogJam.apply(self, "my_logger")
108
+
109
+ This line would appear somewhere inside the definition for the class that will
110
+ use logging. The first parameter to the call is the class that is to be extended
111
+ with the LogJam functionality. The second parameter is the name of the logger
112
+ that the class will use. Note that this parameter is optional and, if not
113
+ specified or if a matching logger does not exist, the class will fall back in
114
+ using the default logger.
115
+
116
+ Once this line has been added to the class definition it will cause the class to
117
+ be extended with two methods - one called log() and one called log=(). The first
118
+ of these retrieves the Logger instance to be used by the class instances. The
119
+ second allows the Logger instance associated with a class to be altered.
120
+
121
+ The following complete (although contrived) example gives an overview of how
122
+ this would work...
123
+
124
+ require 'rubygems'
125
+ require 'logjam'
126
+
127
+ class Writer
128
+ LogJam.apply(self, "echo")
129
+
130
+ def initialize(stream=STDOUT)
131
+ @stream = stream
132
+ end
133
+
134
+ def echo(message)
135
+ Writer.log.debug("Echoed: #{message}")
136
+ @stream.puts message
137
+ end
138
+ end
139
+
140
+ begin
141
+ LogJam.configure({:loggers => [{:name => "echo",
142
+ :file => "echo.log"}]})
143
+
144
+ writer = Writer.new
145
+ writer.echo "This is a string containing my message."
146
+ rescue => error
147
+ puts "ERROR: #{error}\n" + error.backtrace.join("\n")
148
+ end
149
+
150
+ In this example we create a Writer class that can echo a String on a stream. In
151
+ doing this it also logs the message echoed at the debug level. We use the
152
+ LogJam.apply() method to extend this class with logging capabilities so that, in
153
+ the echo() method, we can simply call Writer.log() to get the class logger.
154
+
155
+ In the later section of the code we configure the LogJam system to have a single
156
+ Logger that writes to the echo.log file. We then create a Writer instance and
157
+ use it to write a simple message. This message should appear on the standard
158
+ output stream and be logged to the echo.log file.
159
+
160
+ Advanced Usage
161
+ --------------
162
+ The hope would be that this library can be used in the creation of other
163
+ libraries and allow for control of the logging generated by those libraries
164
+ without having to dig into the workings of the library or to pass around Logger
165
+ instances are constructor parameters or static data. In this case I recommend
166
+ explicitly declaring logger names when using the apply() method and making the
167
+ name that the library uses available with the library documentation so that the
168
+ libraries logging can be switched off or on as needed.
169
+
170
+ It's intended that, in general, the configure() method on the LogJam module
171
+ should only be called once. Calling it a second time will clear all existing
172
+ logging configuration and set up. This may or may not be an issue depending on
173
+ whether you decide to cache logger inside class instances instead of always
174
+ accessing them through the class level accessor.
175
+
176
+ The Logger instance returned from a LogJam are intended to be fully compatible
177
+ with the class defined within the standard Ruby Logger library. If you need to
178
+ change elements, such as the formatter, you should just do so on the logger in
179
+ the normal fashion. If you define multiple Logger instances then you will have
180
+ to change each individually.
181
+
182
+ Using the log=() method that is added to each class by the LogJam facilities it
183
+ is possible to change the Logger being used. If you want to use this method
184
+ please note that changing a Logger that is created via an alias will change the
185
+ original Logger and thereby affect all classes that make use of that Logger (and
186
+ not necessarily just the one making the change). If you want to do this give the
187
+ class it's own logger instance.
188
+
189
+ Finally, any logger can be fetched from the library using it's name and making
190
+ a call to the LogJam.get_logger() method. Note if you omit the name or pass in
191
+ nil you will retrieve the libraries default logger.
192
+
193
+ Example Configurations
194
+ ----------------------
195
+ This section contains some example configurations. A short explanation is given
196
+ for each configuration and then the configuration itself in Hash, YAML and JSON
197
+ formats is provided.
198
+
199
+ This represents the most basic configuration possible. In passing an empty Hash
200
+ to the configure method the system creates a single, default logger that writes
201
+ everything on the standard output stream...
202
+
203
+ Hash
204
+ {}
205
+
206
+ YAML
207
+ ---
208
+ {}
209
+
210
+ JSON
211
+ {}
212
+
213
+ The following simple configuration writes all logging output to a file called
214
+ application.log in the current working directory. If a logging level is not
215
+ explicitly specified then DEBUG is the default...
216
+
217
+ Hash
218
+ {:loggers => [{:default => true, :file => "application.log"}]}
219
+
220
+ YAML
221
+ ---
222
+ :loggers:
223
+ - :default: true
224
+ :file: application.log
225
+
226
+ JSON
227
+ {"loggers": {"default": true, "file": "application.log"}}
228
+
229
+ This configuration declares two loggers. The first is called 'silent' and will
230
+ log nothing. The silent logger is the default and so will be used for any class
231
+ that doesn't have an explicitly named logger. The second is called 'verbose' and
232
+ logs everything from the debug level up on the standard output stream. The
233
+ configuration also declares an alias pointing the name 'database' to refer to
234
+ the verbose logger. An class that declares it uses the 'database' logger will
235
+ generate output while all others will be silenced.
236
+
237
+ Hash
238
+ {:loggers => [{:default => true,
239
+ :file => "STDOUT",
240
+ :level => "UNKNOWN",
241
+ :name => "silent"},
242
+ {:file => "STDOUT",
243
+ :name => "verbose"}],
244
+ :aliases => {"database" => "verbose"}}
245
+
246
+ YAML
247
+ ---
248
+ :loggers:
249
+ - :default: true
250
+ :file: STDOUT
251
+ :level: UNKNOWN
252
+ :name: silent
253
+ - :file: STDOUT
254
+ :name: verbose
255
+ :aliases:
256
+ database: verbose
257
+
258
+ JSON
259
+ {"loggers": [{"default":true,
260
+ "file": "STDOUT",
261
+ "level": "UNKNOWN",
262
+ "name": "silent"},
263
+ {"file": "STDOUT",
264
+ "name": "verbose"}],
265
+ "aliases": {"database":"verbose"}}
@@ -0,0 +1,40 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Copyright (c), 2012 Peter Wood
4
+ # See the license.txt for details of the licensing of the code in this file.
5
+
6
+ require 'stringio'
7
+
8
+ module LogJam
9
+ # This class provides the exception class used by the LogJam library.
10
+ class LogJamError < StandardError
11
+ # Attribute accessor/mutator declarations.
12
+ attr_accessor :verbose
13
+
14
+ # Constructor for the LogJamError class.
15
+ #
16
+ # ==== Parameters
17
+ # message:: The message to be associated with the error.
18
+ # cause:: Any underlying exception to be associated with the error.
19
+ # Defaults to nil.
20
+ def initialize(message, cause=nil, verbose=true)
21
+ super(message)
22
+ @cause = cause
23
+ @verbose = verbose
24
+ end
25
+
26
+ # This method fetches a stringified interpretation of an exception.
27
+ def to_s()
28
+ text = StringIO.new
29
+ text << super
30
+ if @verbose
31
+ text << "\n" + self.backtrace.join("\n")
32
+ if !@cause.nil?
33
+ text << "\n\nCause: #{@cause}"
34
+ text << "\n" + @cause.backtrace.join("\n")
35
+ end
36
+ end
37
+ text.string
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,129 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Copyright (c), 2012 Peter Wood
4
+ # See the license.txt for details of the licensing of the code in this file.
5
+
6
+ require 'logger'
7
+
8
+ # This module defines the name space to be used by all code within the
9
+ # LogJam library.
10
+ module LogJam
11
+ # Module constants.
12
+ LOG_FILE = :log_file
13
+ LOG_ROLL = :log_roll_period
14
+ LOG_LEVEL = :log_level
15
+
16
+ # Module wide property declarations.
17
+ @@logjam_logger = nil
18
+
19
+ # This method fetches the module logger, initializing it and creating it if
20
+ # it doesn't yet exist.
21
+ #
22
+ # ==== Parameters
23
+ # configuration:: The configuration to use in setting up the logging.
24
+ # Defaults to nil to indicate the creation of a logger based
25
+ # on STDOUT.
26
+ def log(configuration=nil)
27
+ LogJam.configure(configuration) if !configuration.nil? || @@logjam_logger.nil?
28
+ @@logjam_logger
29
+ end
30
+
31
+ # This method updates the logger associated with the module.
32
+ #
33
+ # ==== Parameters
34
+ # logger:: The new logger for the module.
35
+ def log=(logger)
36
+ LogJam.log = logger
37
+ end
38
+
39
+ # This method updates the logger associated with the module.
40
+ #
41
+ # ==== Parameters
42
+ # logger:: The new logger for the module.
43
+ def self.log=(logger)
44
+ @@logjam_logger.logger = logger
45
+ end
46
+
47
+ # This method is used to install the LogJam capabilities into a target class.
48
+ #
49
+ # ==== Parameters
50
+ # target:: The class that is to be 'logified'.
51
+ def self.logify(target)
52
+ target.extend(self)
53
+ end
54
+
55
+ # This method sets up the module logging from configuration.
56
+ #
57
+ # ==== Parameters
58
+ # configuration:: The configuration to use when setting up logging.
59
+ def self.configure(configuration)
60
+ if !configuration.nil?
61
+ if @@logjam_logger.nil?
62
+ # Create the logger.
63
+ if LogJam.is_configured?(configuration, LOG_FILE)
64
+ file_name = LogJam.get_value(configuration, LOG_FILE)
65
+ period = LogJam.get_value(configuration, LOG_ROLL)
66
+ @@logjam_logger = LogJamLogger.new(file_name, period)
67
+ else
68
+ @@logjam_logger = LogJamLogger.new(STDOUT)
69
+ end
70
+ else
71
+ # Change the internally held logger.
72
+ if LogJam.is_configured?(configuration, LOG_FILE)
73
+ file_name = LogJam.get_value(configuration, LOG_FILE)
74
+ period = LogJam.get_value(configuration, LOG_ROLL)
75
+ @@logjam_logger.logger = Logger.new(file_name, period)
76
+ else
77
+ @@logjam_logger.logger = Logger.new(STDOUT)
78
+ end
79
+ end
80
+ else
81
+ if @@logjam_logger.nil?
82
+ @@logjam_logger = LogJamLogger.new(STDOUT)
83
+ else
84
+ @@logjam_logger.logger = Logger.new(STDOUT)
85
+ end
86
+ end
87
+
88
+ # Set the logger level.
89
+ level = LogJam.get_value(configuration, LOG_LEVEL, "DEBUG")
90
+ case level.downcase
91
+ when "info"
92
+ level = Logger::INFO
93
+ when "warn"
94
+ level = Logger::WARN
95
+ when "error"
96
+ level = Logger::ERROR
97
+ when "fatal"
98
+ level = Logger::FATAL
99
+ when "unknown"
100
+ level = Logger::UNKNOWN
101
+ else
102
+ level = Logger::DEBUG
103
+ end
104
+ @@logjam_logger.level = level
105
+ @@logjam_logger
106
+ end
107
+
108
+ private
109
+
110
+ # This method encapsulates the functionality for extracting named value from
111
+ # a Hash given a key.
112
+ def self.get_value(configuration, name, default=nil)
113
+ value = default
114
+ if !configuration.nil?
115
+ if configuration.include?(name)
116
+ value = configuration[name]
117
+ elsif configuration.include?(name.to_s)
118
+ value = configuration[name.to_s]
119
+ end
120
+ end
121
+ value
122
+ end
123
+
124
+ # This method is used internally by the module to determine whether a
125
+ # configuration setting has been provided.
126
+ def self.is_configured?(configuration, name)
127
+ configuration.include?(name) || configuration.include?(name.to_s)
128
+ end
129
+ end
@@ -0,0 +1,382 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Copyright (c), 2012 Peter Wood
4
+ # See the license.txt for details of the licensing of the code in this file.
5
+
6
+ require 'json'
7
+ require 'logger'
8
+ require 'stringio'
9
+ require 'yaml'
10
+
11
+ # This module defines the name space to be used by all code within the
12
+ # LogJam library.
13
+ module LogJam
14
+ # Module constants.
15
+ LOGGER_NAME = :name
16
+ LOGGER_FILE = :file
17
+ LOGGER_ROTATION = :rotation
18
+ LOGGER_MAX_SIZE = :max_size
19
+ LOGGER_LEVEL = :level
20
+ LOGGER_DEFAULT = :default
21
+ LOGGER_DATETIME_FORMAT = :datetime_format
22
+ DEFAULT_FILE_NAMES = [".#{File::SEPARATOR}logging.yaml",
23
+ ".#{File::SEPARATOR}logging.yml",
24
+ ".#{File::SEPARATOR}logging.json",
25
+ ".#{File::SEPARATOR}config#{File::SEPARATOR}logging.yaml",
26
+ ".#{File::SEPARATOR}config#{File::SEPARATOR}logging.yml",
27
+ ".#{File::SEPARATOR}config#{File::SEPARATOR}logging.json"]
28
+
29
+ # Module static properties.
30
+ @@logjam_modules = {}
31
+ @@logjam_loggers = {}
32
+
33
+ # This method is used to configure the LogJam module with the various loggers
34
+ # it will use.
35
+ #
36
+ # ==== Parameters
37
+ # source:: Either a String containing the path and name of the configuration
38
+ # file containing the logging set up, an IO object from which the
39
+ # configuration details can be read, a Hash containing a logging
40
+ # configuration or nil.
41
+ def self.configure(source)
42
+ @@logjam_modules = {}
43
+ @@logjam_loggers = {}
44
+
45
+ # Check for default files if nil was passed in.
46
+ source = LogJam.find_default_file if source.nil?
47
+
48
+ if !source.kind_of?(Hash) && !source.nil?
49
+ io = source.kind_of?(String) ? File.new(source) : source
50
+ type = source.kind_of?(String) ? LogJam.guess_format(source) : nil
51
+ LogJam.process_configuration(LogJam.load_configuration(io, type))
52
+ elsif source.nil?
53
+ LogJam.process_configuration({})
54
+ else
55
+ LogJam.process_configuration(source)
56
+ end
57
+ end
58
+
59
+ # This method is used to install logging facilities at the class level for a
60
+ # given class. Once 'logified' a class will possess two new methods. The
61
+ # first, #log(), retrieves the logger associated with the class. The second,
62
+ # #log=(), allows the assignment of the logger associated with the class.
63
+ # Note that changing the logger associated with a class will impact all other
64
+ # classes that use the same logger.
65
+ #
66
+ # ==== Parameters
67
+ # target:: The target class that is to be extended.
68
+ # name:: The name of the logger to be used by the class. Defaults to nil
69
+ # to indicate use of the default logger.
70
+ def self.apply(target, name=nil)
71
+ target.extend(LogJam.get_module(name))
72
+ end
73
+
74
+ # This method attempts to fetch the logger for a specified name. If this
75
+ # logger does not exist then a default logger will be returned instead.
76
+ #
77
+ # ==== Parameters
78
+ # name:: The name of the logger to retrieve.
79
+ def self.get_logger(name=nil)
80
+ LogJam.process_configuration(nil) if @@logjam_loggers.empty?
81
+ @@logjam_loggers.fetch(name, @@logjam_loggers[nil])
82
+ end
83
+
84
+ # This method fetches a list of the names currently defined within the LogJam
85
+ # internal settings.
86
+ def self.names
87
+ @@logjam_loggers.keys.compact
88
+ end
89
+
90
+ private
91
+
92
+ # This method fetches the module associated with a name. If the module does
93
+ # not exist the default module is returned instead.
94
+ #
95
+ # ==== Parameters
96
+ # name:: The name associated with the module to return.
97
+ def self.get_module(name)
98
+ LogJam.create_module(name)
99
+ end
100
+
101
+ # This method attempts to load a LogJam configuration from an IO object.
102
+ #
103
+ # ==== Parameters
104
+ # source:: The IO object to read the configuration details from.
105
+ # type:: An indicator of the format being used for the configuration. This
106
+ # should be :yaml, :json or nil. Nil indicates that the library
107
+ # will try to load the file in various formats.
108
+ def self.load_configuration(source, type)
109
+ configuration = nil
110
+ if ![:yaml, :json].include?(type)
111
+ begin
112
+ # Read in the full details of the configuration.
113
+ details = source.read
114
+
115
+ # Try YAML format first.
116
+ begin
117
+ configuration = LogJam.load_yaml_configuration(StringIO.new(details))
118
+ rescue LogJamError
119
+ # Assume wrong format and ignore.
120
+ configuration = nil
121
+ end
122
+
123
+ if configuration.nil?
124
+ # Try JSON format second.
125
+ begin
126
+ configuration = LogJam.load_json_configuration(StringIO.new(details))
127
+ rescue LogJamError
128
+ # Assume wrong format and ignore.
129
+ configuration = nil
130
+ end
131
+ end
132
+ rescue => error
133
+ raise LogJamError.new("Exception raised loading the LogJam "\
134
+ "configuration.\nCause: #{error}", error)
135
+ end
136
+
137
+ # Check if the load was successful.
138
+ if configuration.nil?
139
+ raise LogJamError.new("Error loading the LogJam configuration. The "\
140
+ "configuration is not in a recognised format "\
141
+ "or contains errors. The configuration must "\
142
+ "be in either YAML or JSON format.")
143
+ end
144
+ elsif type == :json
145
+ configuration = LogJam.load_json_configuration(source)
146
+ elsif type == :yaml
147
+ configuration = LogJam.load_yaml_configuration(source)
148
+ end
149
+ configuration
150
+ end
151
+
152
+ # This method attempts to load a configuration for the LogJam library from
153
+ # a file. The configuration is expected to be in YAML format.
154
+ #
155
+ # ==== Parameters
156
+ # source:: An IO object from which the configuration will be read.
157
+ def self.load_yaml_configuration(source)
158
+ begin
159
+ YAML.load(source)
160
+ rescue => error
161
+ raise LogJamError.new("Error loading LogJam configuration from YAML "\
162
+ "source.\nCause: #{error}", error)
163
+ end
164
+ end
165
+
166
+ # This method attempts to load a configuration for the LogJam library from
167
+ # a file. The configuration is expected to be in JSON format.
168
+ #
169
+ # ==== Parameters
170
+ # source:: An IO object from which the configuration will be read.
171
+ def self.load_json_configuration(source)
172
+ begin
173
+ JSON.parse(source.read)
174
+ rescue => error
175
+ raise LogJamError.new("Error loading LogJam configuration from JSON "\
176
+ "source.\nCause: #{error}", error)
177
+ end
178
+ end
179
+
180
+ # This method processes a logger configuration and generates the appropriate
181
+ # set of loggers and internal objects from it.
182
+ #
183
+ # ==== Parameters
184
+ # configuration:: The configuration to be processed. If this is nil or empty
185
+ # then a single default logger is generated that writes to the standard
186
+ # output stream.
187
+ def self.process_configuration(configuration)
188
+ if !configuration.nil? && !configuration.empty?
189
+ key = (configuration.include?(:loggers) ? :loggers : "loggers")
190
+ if configuration.include?(key)
191
+ loggers = configuration[key]
192
+ if loggers.kind_of?(Array)
193
+ configuration[key].each do |definition|
194
+ LogJam.create_logger(definition)
195
+ end
196
+ elsif loggers.kind_of?(Hash)
197
+ LogJam.create_logger(loggers)
198
+ else
199
+ raise LogJamError.new("The loggers configuration entry is in "\
200
+ "an unrecognised format. Must be either "\
201
+ "a Hash or an Array.")
202
+ end
203
+ end
204
+ end
205
+
206
+ # Set up any aliases that have been specified.
207
+ if !@@logjam_loggers.empty? && !configuration.nil? && !configuration.empty?
208
+ key = (configuration.include?(:loggers) ? :aliases : "aliases")
209
+ if configuration.include?(key)
210
+ configuration[key].each do |name, equivalent|
211
+ @@logjam_loggers[name] = LogJam.get_logger(equivalent)
212
+ @@logjam_modules[name] = LogJam.get_module(equivalent)
213
+ end
214
+ end
215
+ end
216
+
217
+ # Create a default logger if one hasn't been specified.
218
+ if @@logjam_loggers[nil].nil?
219
+ LogJam.create_logger({LOGGER_FILE => "STDOUT"})
220
+ end
221
+ end
222
+
223
+ # This method is used to create an anonymous module under a given name (if it
224
+ # doesn't already exist) and return it to the caller.
225
+ #
226
+ # ==== Parameters
227
+ # name:: The name to create the module under.
228
+ def self.create_module(name)
229
+ if !@@logjam_modules.include?(name)
230
+ # Create the anonymous module and add methods to it.
231
+ @@logjam_modules[name] = Module.new
232
+ @@logjam_modules[name].send(:define_method, :log) do
233
+ LogJam.get_logger(name)
234
+ end
235
+ @@logjam_modules[name].send(:define_method, :log=) do |logger|
236
+ LogJam.get_logger(name).logger = logger
237
+ end
238
+ end
239
+ @@logjam_modules[name]
240
+ end
241
+
242
+ # This method extends a specified class with a named module.
243
+ #
244
+ # ==== Parameters
245
+ # target:: The class that is to be extended.
246
+ # name:: The name of the module to extend the class with.
247
+ def self.extend_class(target, name)
248
+ target.extend(LogJam.get_module(name))
249
+ end
250
+
251
+ # This method attempts to guess the format that configuration details will be
252
+ # in given a file name. Guessing is done by looking at the file's extension.
253
+ # Files ending in '.yaml' or '.yml' are considered YAML. Files ending '.json'
254
+ # are considered JSON. The method returns nil if the path passed in has
255
+ # neither of these extensions.
256
+ #
257
+ # ==== Parameters
258
+ # path:: The path and name of the file to make the guess from.
259
+ def self.guess_format(path)
260
+ type = nil
261
+ if path.nil? && path.include?(".")
262
+ offset = path.length - path.reverse.index(".")
263
+ extension = path[offset, path.length - offset].downcase
264
+ case extension
265
+ when 'yaml', 'yml'
266
+ type = :yaml
267
+
268
+ when 'json'
269
+ type = :json
270
+ end
271
+ end
272
+ type
273
+ end
274
+
275
+ # This method creates a logger from a given definition. A definition should
276
+ # be a Hash containing the values that are used to configure the Logger with.
277
+ #
278
+ # ==== Parameters
279
+ # definition:: A Hash containing the configuration details for the logger.
280
+ def self.create_logger(definition)
281
+ # Fetch the configuration values.
282
+ name = LogJam.get_value(definition, LOGGER_NAME)
283
+ path = LogJam.get_value(definition, LOGGER_FILE)
284
+ rotation = LogJam.get_value(definition, LOGGER_ROTATION)
285
+ max_size = LogJam.get_value(definition, LOGGER_MAX_SIZE)
286
+ level = LogJam.get_value(definition, LOGGER_LEVEL)
287
+ default = LogJam.get_value(definition, LOGGER_DEFAULT)
288
+
289
+ device = nil
290
+ if ["STDOUT", "STDERR"].include?(path)
291
+ device = (path == "STDOUT" ? STDOUT : STDERR)
292
+ else
293
+ device = path
294
+ end
295
+
296
+ if rotation.kind_of?(String) && /^\s*\d+\s*$/ =~ rotation
297
+ rotation = rotation.to_i
298
+ rotation = 0 if rotation < 0
299
+ end
300
+
301
+ if !max_size.nil? && max_size.kind_of?(String)
302
+ max_size = max_size.to_i
303
+ end
304
+ max_size = 1048576 if !max_size.nil? && max_size < 1024
305
+
306
+ if !level.nil?
307
+ case level.downcase
308
+ when 'info'
309
+ level = Logger::INFO
310
+
311
+ when 'warn'
312
+ level = Logger::WARN
313
+
314
+ when 'error'
315
+ level = Logger::ERROR
316
+
317
+ when 'fatal'
318
+ level = Logger::FATAL
319
+
320
+ when 'unknown'
321
+ level = Logger::UNKNOWN
322
+
323
+ else
324
+ level = Logger::DEBUG
325
+ end
326
+ else
327
+ level = Logger::DEBUG
328
+ end
329
+
330
+ if default != true
331
+ if default.kind_of?(String)
332
+ default = ["true", "yes", "on", "1"].include?(default.downcase)
333
+ else
334
+ default = false
335
+ end
336
+ end
337
+
338
+ # Create the actual logger and associated module.
339
+ logger = LogJamLogger.new(device, rotation, max_size)
340
+ logger.level = level
341
+ logger.name = name
342
+ @@logjam_loggers[name] = logger
343
+ logger_module = LogJam.create_module(name)
344
+ if default
345
+ @@logjam_loggers[nil] = logger
346
+ @@logjam_modules[nil] = logger_module
347
+ end
348
+ logger
349
+ end
350
+
351
+ # This method attempts to fetch a value from a Hash. The key passed to the
352
+ # method should be a symbol and this will be checked for first. If this is
353
+ # not found then a check is made for the string equivalent. The first of
354
+ # these that is present in the Hash generates the value returned. If neither
355
+ # is present in the Hash then nil is returned.
356
+ #
357
+ # ==== Parameters
358
+ # source:: The Hash that will be checked for the value.
359
+ # key:: A symbol that will be checked as the key for the value.
360
+ def self.get_value(source, key)
361
+ if source.include?(key)
362
+ source[key]
363
+ elsif source.include?(key.to_s)
364
+ source[key.to_s]
365
+ else
366
+ nil
367
+ end
368
+ end
369
+
370
+ # This module level method is used to check for the existence of a
371
+ # configuration file under one or a standard set of names. This method is
372
+ # only used whenever the configure method is called and either passed nil
373
+ # or no parameter.
374
+ def self.find_default_file
375
+ file_name = nil
376
+ DEFAULT_FILE_NAMES.each do |name|
377
+ file_name = name if File.exists?(name) && File.readable?(name)
378
+ break if !file_name.nil?
379
+ end
380
+ file_name
381
+ end
382
+ end
@@ -0,0 +1,202 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Copyright (c), 2012 Peter Wood
4
+ # See the license.txt for details of the licensing of the code in this file.
5
+
6
+ module LogJam
7
+ # This class represents a specialization of the Ruby Logger class. The class
8
+ # retains a Ruby Logger instance within itself and delegates all standard
9
+ # logger calls to this instance. This allows for changes to the underlying
10
+ # logger without changing the containing one, thus bypassing people caching
11
+ # an instance.
12
+ class LogJamLogger < Logger
13
+ # Constructor for the LogJamLogger class. All parameters are passed
14
+ # straight through to create a standard Ruby Logger instance except
15
+ # when the first parameter is a Logger instance. In this case the
16
+ # Logger passed in is used rather than creating a new one.
17
+ #
18
+ # ==== Parameters
19
+ # logdev:: The log device to be used by the logger. This should
20
+ # either be a String containing a file path/name or an IO
21
+ # object that the logging details will be written to.
22
+ # shift_age:: The maximum number of old log files to retain or a String
23
+ # containing the rotation frequency for the log.
24
+ # shift_size:: The maximum size that the loggin output will be allowed
25
+ # to grow to before rotation occurs.
26
+ def initialize(logdev, shift_age=0, shift_size=1048576)
27
+ if logdev.kind_of?(Logger)
28
+ @log = logdev
29
+ else
30
+ @log = Logger.new(logdev, shift_age, shift_size)
31
+ end
32
+ @name = nil
33
+ end
34
+
35
+ # Attribute accessor/mutator declaration.
36
+ attr_accessor :name
37
+
38
+ # Overload of the property fetcher provided by the contained Logger
39
+ # instance.
40
+ def formatter
41
+ @log.formatter
42
+ end
43
+
44
+ # Overload of the property updater provided by the contained Logger
45
+ # instance.
46
+ def formatter=(formatter)
47
+ @log.formatter = formatter
48
+ end
49
+
50
+ # Overload of the property fetcher provided by the contained Logger
51
+ # instance.
52
+ def level
53
+ @log.level
54
+ end
55
+
56
+ # Overload of the property updater provided by the contained Logger
57
+ # instance.
58
+ def level=(level)
59
+ @log.level = level
60
+ end
61
+
62
+ # Overload of the property fetcher provided by the contained Logger
63
+ # instance.
64
+ def progname
65
+ @log.progname
66
+ end
67
+
68
+ # Overload of the property updater provided by the contained Logger
69
+ # instance.
70
+ def progname=(name)
71
+ @log.progname = name
72
+ end
73
+
74
+ # Overload of the property fetcher provided by the contained Logger
75
+ # instance.
76
+ def sev_threshold
77
+ @log.sev_threshold
78
+ end
79
+
80
+ # Overload of the property updater provided by the contained Logger
81
+ # instance.
82
+ def sev_threshold=(threshold)
83
+ @log.sev_threshold = threshold
84
+ end
85
+
86
+ # Overload of the corresponding method on the Logger class to pass the
87
+ # call straight through to the contained logger.
88
+ def <<(message)
89
+ @log << message
90
+ end
91
+
92
+ # Overload of the corresponding method on the Logger class to pass the
93
+ # call straight through to the contained logger.
94
+ def add(severity, message=nil, program=nil, &block)
95
+ @log.add(severity, message, program, &block)
96
+ end
97
+
98
+ # Overload of the corresponding method on the Logger class to pass the
99
+ # call straight through to the contained logger.
100
+ def close
101
+ @log.close
102
+ end
103
+
104
+ # Overload of the corresponding method on the Logger class to pass the
105
+ # call straight through to the contained logger.
106
+ def datetime_format
107
+ @log.datetime_format
108
+ end
109
+
110
+ # Overload of the corresponding method on the Logger class to pass the
111
+ # call straight through to the contained logger.
112
+ def datetime_format=(format)
113
+ @log.datetime_format = format
114
+ end
115
+
116
+ # Overload of the corresponding method on the Logger class to pass the
117
+ # call straight through to the contained logger.
118
+ def debug(program=nil, &block)
119
+ @log.debug(program, &block)
120
+ end
121
+
122
+ # Overload of the corresponding method on the Logger class to pass the
123
+ # call straight through to the contained logger.
124
+ def debug?
125
+ @log.debug?
126
+ end
127
+
128
+ # Overload of the corresponding method on the Logger class to pass the
129
+ # call straight through to the contained logger.
130
+ def error(program=nil, &block)
131
+ @log.error(program, &block)
132
+ end
133
+
134
+ # Overload of the corresponding method on the Logger class to pass the
135
+ # call straight through to the contained logger.
136
+ def error?
137
+ @log.error?
138
+ end
139
+
140
+ # Overload of the corresponding method on the Logger class to pass the
141
+ # call straight through to the contained logger.
142
+ def fatal(program=nil, &block)
143
+ @log.fatal(program, &block)
144
+ end
145
+
146
+ # Overload of the corresponding method on the Logger class to pass the
147
+ # call straight through to the contained logger.
148
+ def fatal?
149
+ @log.fatal?
150
+ end
151
+
152
+ # Overload of the corresponding method on the Logger class to pass the
153
+ # call straight through to the contained logger.
154
+ def info(program=nil?, &block)
155
+ @log.info(program, &block)
156
+ end
157
+
158
+ # Overload of the corresponding method on the Logger class to pass the
159
+ # call straight through to the contained logger.
160
+ def info?
161
+ @log.info?
162
+ end
163
+
164
+ # Overload of the corresponding method on the Logger class to pass the
165
+ # call straight through to the contained logger.
166
+ def unknown(program=nil, &block)
167
+ @log.unknown(program, &block)
168
+ end
169
+
170
+ # Overload of the corresponding method on the Logger class to pass the
171
+ # call straight through to the contained logger.
172
+ def warn(program=nil, &block)
173
+ @log.warn(program, &block)
174
+ end
175
+
176
+ # Overload of the corresponding method on the Logger class to pass the
177
+ # call straight through to the contained logger.
178
+ def warn?
179
+ @log.warn?
180
+ end
181
+
182
+ # This method fetches the standard Ruby Logger instance contained within
183
+ # a LogJamLogger object.
184
+ def logger
185
+ @log
186
+ end
187
+
188
+ # This method updates the logger instance contained within a LogJamLogger
189
+ # object.
190
+ #
191
+ # ==== Parameters
192
+ # logger:: The object to set as the contained logger. This should be an
193
+ # instance of the standard Ruby Logger class or something
194
+ # compatible with this.
195
+ def logger=(logger)
196
+ @log = logger
197
+ end
198
+
199
+ # Aliases
200
+ alias :log :add
201
+ end
202
+ end
data/lib/logjam.rb ADDED
@@ -0,0 +1,10 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Copyright (c), 2012 Peter Wood
4
+ # See the license.txt for details of the licensing of the code in this file.
5
+
6
+ require 'logger'
7
+ require 'logjam/exceptions'
8
+ require 'logjam/logjam_logger'
9
+ require 'logjam/logjam'
10
+
data/license.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2011 Peter Wood
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logjam
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Black North
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-02-10 00:00:00 +00:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: json
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: LogJam is a library to simplify the use of logging across libraries and applications.
34
+ email: ruby@blacknorth.com
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files: []
40
+
41
+ files:
42
+ - lib/logjam/logjam-original.rb
43
+ - lib/logjam/exceptions.rb
44
+ - lib/logjam/logjam.rb
45
+ - lib/logjam/logjam_logger.rb
46
+ - lib/logjam.rb
47
+ - license.txt
48
+ - README
49
+ has_rdoc: true
50
+ homepage:
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.7
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: A library to aggregate logging.
81
+ test_files: []
82
+