semantic_logger 2.7.0 → 2.8.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 +4 -4
- data/README.md +145 -82
- data/Rakefile +13 -8
- data/lib/semantic_logger/appender/base.rb +14 -7
- data/lib/semantic_logger/appender/file.rb +2 -2
- data/lib/semantic_logger/appender/mongodb.rb +17 -12
- data/lib/semantic_logger/appender/syslog.rb +16 -14
- data/lib/semantic_logger/appender/wrapper.rb +2 -2
- data/lib/semantic_logger/base.rb +71 -38
- data/lib/semantic_logger/logger.rb +7 -3
- data/lib/semantic_logger/semantic_logger.rb +56 -0
- data/lib/semantic_logger/version.rb +1 -1
- data/test/appender_file_test.rb +1 -0
- data/test/appender_mongodb_test.rb +1 -1
- data/test/logger_test.rb +169 -110
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51a1e44ec07ae96cd5dad5b3a3dc2c6377960b15
|
4
|
+
data.tar.gz: 820304cb8615f71b1f921869fcc70e0766ef7258
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3faa5b434bc505b9d2045f7f00c91feae01f89bcd3a498ea7e0fe8f831d75fd276f05fd059ddd149ba2755d3ab6dfa3abeddc6d9c025191e75a4517c304c155f
|
7
|
+
data.tar.gz: d0691ea0ad358cf91c266e5e9005d510aed493f4adb8255423a425133c7258248d53482420e06623db1009a620adcd50ec616c56ad687757a4427eabbc16c603
|
data/README.md
CHANGED
@@ -1,26 +1,35 @@
|
|
1
|
-
semantic_logger
|
1
|
+
semantic_logger [](http://travis-ci.org/reidmorrison/semantic_logger)
|
2
2
|
===============
|
3
3
|
|
4
|
-
|
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
|
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
|
-
|
56
|
+
Benchmarking
|
59
57
|
|
60
|
-
*
|
61
|
-
|
62
|
-
*
|
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
|
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
|
77
|
-
|
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
|
-
|
140
|
+
Customizable
|
139
141
|
|
140
|
-
*
|
141
|
-
|
142
|
-
|
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
|
-
|
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:
|
233
|
+
- message: The text message to log.
|
227
234
|
Mandatory only if no block is supplied
|
228
|
-
- payload:
|
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:
|
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(
|
296
|
-
benchmark_debug(
|
297
|
-
benchmark_info(message
|
298
|
-
benchmark_warn(
|
299
|
-
benchmark_error(
|
300
|
-
benchmark_fatal(
|
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
|
-
|
527
|
-
|
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
|
-
|
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
|
970
|
-
|
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
|
-
|
977
|
-
|
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
|
-
|
984
|
-
|
985
|
-
gem install mongo
|
986
|
-
|
987
|
-
## Future
|
1050
|
+
## Upgrade Notes:
|
988
1051
|
|
989
|
-
|
990
|
-
|
991
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
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
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
|
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
|
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, :
|
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
|
-
# :
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
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
|
-
@
|
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
|
-
|
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
|
-
|
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
|
147
|
-
@ident
|
148
|
-
options
|
149
|
-
@facility
|
150
|
-
|
151
|
-
|
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
|
155
|
-
uri
|
156
|
-
@host
|
157
|
-
@protocol
|
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
|
160
|
-
@port
|
161
|
-
|
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(
|
36
|
+
super(nil, filter, &block)
|
37
37
|
end
|
38
38
|
|
39
39
|
# Pass log calls to the underlying Rails, log4j or Ruby logger
|
data/lib/semantic_logger/base.rb
CHANGED
@@ -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
|
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 =
|
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
|
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
|
-
|
88
|
+
level_index <= #{index}
|
84
89
|
end
|
85
90
|
|
86
91
|
def benchmark_#{level}(message, params = {}, &block)
|
87
|
-
if
|
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
|
-
|
183
|
-
|
184
|
-
|
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
|
-
|
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
|
-
#
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
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
|
data/test/appender_file_test.rb
CHANGED
@@ -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)
|
data/test/logger_test.rb
CHANGED
@@ -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
|
-
|
14
|
-
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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 "
|
105
|
-
|
106
|
-
|
107
|
-
|
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 "
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
123
|
-
|
124
|
-
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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.
|
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-
|
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
|
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.
|
91
|
+
rubygems_version: 2.2.2
|
92
92
|
signing_key:
|
93
93
|
specification_version: 4
|
94
|
-
summary:
|
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
|