semantic_logger 2.7.0 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c16da0b153fc6641eb7fc40fdf7cb8ee2ffe74cd
4
- data.tar.gz: 4907de6046cbbf1b7c5ea6f513faecc18bea3efb
3
+ metadata.gz: 51a1e44ec07ae96cd5dad5b3a3dc2c6377960b15
4
+ data.tar.gz: 820304cb8615f71b1f921869fcc70e0766ef7258
5
5
  SHA512:
6
- metadata.gz: b145848428794d626f5a5d3cd276c4d4929c868346962a24a502d618274c6cf967a7e6d028017c2c02b25a801b0dba014bb52a6cd29d8ef06153d5e9a9c25df7
7
- data.tar.gz: bb09287501c04bb4c6db41231f8e3245a42f8b20d645d3fe63389dc875a27ba0f0187840a02756e05db346168435e755ab7f2a6f88d25340328a77115a1d06cf
6
+ metadata.gz: 3faa5b434bc505b9d2045f7f00c91feae01f89bcd3a498ea7e0fe8f831d75fd276f05fd059ddd149ba2755d3ab6dfa3abeddc6d9c025191e75a4517c304c155f
7
+ data.tar.gz: d0691ea0ad358cf91c266e5e9005d510aed493f4adb8255423a425133c7258248d53482420e06623db1009a620adcd50ec616c56ad687757a4427eabbc16c603
data/README.md CHANGED
@@ -1,26 +1,35 @@
1
- semantic_logger
1
+ semantic_logger [![Build Status](https://secure.travis-ci.org/reidmorrison/semantic_logger.png?branch=master)](http://travis-ci.org/reidmorrison/semantic_logger)
2
2
  ===============
3
3
 
4
- Improved logging for Ruby
4
+ Scalable, next generation logging for Ruby
5
5
 
6
6
  * http://github.com/reidmorrison/semantic_logger
7
7
 
8
- ## Note:
9
-
10
- As of SemanticLogger V2.0 the Rails logging is no longer automatically replaced
11
- when including SemanticLogger. Include the [rails_semantic_logger](http://github.com/reidmorrison/rails_semantic_logger)
12
- gem to replace the Rails default logger with SemanticLogger
13
-
14
8
  ## Overview
15
9
 
16
- Semantic Logger takes logging in Ruby to a new level by adding several new
10
+ Semantic Logger takes logging in Ruby to the next level by adding several new
17
11
  capabilities to the commonly used Logging API:
18
12
 
13
+ High Performance
14
+
15
+ * Logging is performed in a separate thread so as not to slow down the application
16
+ whilst logging to one or more destinations
17
+ * Supports logging from hundreds of concurrent threads in the same process
18
+
19
+ Drop-in Replacement
20
+
21
+ * Simple drop-in replacement for the Ruby, or the Rails loggers
22
+ * Supports current common logging interface
23
+ * No changes to existing to code to use new logger ( other than replacing the logger )
24
+
19
25
  Dynamic
20
26
 
21
27
  * Increase the log level at runtime for just one class
22
28
  * For example enable debug level logging for a single class (logging instance)
23
29
  while the program is running to get more detailed logging in production for just that class
30
+ * Change the default global logging level for all classes, unless that class has
31
+ specifically been overridden above
32
+ * Use UNIX signals to change the log level for a running process
24
33
 
25
34
  Tagged Logging
26
35
 
@@ -30,17 +39,6 @@ Tagged Logging
30
39
  down log entries for a single call that is mixed in with log entries
31
40
  from hundreds of other log entries
32
41
 
33
- High Performance
34
-
35
- * Logging is performed in a separate thread so as not to impact performance of
36
- running code
37
-
38
- Customizable
39
-
40
- * Custom formatting by destination
41
- * Easy to "roll your own" destination (Appender).
42
- For example to log to Hadoop, Redis, etc..
43
-
44
42
  Payload support
45
43
 
46
44
  * Aside from the regular log message, a hash payload can also be supplied with
@@ -55,15 +53,23 @@ Exceptions
55
53
  to text destinations and writes the exception elements as a hash to NOSQL
56
54
  destinations
57
55
 
58
- Drop-in Replacement
56
+ Benchmarking
59
57
 
60
- * Simple drop-in replacement for the Ruby, or the Rails loggers
61
- * Supports current common logging interface
62
- * No changes to existing to code to use new logger ( other than replacing the logger )
58
+ * The performance of any block of code can be measured and logged at the same time
59
+ depending on the active log level
60
+ * Supports only logging when the block of code exceeds a specified number of milli-seconds.
61
+ Makes it easy to find bottlenecks when the system suddenly slows down in production
62
+ * Exceptions thrown in the block of code can also be logged so as to aid in finding
63
+ exceptions that may be discarded or hidden by the application
64
+ * Benchmarked data can also be forwarded to external metric systems so that the
65
+ performance of these blocks can be measured and/or monitored over time
63
66
 
64
67
  Thread Safe
65
68
 
66
- * Semantic Logger ensures that all logging is fully thread-safe
69
+ * Semantic Logger is completely thread safe and all methods can be called
70
+ concurrently from any thread
71
+ * Tagged logging keeps any tagging data on a per-thread basis to ensure that
72
+ tags from different threads are not inter-mingled
67
73
  * Supports highly concurrent environments running hundreds of threads
68
74
  * Each appender writes all log entries sequentially in the appender thread so
69
75
  that log entries are written in the correct sequence
@@ -73,8 +79,9 @@ Thread Safe
73
79
 
74
80
  Thread Aware
75
81
 
76
- * Includes the process and thread id information in every log entry
77
- * If running JRuby it will also include the name of the thread for every log entry
82
+ * Includes the process id, and thread name or thread id in every log entry so that
83
+ log entries from different processes and even threads are easily discernable
84
+ * Human readable names can be assigned to every thread for logging purposes
78
85
 
79
86
  Trace Level
80
87
 
@@ -93,11 +100,6 @@ Multiple Destinations
93
100
  For example, only log :info and above to MongoDB, or :warn and above to a
94
101
  second log file
95
102
 
96
- Benchmarking
97
-
98
- * The performance of any block of code can be measured and logged at the same time
99
- depending on the active log level
100
-
101
103
  Semantic Capabilities
102
104
 
103
105
  * With Semantic Logger it is simple to mix-in additional semantic information with
@@ -135,12 +137,11 @@ NOSQL Destinations
135
137
  }
136
138
  ```
137
139
 
138
- Thread Safe
140
+ Customizable
139
141
 
140
- * Semantic Logger is completely thread safe and all methods can be called
141
- concurrently from any thread
142
- * Tagged logging keeps any tagging data on a per-thread basis to ensure that
143
- tags from different threads are not inter-mingled
142
+ * Custom formatting by destination
143
+ * Easy to "roll your own" destination (Appender).
144
+ For example to log to Hadoop, Redis, etc..
144
145
 
145
146
  ## Introduction
146
147
 
@@ -210,24 +211,30 @@ Or to query whether a specific log level is set
210
211
  logger.info?
211
212
  ```
212
213
 
213
- The following logging methods are available
214
+ The following traditional logging methods are available
215
+
216
+ ```ruby
217
+ logger.trace("Low level trace information such as data sent over a socket")
218
+ logger.debug("Debugging information to aid with problem determination")
219
+ logger.info("Informational message such as request received")
220
+ logger.warn("Warn about something in the system")
221
+ logger.error("An error occurred during processing")
222
+ logger.fatal("Oh no something really bad happened")
223
+ ```
224
+
225
+ Each of the above calls can take additional parameters, for example:
214
226
 
215
227
  ```ruby
216
- trace(message, payload=nil, exception=nil, &block)
217
- debug(message, payload=nil, exception=nil, &block)
218
- info(message, payload=nil, exception=nil, &block)
219
- warn(message, payload=nil, exception=nil, &block)
220
- error(message, payload=nil, exception=nil, &block)
221
- fatal(message, payload=nil, exception=nil, &block)
228
+ log.info(message, payload=nil, exception=nil, &block)
222
229
  ```
223
230
 
224
231
  Parameters
225
232
 
226
- - message: The text message to log.
233
+ - message: The text message to log.
227
234
  Mandatory only if no block is supplied
228
- - payload: Optional, either a Ruby Exception object or a Hash
235
+ - payload: Optional, either a Ruby Exception object or a Hash
229
236
  - exception: Optional, Ruby Exception object. Allows both an exception and a payload to be logged
230
- - block: The optional block is executed only if the corresponding log level
237
+ - block: The optional block is executed only if the corresponding log level
231
238
  is active. Can be used to prevent unnecessary calculations of debug data in
232
239
  production.
233
240
 
@@ -292,12 +299,21 @@ The exception will flow through to the caller unchanged
292
299
  The following benchmarking methods are available
293
300
 
294
301
  ```ruby
295
- benchmark_trace(message, params=nil, &block)
296
- benchmark_debug(message, params=nil, &block)
297
- benchmark_info(message, params=nil, &block)
298
- benchmark_warn(message, params=nil, &block)
299
- benchmark_error(message, params=nil, &block)
300
- benchmark_fatal(message, params=nil, &block)
302
+ logger.benchmark_trace("Low level trace information such as data sent over a socket")
303
+ logger.benchmark_debug("Debugging information to aid with problem determination")
304
+ logger.benchmark_info("Informational message such as request received")
305
+ logger.benchmark_warn("Warn about something in the system")
306
+ logger.benchmark_error("An error occurred during processing")
307
+ logger.benchmark_fatal("Oh no something really bad happened")
308
+ logger.benchmark(:info, "Informational message such as request received")
309
+ ```
310
+
311
+ Each of the above calls can take additional parameters, for example:
312
+
313
+ ```ruby
314
+ log.benchmark_info(message, params=nil) do
315
+ # Measure how long it takes to run this block of code
316
+ end
301
317
  ```
302
318
 
303
319
  Parameters
@@ -442,6 +458,59 @@ that was written during the second call to the ExternalSupplier:
442
458
  2013-11-07 16:19:26.683 I [35674:main] (0.0ms) ExternalSupplier -- Calling external interface
443
459
  ```
444
460
 
461
+ ### Change the global default logging level at runtime
462
+
463
+ Log levels can be changed using signals on operating systems that support them.
464
+ This allows log levels to be changed externally without requiring a restart
465
+ of the running process.
466
+
467
+ When the signal is raised, the global default log level rotates through the following
468
+ log levels in the following order, starting from the current global default level:
469
+
470
+ ```ruby
471
+ :warn, :info, :debug, :trace
472
+ ```
473
+
474
+ If the current level is :trace it wraps around back to :warn
475
+
476
+ Example:
477
+
478
+ ```
479
+ kill -SIGUSR2 1234
480
+ ```
481
+
482
+ #### Enabling Log Level Signal handler
483
+
484
+ On startup SemanticLogger does not register any signals so that it does not
485
+ interfere with any existing signal handlers. In order to enable the above log level
486
+ changes the signal handler must be registered by calling `SemanticLogger.add_signal_handler`
487
+
488
+ ```ruby
489
+ require 'semantic_logger'
490
+
491
+ # Enable signal handling for this process
492
+ SemanticLogger.add_signal_handler('USR2')
493
+
494
+ SemanticLogger.add_appender('development.log')
495
+
496
+ logger = SemanticLogger['Example']
497
+ logger.info "Hello World"
498
+ ```
499
+
500
+ Note: The changes to the logging level will not change for any classes where the
501
+ log_level was set explicity within the application itself. The above signal only changes
502
+ the global default level, which is used by loggers when their log level has not been changed.
503
+
504
+ #### Change the log level without using signals
505
+
506
+ If the application has another means of communicating without needing signals,
507
+ the global default log level can be modified using `SemanticLogger.default_level=`
508
+
509
+ ```ruby
510
+ # Change the global default logging level for active loggers
511
+ SemanticLogger.default_level = :debug
512
+ ```
513
+
445
514
  ### Tagged Logging
446
515
 
447
516
  Semantic Logger allows any Ruby or Rails program to also include tagged logging.
@@ -523,27 +592,17 @@ logger.benchmark_info "Calling external interface" do
523
592
  end
524
593
  ```
525
594
 
526
- Sending the performance information gathered above to something like NewRelic
527
- is also very useful, and we can end up with:
528
-
529
- ```ruby
530
- logger.benchmark_info "Calling external interface" do
531
- self.class.trace_execution_scoped(['Custom/slow_action/beginning_work']) do
532
- # Code to call the external supplier ...
533
- end
534
- end
535
- ```
536
-
537
- Rather than wrapping the code everywhere with two blocks, a single subscriber can
538
- be setup in config/initializers/semantic_logger_metrics.rb:
595
+ A single subscriber can be defined to collect all the metrics and forward them
596
+ for example to NewRelic:
539
597
 
540
598
  ```ruby
599
+ # config/initializers/semantic_logger_metrics.rb
541
600
  SemanticLogger.on_metric do |log_struct|
542
601
  ::NewRelic::Agent.record_metric(log_struct.metric, log_struct.duration)
543
602
  end
544
603
  ```
545
604
 
546
- Then update the log entry as follows:
605
+ Add the :metric option to the log entry as follows:
547
606
 
548
607
  ```ruby
549
608
  logger.benchmark_info "Calling external interface", :metric => 'Custom/slow_action/beginning_work' do
@@ -709,7 +768,7 @@ to write your own custom formatter or log appender it is necessary to understand
709
768
  the fields:
710
769
 
711
770
  ```ruby
712
- Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index)
771
+ Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric)
713
772
  ```
714
773
  level [Symbol]
715
774
 
@@ -748,6 +807,10 @@ level_index [Integer]
748
807
 
749
808
  * Internal use only. Index of the log level
750
809
 
810
+ metric [Object]
811
+
812
+ * Object supplied when the benchmark api was called
813
+
751
814
  ### Mixing Logging Levels
752
815
 
753
816
  It is sometimes useful to log a subset of the log messages to a separate file
@@ -966,29 +1029,29 @@ logger.info "Hello World"
966
1029
 
967
1030
  Look at the [existing appenders](https://github.com/reidmorrison/semantic_logger/tree/master/lib/semantic_logger/appender) for good examples
968
1031
 
969
- To have your appender included in the standard list of appenders follow the fork
970
- instructions below.
971
- Very Important: New appenders will not be accepted without complete working tests.
1032
+ To have your appender included in the standard list of appenders, submit it along
1033
+ with complete working tests.
972
1034
  See the [MongoDB Appender Test](https://github.com/reidmorrison/semantic_logger/blob/master/test/appender_mongodb_test.rb) for an example.
973
1035
 
974
1036
  ## Dependencies
975
1037
 
976
- - Ruby MRI 1.8.7, 1.9.3 (or above) Or, JRuby 1.6.3 (or above)
977
- - Optional: To log to MongoDB, Mongo Ruby Driver 1.5.2 or above
1038
+ See [.travis.yml](https://github.com/reidmorrison/semantic_logger/.travis.yml) for the list of tested Ruby platforms
1039
+
1040
+ The following gems are only required when their corresponding appenders are being used,
1041
+ and are therefore not automatically included by this gem:
1042
+ - MongoDB Appender: mongo 1.9.2 or above
1043
+ - Syslog Appender: syslog_protocol 0.9.2 or above
1044
+ - Syslog Appender to a remote syslogng server over TCP or UDP: resilient_socket 0.5.0 or above
978
1045
 
979
1046
  ## Install
980
1047
 
981
1048
  gem install semantic_logger
982
1049
 
983
- To log to MongoDB, it also needs the Ruby Mongo Driver
984
-
985
- gem install mongo
986
-
987
- ## Future
1050
+ ## Upgrade Notes:
988
1051
 
989
- - Add support for a configuration file that can set log level by class name
990
- - Configuration file to support adding appenders
991
- - Based on end-user demand add appenders for: hadoop, redis, etc..
1052
+ As of SemanticLogger V2.0 the Rails logging is no longer automatically replaced
1053
+ when including SemanticLogger. Include the [rails_semantic_logger](http://github.com/reidmorrison/rails_semantic_logger)
1054
+ gem to replace the Rails default logger with SemanticLogger
992
1055
 
993
1056
  Meta
994
1057
  ----
@@ -1013,7 +1076,7 @@ Marc Bellingrath :: marrrc.b@gmail.com
1013
1076
  License
1014
1077
  -------
1015
1078
 
1016
- Copyright 2012, 2013 Reid Morrison
1079
+ Copyright 2012, 2013, 2014 Reid Morrison
1017
1080
 
1018
1081
  Licensed under the Apache License, Version 2.0 (the "License");
1019
1082
  you may not use this file except in compliance with the License.
data/Rakefile CHANGED
@@ -1,15 +1,18 @@
1
- lib = File.expand_path('../lib/', __FILE__)
2
- $:.unshift lib unless $:.include?(lib)
3
-
4
- require 'rubygems'
5
- require 'rubygems/package'
6
1
  require 'rake/clean'
7
2
  require 'rake/testtask'
3
+
4
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
8
5
  require 'semantic_logger/version'
9
6
 
10
- desc "Build gem"
11
- task :gem do |t|
12
- Gem::Package.build(Gem::Specification.load('semantic_logger.gemspec'))
7
+ task :gem do
8
+ system "gem build semantic_logger.gemspec"
9
+ end
10
+
11
+ task :publish => :gem do
12
+ system "git tag -a v#{SemanticLogger::VERSION} -m 'Tagging #{SemanticLogger::VERSION}'"
13
+ system "git push --tags"
14
+ system "gem push semantic_logger-#{SemanticLogger::VERSION}.gem"
15
+ system "rm semantic_logger-#{SemanticLogger::VERSION}.gem"
13
16
  end
14
17
 
15
18
  desc "Run Test Suite"
@@ -21,3 +24,5 @@ task :test do
21
24
 
22
25
  Rake::Task['functional'].invoke
23
26
  end
27
+
28
+ task :default => :test
@@ -80,18 +80,25 @@ module SemanticLogger
80
80
  ############################################################################
81
81
  protected
82
82
 
83
- # By default the appender should log everything that is sent to it by
84
- # the loggers. That way the loggers control the log level
85
- # By setting the log level to a higher value the appender can be setup
86
- # to log for example only :warn or higher while other appenders
87
- # are able to log lower level information
88
- def initialize(level, &block)
83
+ # Initializer for Abstract Class SemanticLogger::Appender
84
+ #
85
+ # Parameters
86
+ # level [Symbol]
87
+ # Only allow log entries of this level or higher to be written to this appender
88
+ # For example if set to :warn, this appender would only log :warn and :fatal
89
+ # log messages when other appenders could be logging :info and lower
90
+ #
91
+ # filter [RegExp|Proc]
92
+ # Optional regular expression to filter log entries based on the class name
93
+ # When filter is a proc, it is passed the entire log struct and must return
94
+ # true or false indicating whether to log the message
95
+ def initialize(level=nil, filter=nil, &block)
89
96
  # Set the formatter to the supplied block
90
97
  @formatter = block || default_formatter
91
98
 
92
99
  # Appenders don't take a class name, so use this class name if an appender
93
100
  # is logged to directly
94
- super(self.class, level || :trace)
101
+ super(self.class, level, filter)
95
102
  end
96
103
 
97
104
  # For JRuby include the Thread name rather than its id
@@ -39,7 +39,7 @@ module SemanticLogger
39
39
  # logger = SemanticLogger['test']
40
40
  # logger.info 'Hello World'
41
41
  #
42
- def initialize(filename, level=nil, &block)
42
+ def initialize(filename, level=nil, filter=nil, &block)
43
43
  raise "filename cannot be null when initializing the SemanticLogging::Appender::File" unless filename
44
44
  @log = if filename.respond_to?(:write) and filename.respond_to?(:close)
45
45
  filename
@@ -49,7 +49,7 @@ module SemanticLogger
49
49
  end
50
50
 
51
51
  # Set the log level and formatter if supplied
52
- super(level, &block)
52
+ super(level, filter, &block)
53
53
  end
54
54
 
55
55
  # After forking an active process call #reopen to re-open
@@ -31,7 +31,7 @@ module SemanticLogger
31
31
  #
32
32
  class MongoDB < SemanticLogger::Appender::Base
33
33
  attr_reader :db, :collection_name
34
- attr_accessor :host_name, :safe, :application
34
+ attr_accessor :host_name, :write_concern, :application
35
35
 
36
36
  # Create a MongoDB Appender instance
37
37
  #
@@ -49,11 +49,10 @@ module SemanticLogger
49
49
  # host_name to include in the document logged to Mongo
50
50
  # Default: first part of host name extracted from Socket
51
51
  #
52
- # :safe [Boolean]
53
- # Whether to use safe write for logging
54
- # Not recommended to change this value except to diagnose connection
55
- # issues or when log entries are not being written to Mongo
56
- # Default: false
52
+ # :write_concern [Integer]
53
+ # Write concern to use
54
+ # see: http://docs.mongodb.org/manual/reference/write-concern/
55
+ # Default: 0
57
56
  #
58
57
  # :application [String]
59
58
  # Name of the application to include in the document written to mongo
@@ -75,12 +74,18 @@ module SemanticLogger
75
74
  # :level [Symbol]
76
75
  # Only allow log entries of this level or higher to be written to MongoDB
77
76
  #
77
+ # :filter [Regexp|Proc]
78
+ # RegExp: Only include log messages where the class name matches the supplied
79
+ # regular expression. All other messages will be ignored
80
+ # Proc: Only include log messages where the supplied Proc returns true
81
+ # The Proc must return true or false
78
82
  def initialize(params={}, &block)
79
83
  @db = params[:db] || raise('Missing mandatory parameter :db')
80
84
  @collection_name = params[:collection_name] || 'semantic_logger'
81
85
  @host_name = params[:host_name] || Socket.gethostname.split('.').first
82
- @safe = params[:safe] || false
86
+ @write_concern = params[:write_concern] || 0
83
87
  @application = params[:application]
88
+ filter = params[:filter]
84
89
 
85
90
  # Create a collection that will hold the lesser of 1GB space or 10K documents
86
91
  @collection_size = params[:collection_size] || 1024**3
@@ -90,7 +95,7 @@ module SemanticLogger
90
95
  create_indexes
91
96
 
92
97
  # Set the log level and formatter
93
- super(params[:level], &block)
98
+ super(params[:level], filter, &block)
94
99
  end
95
100
 
96
101
  # Create the required capped collection
@@ -103,7 +108,9 @@ module SemanticLogger
103
108
  #
104
109
  # Creates an index based on tags to support faster lookups
105
110
  def create_indexes
106
- db.create_collection(collection_name, {:capped => true, :size => @collection_size, :max => @collection_max})
111
+ options = {:capped => true, :size => @collection_size}
112
+ options[:max] = @collection_max if @collection_max
113
+ db.create_collection(collection_name, options)
107
114
  collection.ensure_index('tags')
108
115
  end
109
116
 
@@ -172,9 +179,7 @@ module SemanticLogger
172
179
  # Log the message to MongoDB
173
180
  def log(log)
174
181
  # Insert log entry into Mongo
175
- # Use safe=>false so that we do not wait for it to be written to disk, or
176
- # for the response from the MongoDB server
177
- collection.insert(formatter.call(log), :safe=>safe) if level_index <= (log.level_index || 0)
182
+ collection.insert(formatter.call(log), :w=>@write_concern) if level_index <= (log.level_index || 0)
178
183
  end
179
184
 
180
185
  end
@@ -143,22 +143,24 @@ module SemanticLogger
143
143
  # For a list of options see the resilient_socket documentation:
144
144
  # https://github.com/reidmorrison/resilient_socket/blob/master/lib/resilient_socket/tcp_client.rb
145
145
  def initialize(params = {}, &block)
146
- params = params.dup
147
- @ident = params.delete(:ident) || 'ruby'
148
- options = params.delete(:options) || (::Syslog::LOG_PID | ::Syslog::LOG_CONS)
149
- @facility = params.delete(:facility) || ::Syslog::LOG_USER
150
- level = params.delete(:level)
151
- level_map = params.delete(:level_map)
146
+ params = params.dup
147
+ @ident = params.delete(:ident) || 'ruby'
148
+ options = params.delete(:options) || (::Syslog::LOG_PID | ::Syslog::LOG_CONS)
149
+ @facility = params.delete(:facility) || ::Syslog::LOG_USER
150
+ filter = params.delete(:filter)
151
+ level = params.delete(:level)
152
+ level_map = params.delete(:level_map)
152
153
  @level_map = DEFAULT_LEVEL_MAP.dup
153
154
  @level_map.update(level_map) if level_map
154
- @server = params.delete(:server) || 'syslog://localhost'
155
- uri = URI(@server)
156
- @host = uri.host || 'localhost'
157
- @protocol = (uri.scheme || :syslog).to_sym
155
+ @server = params.delete(:server) || 'syslog://localhost'
156
+ uri = URI(@server)
157
+ @host = uri.host || 'localhost'
158
+ @protocol = (uri.scheme || :syslog).to_sym
158
159
  raise "Unknown protocol #{@protocol}!" unless [:syslog, :tcp, :udp].include?(@protocol)
159
- @host = 'localhost' if @protocol == :syslog
160
- @port = URI(@server).port || 514
161
- @local_hostname = params.delete(:local_hostname) || Socket.gethostname || `hostname`.strip
160
+ @host = 'localhost' if @protocol == :syslog
161
+ @port = URI(@server).port || 514
162
+
163
+ @local_hostname = params.delete(:local_hostname) || Socket.gethostname || `hostname`.strip
162
164
  tcp_client_options = params.delete(:tcp_client)
163
165
 
164
166
  # Warn about any unknown configuration options.
@@ -194,7 +196,7 @@ module SemanticLogger
194
196
  raise "Unsupported protocol: #{protocol}"
195
197
  end
196
198
 
197
- super(level, &block)
199
+ super(level, filter, &block)
198
200
  end
199
201
 
200
202
  # Write the log using the specified protocol and host.
@@ -27,13 +27,13 @@ module SemanticLogger
27
27
  #
28
28
  # Note: Since the log level is controlled by setting the Ruby or Rails logger directly
29
29
  # the level is ignored for this appender
30
- def initialize(logger, &block)
30
+ def initialize(logger, filter=nil, &block)
31
31
  raise "logger cannot be null when initiailizing the SemanticLogging::Appender::Wrapper" unless logger
32
32
  @logger = logger
33
33
 
34
34
  # Set the formatter to the supplied block
35
35
  @formatter = block || self.default_formatter
36
- super(:trace, &block)
36
+ super(nil, filter, &block)
37
37
  end
38
38
 
39
39
  # Pass log calls to the underlying Rails, log4j or Ruby logger
@@ -9,22 +9,27 @@
9
9
  module SemanticLogger
10
10
  class Base
11
11
  # Class name to be logged
12
- attr_accessor :name
13
-
14
- attr_reader :level
12
+ attr_accessor :name, :filter
15
13
 
16
14
  # Set the logging level for this logger
17
15
  #
18
- # Note: This level is only for this particular appender. It does not override
16
+ # Note: This level is only for this particular instance. It does not override
19
17
  # the log level in any logging instance or the default log level
20
18
  # SemanticLogger.default_level
21
19
  #
22
- # Must be one of the values in SemanticLogger::LEVELS
20
+ # Must be one of the values in SemanticLogger::LEVELS, or
21
+ # nil if this logger instance should use the global default log_level
23
22
  def level=(level)
24
- @level_index = self.class.map_level_to_index(level)
23
+ @level_index = SemanticLogger.level_to_index(level)
25
24
  @level = level
26
25
  end
27
26
 
27
+ # Returns the current log level if set, otherwise it returns the global
28
+ # default log level
29
+ def level
30
+ @level || SemanticLogger.default_level
31
+ end
32
+
28
33
  # Implement the log level calls
29
34
  # logger.debug(message, hash|exception=nil, &block)
30
35
  #
@@ -69,9 +74,9 @@ module SemanticLogger
69
74
  # logger.info("Parsing received XML", exc)
70
75
  #
71
76
  SemanticLogger::LEVELS.each_with_index do |level, index|
72
- class_eval <<-EOT, __FILE__, __LINE__
77
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
73
78
  def #{level}(message=nil, payload=nil, exception=nil, &block)
74
- if @level_index <= #{index}
79
+ if level_index <= #{index}
75
80
  log_internal(:#{level}, #{index}, message, payload, exception, &block)
76
81
  true
77
82
  else
@@ -80,11 +85,11 @@ module SemanticLogger
80
85
  end
81
86
 
82
87
  def #{level}?
83
- @level_index <= #{index}
88
+ level_index <= #{index}
84
89
  end
85
90
 
86
91
  def benchmark_#{level}(message, params = {}, &block)
87
- if @level_index <= #{index}
92
+ if level_index <= #{index}
88
93
  benchmark_internal(:#{level}, #{index}, message, params, &block)
89
94
  else
90
95
  block.call(params) if block
@@ -93,6 +98,16 @@ module SemanticLogger
93
98
  EOT
94
99
  end
95
100
 
101
+ # Dynamically supply the log level with every benchmark call
102
+ def benchmark(level, message, params = {}, &block)
103
+ index = SemanticLogger.level_to_index(level)
104
+ if level_index <= index
105
+ benchmark_internal(level, index, message, params, &block)
106
+ else
107
+ block.call(params) if block
108
+ end
109
+ end
110
+
96
111
  # Add the supplied tags to the list of tags to log for this thread whilst
97
112
  # the supplied block is active
98
113
  # Returns nil if no tags are currently set
@@ -179,9 +194,31 @@ module SemanticLogger
179
194
  ############################################################################
180
195
  protected
181
196
 
182
- def initialize(klass, level=nil)
183
- @name = klass.is_a?(String) ? klass : klass.name
184
- self.level = level || SemanticLogger.default_level
197
+ # Initializer for Abstract Class SemanticLogger::Base
198
+ #
199
+ # Parameters
200
+ # klass [String]
201
+ # Name of the class, module, or other identifier for which the log messages
202
+ # are being logged
203
+ #
204
+ # level [Symbol]
205
+ # Only allow log entries of this level or higher to be written to this appender
206
+ # For example if set to :warn, this appender would only log :warn and :fatal
207
+ # log messages when other appenders could be logging :info and lower
208
+ #
209
+ # filter [Regexp|Proc]
210
+ # RegExp: Only include log messages where the class name matches the supplied
211
+ # regular expression. All other messages will be ignored
212
+ # Proc: Only include log messages where the supplied Proc returns true
213
+ # The Proc must return true or false
214
+ def initialize(klass, level=nil, filter=nil)
215
+ # Support filtering all messages to this logger using a Regular Expression
216
+ # or Proc
217
+ raise ":filter must be a Regexp or Proc" unless filter.nil? || filter.is_a?(Regexp) || filter.is_a?(Proc)
218
+
219
+ @filter = filter.is_a?(Regexp) ? filter.freeze : filter
220
+ @name = klass.is_a?(String) ? klass : klass.name
221
+ self.level = level unless level.nil?
185
222
  end
186
223
 
187
224
  # Write log data to underlying data storage
@@ -190,7 +227,11 @@ module SemanticLogger
190
227
  end
191
228
 
192
229
  # Return the level index for fast comparisons
193
- attr_reader :level_index
230
+ # Returns the global default level index if the level has not been explicitly
231
+ # set for this instance
232
+ def level_index
233
+ @level_index || SemanticLogger.default_level_index
234
+ end
194
235
 
195
236
  # Struct Log
196
237
  #
@@ -226,27 +267,15 @@ module SemanticLogger
226
267
  # Object supplied when benchmark_x was called
227
268
  Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric)
228
269
 
229
- # Internal method to return the log level as an internal index
230
- # Also supports mapping the ::Logger levels to SemanticLogger levels
231
- def self.map_level_to_index(level)
232
- index = if level.is_a?(Integer) && defined?(::Logger::Severity)
233
- # Mapping of Rails and Ruby Logger levels to SemanticLogger levels
234
- @@map_levels ||= begin
235
- levels = []
236
- ::Logger::Severity.constants.each do |constant|
237
- levels[::Logger::Severity.const_get(constant)] = LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error)
238
- end
239
- levels
240
- end
241
- @@map_levels[level]
242
- elsif level.is_a?(String)
243
- level = level.downcase.to_sym
244
- LEVELS.index(level)
245
- else
246
- LEVELS.index(level)
270
+ # Whether to log the supplied message based on the current filter if any
271
+ def include_message?(struct)
272
+ return true if @filter.nil?
273
+
274
+ if @filter.is_a?(Regexp)
275
+ (@filter =~ struct.name) != nil
276
+ elsif @filter.is_a?(Proc)
277
+ @filter.call(struct) == true
247
278
  end
248
- raise "Invalid level:#{level.inspect} being requested. Must be one of #{LEVELS.inspect}" unless index
249
- index
250
279
  end
251
280
 
252
281
  # Log message at the specified level
@@ -270,7 +299,8 @@ module SemanticLogger
270
299
  if self.payload
271
300
  payload = payload.nil? ? self.payload : self.payload.merge(payload)
272
301
  end
273
- log Log.new(level, Thread.current.name, name, message, payload, Time.now, nil, tags, index, exception)
302
+ struct = Log.new(level, Thread.current.name, name, message, payload, Time.now, nil, tags, index, exception)
303
+ log(struct) if include_message?(struct)
274
304
  end
275
305
 
276
306
  # Measure the supplied block and log the message
@@ -302,14 +332,17 @@ module SemanticLogger
302
332
  if exception
303
333
  case log_exception
304
334
  when :full
305
- log Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, exception, metric)
335
+ struct = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, exception, metric)
336
+ log(struct) if include_message?(struct)
306
337
  when :partial
307
- log Log.new(level, Thread.current.name, name, "#{message} -- Exception: #{exception.class}: #{exception.message}", payload, end_time, duration, tags, index, nil, metric)
338
+ struct = Log.new(level, Thread.current.name, name, "#{message} -- Exception: #{exception.class}: #{exception.message}", payload, end_time, duration, tags, index, nil, metric)
339
+ log(struct) if include_message?(struct)
308
340
  end
309
341
  raise exception
310
342
  elsif duration >= min_duration
311
343
  # Only log if the block took longer than 'min_duration' to complete
312
- log Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric)
344
+ struct = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric)
345
+ log(struct) if include_message?(struct)
313
346
  end
314
347
  end
315
348
  end
@@ -33,9 +33,13 @@ module SemanticLogger
33
33
  # The initial log level to start with for this logger instance
34
34
  # Default: SemanticLogger.default_level
35
35
  #
36
- def initialize(klass, level=nil)
37
- @name = klass.is_a?(String) ? klass : klass.name
38
- self.level = level || SemanticLogger.default_level
36
+ # filter [Regexp|Proc]
37
+ # RegExp: Only include log messages where the class name matches the supplied
38
+ # regular expression. All other messages will be ignored
39
+ # Proc: Only include log messages where the supplied Proc returns true
40
+ # The Proc must return true or false
41
+ def initialize(klass, level=nil, filter=nil)
42
+ super
39
43
  end
40
44
 
41
45
  # Returns [Integer] the number of log entries that have not been written
@@ -13,6 +13,8 @@ module SemanticLogger
13
13
  # will not be changed in any way
14
14
  def self.default_level=(level)
15
15
  @@default_level = level
16
+ # For performance reasons pre-calculate the level index
17
+ @@default_level_index = level_to_index(level)
16
18
  end
17
19
 
18
20
  # Returns the global default log level for new Logger instances
@@ -150,6 +152,25 @@ module SemanticLogger
150
152
  SemanticLogger::Logger.on_metric(&block)
151
153
  end
152
154
 
155
+ # Add a signal handler so that the log level can be changed externally
156
+ # without restarting the process
157
+ #
158
+ # When the signal is raised on this process, the global default log level
159
+ # rotates through the following log levels in the following order, starting
160
+ # from the current global default level:
161
+ # :warn, :info, :debug, :trace
162
+ #
163
+ # If the current level is :trace it wraps around back to :warn
164
+ def self.add_signal_handler(signal='USR2')
165
+ Signal.trap(signal) do
166
+ index = (default_level == :trace) ? LEVELS.find_index(:error) : LEVELS.find_index(default_level)
167
+ new_level = LEVELS[index-1]
168
+ self['SemanticLogger'].warn "Changed global default log level to #{new_level.inspect}"
169
+ self.default_level = new_level
170
+ end
171
+ signal
172
+ end
173
+
153
174
  ############################################################################
154
175
  protected
155
176
 
@@ -158,6 +179,41 @@ module SemanticLogger
158
179
  ############################################################################
159
180
  private
160
181
 
182
+ def self.default_level_index
183
+ @@default_level_index
184
+ end
185
+
186
+ # Returns the symbolic level for the supplied level index
187
+ def index_to_level(level_index)
188
+ LEVELS[level_index]
189
+ end
190
+
191
+ # Internal method to return the log level as an internal index
192
+ # Also supports mapping the ::Logger levels to SemanticLogger levels
193
+ def self.level_to_index(level)
194
+ return if level.nil?
195
+
196
+ index = if level.is_a?(Symbol)
197
+ LEVELS.index(level)
198
+ elsif level.is_a?(String)
199
+ level = level.downcase.to_sym
200
+ LEVELS.index(level)
201
+ elsif level.is_a?(Integer) && defined?(::Logger::Severity)
202
+ # Mapping of Rails and Ruby Logger levels to SemanticLogger levels
203
+ @@map_levels ||= begin
204
+ levels = []
205
+ ::Logger::Severity.constants.each do |constant|
206
+ levels[::Logger::Severity.const_get(constant)] = LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error)
207
+ end
208
+ levels
209
+ end
210
+ @@map_levels[level]
211
+ end
212
+ raise "Invalid level:#{level.inspect} being requested. Must be one of #{LEVELS.inspect}" unless index
213
+ index
214
+ end
215
+
161
216
  # Initial default Level for all new instances of SemanticLogger::Logger
162
217
  @@default_level = :info
218
+ @@default_level_index = level_to_index(@@default_level)
163
219
  end
@@ -1,3 +1,3 @@
1
1
  module SemanticLogger #:nodoc
2
- VERSION = "2.7.0"
2
+ VERSION = "2.8.0"
3
3
  end
@@ -13,6 +13,7 @@ require 'stringio'
13
13
  class AppenderFileTest < Test::Unit::TestCase
14
14
  context SemanticLogger::Appender::File do
15
15
  setup do
16
+ SemanticLogger.default_level = :trace
16
17
  @time = Time.new
17
18
  @io = StringIO.new
18
19
  @appender = SemanticLogger::Appender::File.new(@io)
@@ -24,7 +24,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
24
24
  end
25
25
 
26
26
  teardown do
27
- @appender.purge_all
27
+ @appender.purge_all if @appender
28
28
  end
29
29
 
30
30
  context "format logs into documents" do
@@ -10,139 +10,198 @@ require 'semantic_logger'
10
10
  # Unit Test for SemanticLogger::Logger
11
11
  class LoggerTest < Test::Unit::TestCase
12
12
  context SemanticLogger::Logger do
13
- setup do
14
- # Use a mock logger that just keeps the last logged entry in an instance
15
- # variable
16
- SemanticLogger.default_level = :trace
17
- @mock_logger = MockLogger.new
18
- SemanticLogger.add_appender(@mock_logger)
19
-
20
- # Add mock metric subscriber
21
- $last_metric = nil
22
- SemanticLogger.on_metric do |log_struct|
23
- $last_metric = log_struct.dup
24
- end
25
-
26
- # Use this test's class name as the application name in the log output
27
- @logger = SemanticLogger::Logger.new(self.class, :trace)
28
- @hash = { :session_id => 'HSSKLEU@JDK767', :tracking_number => 12345 }
29
- @hash_str = @hash.inspect.sub("{", "\\{").sub("}", "\\}")
30
- assert_equal [], @logger.tags
31
- end
32
-
33
- teardown do
34
- # Remove all appenders
35
- SemanticLogger.appenders.each{|appender| SemanticLogger.remove_appender(appender)}
36
- end
37
-
38
- # Ensure that any log level can be logged
39
- SemanticLogger::LEVELS.each do |level|
40
- should "log #{level} info" do
41
- @logger.send(level, 'hello world', @hash) { "Calculations" }
42
- SemanticLogger.flush
43
- assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] LoggerTest -- hello world -- Calculations -- #{@hash_str}/, @mock_logger.message
44
- end
45
- end
46
-
47
- context "tagged logging" do
48
- should "add tags to log entries" do
49
- @logger.tagged('12345', 'DJHSFK') do
50
- @logger.info('Hello world')
51
- SemanticLogger.flush
52
- assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \[12345\] \[DJHSFK\] LoggerTest -- Hello world/, @mock_logger.message
53
- end
54
- end
55
-
56
- should "add embedded tags to log entries" do
57
- @logger.tagged('First Level', 'tags') do
58
- @logger.tagged('Second Level') do
59
- @logger.info('Hello world')
60
- SemanticLogger.flush
61
- assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \[First Level\] \[tags\] \[Second Level\] LoggerTest -- Hello world/, @mock_logger.message
13
+ # Test each filter
14
+ [ nil, /\ALogger/, Proc.new{|l| (/\AExclude/ =~ l.message).nil? } ].each do |filter|
15
+ context "filter: #{filter.inspect}" do
16
+ setup do
17
+ # Use a mock logger that just keeps the last logged entry in an instance
18
+ # variable
19
+ SemanticLogger.default_level = :trace
20
+ @mock_logger = MockLogger.new
21
+ SemanticLogger.add_appender(@mock_logger)
22
+
23
+ # Add mock metric subscriber
24
+ $last_metric = nil
25
+ SemanticLogger.on_metric do |log_struct|
26
+ $last_metric = log_struct.dup
62
27
  end
63
- assert_equal 2, @logger.tags.count, @logger.tags
64
- assert_equal 'First Level', @logger.tags.first
65
- assert_equal 'tags', @logger.tags.last
28
+
29
+ # Use this test's class name as the application name in the log output
30
+ @logger = SemanticLogger::Logger.new(self.class, :trace, filter)
31
+ @hash = { :session_id => 'HSSKLEU@JDK767', :tracking_number => 12345 }
32
+ @hash_str = @hash.inspect.sub("{", "\\{").sub("}", "\\}")
33
+ assert_equal [], @logger.tags
66
34
  end
67
- end
68
35
 
69
- should "add payload to log entries" do
70
- hash = {:tracking_number=>"123456", :even=>2, :more=>"data"}
71
- hash_str = hash.inspect.sub("{", "\\{").sub("}", "\\}")
72
- @logger.with_payload(:tracking_number => '123456') do
73
- @logger.with_payload(:even => 2, :more => 'data') do
74
- @logger.info('Hello world')
75
- SemanticLogger.flush
76
- assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] LoggerTest -- Hello world -- #{hash_str}/, @mock_logger.message
77
- end
36
+ teardown do
37
+ # Remove all appenders
38
+ SemanticLogger.appenders.each{|appender| SemanticLogger.remove_appender(appender)}
78
39
  end
79
- end
80
40
 
81
- context "Ruby Logger" do
82
41
  # Ensure that any log level can be logged
83
- Logger::Severity.constants.each do |level|
84
- should "log Ruby logger #{level} info" do
85
- @logger.level = Logger::Severity.const_get(level)
86
- if level.to_s == 'UNKNOWN'
87
- assert_equal Logger::Severity.const_get('ERROR')+1, @logger.send(:level_index)
88
- else
89
- assert_equal Logger::Severity.const_get(level)+1, @logger.send(:level_index)
42
+ SemanticLogger::LEVELS.each do |level|
43
+ context level do
44
+ should "log" do
45
+ @logger.send(level, 'hello world', @hash) { "Calculations" }
46
+ SemanticLogger.flush
47
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] LoggerTest -- hello world -- Calculations -- #{@hash_str}/, @mock_logger.message
48
+ end
49
+
50
+ should "exclude log messages using Proc filter" do
51
+ if filter.is_a?(Proc)
52
+ @logger.send(level, 'Exclude this log message', @hash) { "Calculations" }
53
+ SemanticLogger.flush
54
+ assert_nil @mock_logger.message
55
+ end
90
56
  end
91
57
  end
92
58
  end
93
- end
94
59
 
95
- context "benchmark" do
96
- # Ensure that any log level can be benchmarked and logged
97
- SemanticLogger::LEVELS.each do |level|
98
- should "log #{level} info" do
99
- assert_equal "result", @logger.send("benchmark_#{level}".to_sym, 'hello world') { "result" } # Measure duration of the supplied block
100
- SemanticLogger.flush
101
- assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world/, @mock_logger.message
60
+ context "tagged logging" do
61
+ should "add tags to log entries" do
62
+ @logger.tagged('12345', 'DJHSFK') do
63
+ @logger.info('Hello world')
64
+ SemanticLogger.flush
65
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \[12345\] \[DJHSFK\] LoggerTest -- Hello world/, @mock_logger.message
66
+ end
102
67
  end
103
68
 
104
- should "log #{level} info with payload" do
105
- assert_equal "result", @logger.send("benchmark_#{level}".to_sym, 'hello world', :payload => @hash) { "result" } # Measure duration of the supplied block
106
- SemanticLogger.flush
107
- assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- #{@hash_str}/, @mock_logger.message
69
+ should "add embedded tags to log entries" do
70
+ @logger.tagged('First Level', 'tags') do
71
+ @logger.tagged('Second Level') do
72
+ @logger.info('Hello world')
73
+ SemanticLogger.flush
74
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \[First Level\] \[tags\] \[Second Level\] LoggerTest -- Hello world/, @mock_logger.message
75
+ end
76
+ assert_equal 2, @logger.tags.count, @logger.tags
77
+ assert_equal 'First Level', @logger.tags.first
78
+ assert_equal 'tags', @logger.tags.last
79
+ end
108
80
  end
109
81
 
110
- should "not log #{level} info when block is faster than :min_duration" do
111
- assert_equal "result", @logger.send("benchmark_#{level}".to_sym, 'hello world', :min_duration => 0.5) { "result" } # Measure duration of the supplied block
112
- SemanticLogger.flush
113
- assert_nil @mock_logger.message
82
+ should "add payload to log entries" do
83
+ hash = {:tracking_number=>"123456", :even=>2, :more=>"data"}
84
+ hash_str = hash.inspect.sub("{", "\\{").sub("}", "\\}")
85
+ @logger.with_payload(:tracking_number => '123456') do
86
+ @logger.with_payload(:even => 2, :more => 'data') do
87
+ @logger.info('Hello world')
88
+ SemanticLogger.flush
89
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] LoggerTest -- Hello world -- #{hash_str}/, @mock_logger.message
90
+ end
91
+ end
114
92
  end
115
93
 
116
- should "log #{level} info when block duration exceeds :min_duration" do
117
- assert_equal "result", @logger.send("benchmark_#{level}".to_sym, 'hello world', :min_duration => 0.2, :payload => @hash) { sleep 0.5; "result" } # Measure duration of the supplied block
118
- SemanticLogger.flush
119
- assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- #{@hash_str}/, @mock_logger.message
94
+ context "Ruby Logger" do
95
+ # Ensure that any log level can be logged
96
+ Logger::Severity.constants.each do |level|
97
+ should "log Ruby logger #{level} info" do
98
+ @logger.level = Logger::Severity.const_get(level)
99
+ if level.to_s == 'UNKNOWN'
100
+ assert_equal Logger::Severity.const_get('ERROR')+1, @logger.send(:level_index)
101
+ else
102
+ assert_equal Logger::Severity.const_get(level)+1, @logger.send(:level_index)
103
+ end
104
+ end
105
+ end
120
106
  end
121
107
 
122
- should "log #{level} info with an exception" do
123
- assert_raise RuntimeError do
124
- @logger.send("benchmark_#{level}", 'hello world', :payload => @hash) { raise RuntimeError.new("Test") } # Measure duration of the supplied block
108
+ context "benchmark" do
109
+ # Ensure that any log level can be benchmarked and logged
110
+ SemanticLogger::LEVELS.each do |level|
111
+
112
+ context 'direct method' do
113
+ should "log #{level} info" do
114
+ assert_equal "result", @logger.send("benchmark_#{level}".to_sym, 'hello world') { "result" } # Measure duration of the supplied block
115
+ SemanticLogger.flush
116
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world/, @mock_logger.message
117
+ end
118
+
119
+ should "log #{level} info with payload" do
120
+ assert_equal "result", @logger.send("benchmark_#{level}".to_sym, 'hello world', :payload => @hash) { "result" } # Measure duration of the supplied block
121
+ SemanticLogger.flush
122
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- #{@hash_str}/, @mock_logger.message
123
+ end
124
+
125
+ should "not log #{level} info when block is faster than :min_duration" do
126
+ assert_equal "result", @logger.send("benchmark_#{level}".to_sym, 'hello world', :min_duration => 500) { "result" } # Measure duration of the supplied block
127
+ SemanticLogger.flush
128
+ assert_nil @mock_logger.message
129
+ end
130
+
131
+ should "log #{level} info when block duration exceeds :min_duration" do
132
+ assert_equal "result", @logger.send("benchmark_#{level}".to_sym, 'hello world', :min_duration => 200, :payload => @hash) { sleep 0.5; "result" } # Measure duration of the supplied block
133
+ SemanticLogger.flush
134
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- #{@hash_str}/, @mock_logger.message
135
+ end
136
+
137
+ should "log #{level} info with an exception" do
138
+ assert_raise RuntimeError do
139
+ @logger.send("benchmark_#{level}", 'hello world', :payload => @hash) { raise RuntimeError.new("Test") } # Measure duration of the supplied block
140
+ end
141
+ SemanticLogger.flush
142
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- Exception: RuntimeError: Test -- #{@hash_str}/, @mock_logger.message
143
+ end
144
+
145
+ should "log #{level} info with metric" do
146
+ metric_name = '/my/custom/metric'
147
+ assert_equal "result", @logger.send("benchmark_#{level}".to_sym, 'hello world', :metric => metric_name) { "result" } # Measure duration of the supplied block
148
+ SemanticLogger.flush
149
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world/, @mock_logger.message
150
+ assert metric_name, $last_metric.metric
151
+ end
152
+ end
153
+
154
+ context 'generic method' do
155
+ should "log #{level} info" do
156
+ assert_equal "result", @logger.benchmark(level, 'hello world') { "result" } # Measure duration of the supplied block
157
+ SemanticLogger.flush
158
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world/, @mock_logger.message
159
+ end
160
+
161
+ should "log #{level} info with payload" do
162
+ assert_equal "result", @logger.benchmark(level, 'hello world', :payload => @hash) { "result" } # Measure duration of the supplied block
163
+ SemanticLogger.flush
164
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- #{@hash_str}/, @mock_logger.message
165
+ end
166
+
167
+ should "not log #{level} info when block is faster than :min_duration" do
168
+ assert_equal "result", @logger.benchmark(level, 'hello world', :min_duration => 500) { "result" } # Measure duration of the supplied block
169
+ SemanticLogger.flush
170
+ assert_nil @mock_logger.message
171
+ end
172
+
173
+ should "log #{level} info when block duration exceeds :min_duration" do
174
+ assert_equal "result", @logger.benchmark(level, 'hello world', :min_duration => 200, :payload => @hash) { sleep 0.5; "result" } # Measure duration of the supplied block
175
+ SemanticLogger.flush
176
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- #{@hash_str}/, @mock_logger.message
177
+ end
178
+
179
+ should "log #{level} info with an exception" do
180
+ assert_raise RuntimeError do
181
+ @logger.benchmark(level, 'hello world', :payload => @hash) { raise RuntimeError.new("Test") } # Measure duration of the supplied block
182
+ end
183
+ SemanticLogger.flush
184
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- Exception: RuntimeError: Test -- #{@hash_str}/, @mock_logger.message
185
+ end
186
+
187
+ should "log #{level} info with metric" do
188
+ metric_name = '/my/custom/metric'
189
+ assert_equal "result", @logger.benchmark(level, 'hello world', :metric => metric_name) { "result" } # Measure duration of the supplied block
190
+ SemanticLogger.flush
191
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world/, @mock_logger.message
192
+ assert metric_name, $last_metric.metric
193
+ end
194
+ end
125
195
  end
126
- SemanticLogger.flush
127
- assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- Exception: RuntimeError: Test -- #{@hash_str}/, @mock_logger.message
128
- end
129
196
 
130
- should "log #{level} info with metric" do
131
- metric_name = '/my/custom/metric'
132
- assert_equal "result", @logger.send("benchmark_#{level}".to_sym, 'hello world', metric: metric_name) { "result" } # Measure duration of the supplied block
133
- SemanticLogger.flush
134
- assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world/, @mock_logger.message
135
- assert metric_name, $last_metric.metric
197
+ should "log when the block performs a return" do
198
+ assert_equal "Good", function_with_return(@logger)
199
+ SemanticLogger.flush
200
+ assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- #{@hash_str}/, @mock_logger.message
201
+ end
136
202
  end
137
203
  end
138
-
139
- should "log when the block performs a return" do
140
- assert_equal "Good", function_with_return(@logger)
141
- SemanticLogger.flush
142
- assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- #{@hash_str}/, @mock_logger.message
143
- end
144
204
  end
145
-
146
205
  end
147
206
  end
148
207
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: semantic_logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.0
4
+ version: 2.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-07 00:00:00.000000000 Z
11
+ date: 2014-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sync_attr
@@ -38,7 +38,7 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.1.0
41
- description: Semantic Logger takes logging in Ruby to a new level by adding several
41
+ description: Semantic Logger takes logging in Ruby to the next level by adding several
42
42
  new capabilities to the commonly used Logging API
43
43
  email:
44
44
  - reidmo@gmail.com
@@ -88,10 +88,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
88
  version: '0'
89
89
  requirements: []
90
90
  rubyforge_project:
91
- rubygems_version: 2.2.0
91
+ rubygems_version: 2.2.2
92
92
  signing_key:
93
93
  specification_version: 4
94
- summary: Improved logging for Ruby
94
+ summary: Scalable, next generation logging for Ruby
95
95
  test_files:
96
96
  - test/appender_file_test.rb
97
97
  - test/appender_mongodb_test.rb