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