logjam 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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