logjam 1.1.0 → 1.2.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e9535269f47ac5920a95577153acd06362603e8f
4
+ data.tar.gz: 22348c06754806f3418c6f6da2b497d3ca9cd40e
5
+ SHA512:
6
+ metadata.gz: 16e5edda4ea08cb7ca7dcfb9befd04121cbae237d8eeacb9aa728ecaf8a3a66e5c1d9112936a83ca0f47cf858cfa96ebfa6f236a4dcbbd7dbadc3cd575d56816
7
+ data.tar.gz: 29097aca1db189e166b37155c6584a2f0e37d46ee724a74d410dd4dd7c3750ec032ea1737e01646399d2ebc0bac3bf380b1102076415dfec703ba3b85ec477f3
@@ -6,46 +6,61 @@ creating this library were...
6
6
 
7
7
  * Easy of use. Fall back on defaults as much as possible and allow the
8
8
  functionality to be integrated and used with the least amount of work.
9
-
9
+
10
10
  * Flexibility. After easy of use is taken into consideration it should be
11
11
  possible to use the library in a more advanced fashion if that is called
12
12
  for.
13
-
13
+
14
14
  * Minimize the code to use it. It shouldn't require a great deal of code to
15
15
  deploy or use the facilities and there should be no code required to pass
16
16
  entities such as loggers around.
17
-
17
+
18
18
  * Usable in libraries. I found myself writing a lot of common logging code
19
19
  when writing libraries and application and wanted to abstract that out. I
20
20
  wanted to minimize the burden this placed on library users at the same
21
21
  time.
22
22
 
23
+ ## Release Log
24
+
25
+ * v1.2.0: This version sees a major rewrite of the internals of the library
26
+ while attempting to retain backward compatibility. Library configuration
27
+ has been changed to get greater flexibility and to allow for the logging
28
+ configuration to be folded into a larger configuration file. The tests were
29
+ all changed to rspec and more extensive tests written.
30
+
23
31
  ## Configuration & Setup
24
32
 
25
- To explicitly configure the settings LogJam you make a call to the
26
- LogJam#configure() method. This method takes a single parameter which should be
27
- either a 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 LogJam#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. An individual logger definition is
46
- itself a Hash in which the following keys are recognised (either as Strings or
47
- Symbols) - default, datetime_format, file, level, max_size, name and rotation.
48
- The meanings applied to these keys are as follows...
33
+ The simplest setup to use with this library is to create a YAML file in the
34
+ called ```logging.yml```, either in the current working directory or in a
35
+ subdirectory of the working directory called ```config```. Place the following
36
+ contents into this file...
37
+
38
+ development:
39
+ loggers:
40
+ - default: true
41
+ file: STDOUT
42
+ name: devlog
43
+ production:
44
+ loggers:
45
+ - default: true
46
+ file: ./logs/production.log
47
+ name: prodlog
48
+ test:
49
+ loggers:
50
+ - default: true
51
+ file: STDOUT
52
+ name: testlog
53
+
54
+ By doing this you've now created a configuration that is environment dependent
55
+ and that the LogJam library will automatically pick up. When run in the
56
+ development (the default environment if no other is specified) or test
57
+ environments your application will now log to the standard output stream. For
58
+ the production environment the logging output will be written to a file called
59
+ ```production.log``` which will be in the ```logs``` subdirectory.
60
+
61
+ The settings covered in the example configuration above are just some of the
62
+ parameters recognised for the definition of a logger. Here is a more complete
63
+ list of parameters that are used when creating loggers...
49
64
 
50
65
  * default: A boolean indicating whether this logger is the default (i.e. the
51
66
  one to be used when no other explicitly fits the bill). Only one logger
@@ -63,7 +78,7 @@ The meanings applied to these keys are as follows...
63
78
  to DEBUG.
64
79
 
65
80
  * max_size: When rotation is set to an integer value this value can be set to
66
- indicate the maximum permitted file size for a log file.
81
+ indicate the maximum permitted file size for a log file in bytes.
67
82
 
68
83
  * name: The name to associate with the logger. This allows loggers to be tied
69
84
  to classes or for the creation of aliases that tie multiple names to a single
@@ -75,30 +90,33 @@ The meanings applied to these keys are as follows...
75
90
  such as "daily", "weekly" or "monthly".
76
91
 
77
92
  A note on logger names. Logger names (including alias names) aren't hierarchical
78
- and should be unique.
79
-
80
- The second key recognised in the configuration Hash is 'aliases', again as
81
- either a String or Symbol. The value under the aliases key is expected to be a
82
- Hash that maps String keys to String values. The key values are expected to be
83
- alias names and the mapped values are expected to be the name of a logger
84
- declared in the logger section. An entry in the aliases Hash creates an alias
85
- for an existing logger under a different name.
86
-
87
- The final parameter option when calling LogJam#configure() is to pass a nil to
88
- the method. If you pass nil to the configure method it behaves in a slightly
89
- different fashion. In this case the method searches for a configuration file
90
- that it can use given a default set of files names (logging.yaml, logging.yml
91
- and logging.json in the current working directory and in a subdirectory of the
92
- current working directory called config). The first of these files that it finds
93
- it attempts to use as configuration for the logging set up. If it doesn't find
94
- a configuration file then this type of call becomes equivalent to passing an
95
- empty Hash to the configure() method. As of version 1.1.0 the library includes
96
- an implicit call to LogJam#configure(nil) removing the need to do this
97
- explicitly in your own code.
98
-
99
- Note that you can call LogJam#configure multiple times, with each successive
100
- call overwriting and replacing the details of previous calls. See the end of
101
- this document for some example configurations.
93
+ and should be unique. Note that you may specify multiple logger definitions if
94
+ you wish, which would look like this...
95
+
96
+ development:
97
+ loggers:
98
+ - default: true
99
+ file: STDOUT
100
+ name: devlog
101
+ - file: ./logs/development.log
102
+ name: filelog
103
+
104
+ In addition to specifying logger definitions you can also specify logger
105
+ aliases. This is essentially a mechanism to allow a single logger to be
106
+ available under multiple names and a configuration including an alias definition
107
+ might look as follows...
108
+
109
+ development:
110
+ loggers:
111
+ - default: true
112
+ file: STDOUT
113
+ name: devlog
114
+ aliases:
115
+ database: devlog
116
+
117
+ If you don't provide a logging configuration then the LogJam library will fall
118
+ back on creating a single default logger that writes everything to the standard
119
+ output stream.
102
120
 
103
121
  ## Logging With The Library
104
122
 
@@ -109,85 +127,30 @@ Configuration & Setup section in which it's explained how to configure logging
109
127
  from a single Hash or file. This section will provide details on how to deploy
110
128
  loggers to various classes.
111
129
 
112
- The LogJam library works by providing an extension to any class that uses it
113
- that provides access to the logger to be used by the class. As of version 1.1.0
114
- LogJam applies itself as an extension to the Ruby Object class, making the
115
- logging facilities it provides available in any object. Prior to v1.1.0 you had
116
- to make a call the apply() method of the LogJam module inside your class
117
- definition. A typical call of this type might look like...
118
-
119
- ```
120
- # Apply logging facilties to my class.
121
- LogJam.apply(self, "my_logger")
122
- ```
123
-
124
- This option remains available for backward compatibility but is no longer
125
- needed. A line like this would appear somewhere inside the definition for the
126
- class that will use logging. The first parameter to the call is the class that
127
- is to be extended with the LogJam functionality. The second parameter is the
128
- name of the logger that the class will use. Note that this parameter is optional
129
- and, if notspecified or if a matching logger does not exist, the class will fall
130
- back in using the default logger.
131
-
132
- From version 1.1.0 onward you no longer need to call apply. Instead LogJams
133
- logging facilities are available in all objects. This change means that all
134
- classes use the default logger as standard. If you want to continue to use an
135
- explitictly named logger on a per class basis you can make a call to the
136
- LogJam#set_logger_name() method within your class definitions. This is similar
137
- in nature to how the apply method was used and so would look something like the
138
- following...
130
+ The LogJam library extends the object class to make access to a logger available
131
+ at both the class and the instance level. The obtain a logger object you can
132
+ make a call to the ```#log()``` method. If you haven't explicitly configured a
133
+ logger for a class this will return an instance of the default logger. A version
134
+ of this method is also available at the instance level.
139
135
 
140
- ```
141
- # Use an explicitly named logger for my class.
142
- set_logger_name "my_logger"
143
- ```
136
+ If you want to get more advanced and configure a particular logger for a
137
+ specific class or group of classes then you have to explicitly set the logger
138
+ on those classes. To do that you define multiple loggers in your configuration
139
+ and then make a call to the ```#set_logger_name()``` method for the affected
140
+ class. For example, if you defined a logger called string_logger that you wanted
141
+ to use just for String objects you could do that like so...
144
142
 
145
- LogJams logging facilities consist of two class level methods (one called log()
146
- and one called log=()) and an instance level method called log(). The log()
147
- methods retrieve the Logger instance to be used by the class instances. The
148
- log=() method allows the Logger instance associated with a class to be altered.
149
- You should take care when assigning a logger in this fashion as assigning it
150
- on one class may have an impact on many classes (e.g. if there is only a single
151
- default logger defined in configuration).
143
+ String.set_logger_name("string_logger")
152
144
 
153
- The following complete (although contrived) example gives an overview of how
154
- this would work...
145
+ With your code you can obtain a logger instance and then use the method common
146
+ to Ruby's Logger class on the object returned. So, to log a statement at the
147
+ info level in a piece of code you would do something like this...
155
148
 
156
- ```
157
- require 'rubygems'
158
- require 'logjam'
159
-
160
- class Writer
161
- def initialize(stream=STDOUT)
162
- @stream = stream
163
- end
164
-
165
- def echo(message)
166
- log.debug("Echoed: #{message}")
167
- @stream.puts message
168
- end
169
- end
170
-
171
- begin
172
- LogJam.configure({:loggers => [{:name => "echo",
173
- :file => "echo.log"}]})
174
-
175
- writer = Writer.new
176
- writer.echo "This is a string containing my message."
177
- rescue => error
178
- puts "ERROR: #{error}\n" + error.backtrace.join("\n")
179
- end
180
- ```
149
+ log.info("This is a statement that I am logging.")
181
150
 
182
- In this example we create a Writer class that can echo a String on a stream. In
183
- doing this it also logs the message echoed at the debug level. We can simply
184
- call the log() method to get the class logger.
151
+ Consult the documentation of the Ruby Logger class for more information on the
152
+ methods and logging levels available.
185
153
 
186
- In the later section of the code we configure the LogJam system to have a single
187
- Logger that writes to the echo.log file. We then create a Writer instance and
188
- use it to write a simple message. This message should appear on the standard
189
- output stream and be logged to the echo.log file.
190
-
191
154
  ## Advanced Usage
192
155
 
193
156
  The hope would be that this library can be used in the creation of other
@@ -257,7 +220,7 @@ Hash
257
220
 
258
221
  YAML
259
222
  ```
260
- :loggers:
223
+ :loggers:
261
224
  - :default: true
262
225
  :file: application.log
263
226
  ```
@@ -288,14 +251,14 @@ Hash
288
251
 
289
252
  YAML
290
253
  ```
291
- :loggers:
254
+ :loggers:
292
255
  - :default: true
293
256
  :file: STDOUT
294
257
  :level: UNKNOWN
295
258
  :name: silent
296
259
  - :file: STDOUT
297
260
  :name: verbose
298
- :aliases:
261
+ :aliases:
299
262
  database: verbose
300
263
  ```
301
264
 
@@ -330,13 +293,13 @@ Hash
330
293
 
331
294
  YAML
332
295
  ```
333
- :loggers:
296
+ :loggers:
334
297
  - :default: true
335
298
  :file: ./log/main.log
336
299
  :name: main
337
300
  - :file: ./log/secondary.log
338
301
  :name: secondary
339
- :aliases:
302
+ :aliases:
340
303
  database: secondary
341
304
  model: secondary
342
305
  controller: main
@@ -356,20 +319,20 @@ JSON
356
319
 
357
320
  ## Testing
358
321
 
359
- LogJam uses the minitest Ruby library for testing. The best approach to running
322
+ LogJam uses the RSpec Ruby library for testing. The best approach to running
360
323
  the tests are to create a new gemset (assuming you're using RVM), do a bundle
361
324
  install on this gemset from within the LogJam root directory and then use a
362
325
  command such as the following to run the tests...
363
326
 
364
327
  ```
365
- $> rake test:unit
328
+ $> rspec
366
329
  ```
367
330
 
368
- Individual tests can be run by including a definition for TESTS which contains
369
- a comma separated_list of the tests to be run. For example...
331
+ Individual tests can be run by appending the path to the file that you want to
332
+ execute after the ```rspec``` command. For example...
370
333
 
371
334
  ```
372
- $> rake test:unit TESTS=object,apply
335
+ $> rake spec/logjam_spec.rb
373
336
  ```
374
337
 
375
- ...would run only the object and apply tests.
338
+ ...would run only the the tests in the logjam_spec.rb test file.
data/lib/logjam.rb CHANGED
@@ -3,11 +3,187 @@
3
3
  # Copyright (c), 2012 Peter Wood
4
4
  # See the license.txt for details of the licensing of the code in this file.
5
5
 
6
+ require 'forwardable'
6
7
  require 'logger'
8
+ require 'configurative'
7
9
  require 'logjam/version'
8
10
  require 'logjam/exceptions'
9
- require 'logjam/logjam_logger'
10
- require 'logjam/logjam'
11
+ require 'logjam/configuration'
12
+ require 'logjam/logger'
11
13
  require 'logjam/object'
12
14
 
13
- LogJam.configure(nil)
15
+ module LogJam
16
+ # Module constants.
17
+ LEVEL_MAP = {"debug" => Logger::DEBUG,
18
+ "info" => Logger::INFO,
19
+ "warn" => Logger::WARN,
20
+ "error" => Logger::ERROR,
21
+ "fatal" => Logger::FATAL,
22
+ "unknown" => Logger::UNKNOWN}
23
+ STREAM_MAP = {"stdout" => STDOUT,
24
+ "stderr" => STDERR}
25
+
26
+ # Module static properties.
27
+ @@logjam_modules = {}
28
+ @@logjam_loggers = {}
29
+ @@logjam_contexts = {}
30
+
31
+ # This method is used to configure the LogJam module with the various loggers
32
+ # it will use.
33
+ #
34
+ # ==== Parameters
35
+ # source:: Either a Hash containing the configuration to be used or nil to
36
+ # indicate the use of default configuration settings.
37
+ def self.configure(source=nil)
38
+ @@logjam_modules = {}
39
+ @@logjam_loggers = {}
40
+ LogJam.process_configuration(source ? source : Configuration.instance)
41
+ end
42
+
43
+ # This method is used to install logging facilities at the class level for a
44
+ # given class. Once 'logified' a class will possess two new methods. The
45
+ # first, #log(), retrieves the logger associated with the class. The second,
46
+ # #log=(), allows the assignment of the logger associated with the class.
47
+ # Note that changing the logger associated with a class will impact all other
48
+ # classes that use the same logger.
49
+ #
50
+ # ==== Parameters
51
+ # target:: The target class that is to be extended.
52
+ # name:: The name of the logger to be used by the class. Defaults to nil
53
+ # to indicate use of the default logger.
54
+ # context:: A Hash of additional parameters that are specific to the class
55
+ # to which LogJam is being applied.
56
+ def self.apply(target, name=nil, context={})
57
+ @@logjam_contexts[target] = {}.merge(context)
58
+ target.extend(LogJam.get_module(name, @@logjam_contexts[target]))
59
+ target.send(:define_method, :log) {LogJam.get_logger(name)} if !target.method_defined?(:log)
60
+ end
61
+
62
+ # This method attempts to fetch the logger for a specified name. If this
63
+ # logger does not exist then a default logger will be returned instead.
64
+ #
65
+ # ==== Parameters
66
+ # name:: The name of the logger to retrieve.
67
+ def self.get_logger(name=nil)
68
+ LogJam.process_configuration(nil) if @@logjam_loggers.empty?
69
+ @@logjam_loggers.fetch(name, @@logjam_loggers[nil])
70
+ end
71
+
72
+ # This method fetches a list of the names currently defined within the LogJam
73
+ # internal settings.
74
+ def self.names
75
+ @@logjam_loggers.keys.compact
76
+ end
77
+
78
+ # A convenience mechanism that provides an instance level access to the
79
+ # class level logger.
80
+ def log
81
+ self.class.log
82
+ end
83
+
84
+ private
85
+
86
+ # This method fetches the module associated with a name. If the module does
87
+ # not exist the default module is returned instead.
88
+ #
89
+ # ==== Parameters
90
+ # name:: The name associated with the module to return.
91
+ # context:: The context that applies to the module to be retrieved.
92
+ def self.get_module(name, context={})
93
+ LogJam.create_module(name)
94
+ end
95
+
96
+ # This method processes a logger configuration and generates the appropriate
97
+ # set of loggers and internal objects from it.
98
+ #
99
+ # ==== Parameters
100
+ # settings:: A collection of the settings to be processed.
101
+ def self.process_configuration(settings)
102
+ settings = Configurative::SettingsParser.new.parse(settings) if settings.kind_of?(Hash)
103
+ if settings && !settings.empty?
104
+ loggers = settings.loggers
105
+ if loggers
106
+ if loggers.kind_of?(Array)
107
+ loggers.each {|definition| LogJam.create_logger(definition)}
108
+ elsif loggers.kind_of?(Hash)
109
+ LogJam.create_logger(loggers)
110
+ else
111
+ raise Error, "The loggers configuration entry is in an "\
112
+ "unrecognised format. Must be either a Hash or an "\
113
+ "Array."
114
+ end
115
+ end
116
+
117
+ aliases = settings.aliases
118
+ if aliases
119
+ aliases.each do |name, equivalent|
120
+ @@logjam_loggers[name] = @@logjam_loggers[equivalent]
121
+ @@logjam_modules[name] = LogJam.get_module(equivalent)
122
+ end
123
+ end
124
+ end
125
+
126
+ # Create a default logger if one hasn't been specified.
127
+ LogJam.create_logger({default: true, file: "STDOUT"}) if @@logjam_loggers[nil].nil?
128
+ end
129
+
130
+ # This method is used to create an anonymous module under a given name (if it
131
+ # doesn't already exist) and return it to the caller.
132
+ #
133
+ # ==== Parameters
134
+ # name:: The name to create the module under.
135
+ def self.create_module(name)
136
+ if !@@logjam_modules.include?(name)
137
+ # Create the anonymous module and add methods to it.
138
+ @@logjam_modules[name] = Module.new
139
+ @@logjam_modules[name].send(:define_method, :log) do
140
+ LogJam.get_logger(name)
141
+ end
142
+ @@logjam_modules[name].send(:define_method, :log=) do |logger|
143
+ LogJam.get_logger(name).logger = logger
144
+ end
145
+ end
146
+ @@logjam_modules[name]
147
+ end
148
+
149
+ # This method creates a logger from a given definition. A definition should
150
+ # be a Hash containing the values that are used to configure the Logger with.
151
+ #
152
+ # ==== Parameters
153
+ # definition:: A Hash containing the configuration details for the logger.
154
+ def self.create_logger(definition)
155
+ # Fetch the configuration values.
156
+ definition = to_definition(definition)
157
+ rotation = definition.rotation
158
+ max_size = definition.max_size
159
+ device = STREAM_MAP.fetch(definition.file.downcase.strip, definition.file)
160
+
161
+ if rotation.kind_of?(String) && /^\s*\d+\s*$/ =~ rotation
162
+ rotation = rotation.to_i
163
+ rotation = 0 if rotation < 0
164
+ end
165
+
166
+ max_size = max_size.to_i if !max_size.nil? && max_size.kind_of?(String)
167
+ max_size = 1048576 if !max_size.nil? && max_size < 1024
168
+
169
+ # Create the actual logger and associated module.
170
+ logger = LogJam::Logger.new(device, rotation, max_size)
171
+ logger.level = LEVEL_MAP.fetch(definition.level.downcase.strip, Logger::DEBUG)
172
+ logger.name = definition.name
173
+ logger.progname = definition.name
174
+ @@logjam_loggers[definition.name] = logger
175
+ logger_module = LogJam.create_module(name)
176
+ if definition.default
177
+ @@logjam_loggers[nil] = logger
178
+ @@logjam_modules[nil] = logger_module
179
+ end
180
+ logger
181
+ end
182
+
183
+ def self.to_definition(settings)
184
+ settings = Configurative::SettingsParser.new.parse(settings) if settings.kind_of?(Hash)
185
+ settings.file = "stdout" if !settings.include?(:file) || settings.file == ""
186
+ settings.level = "debug" if !settings.include?(:level) || settings.level == ""
187
+ settings
188
+ end
189
+ end