semantic_logger 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +313 -9
- data/lib/semantic_logger/appender/mongodb.rb +1 -1
- data/lib/semantic_logger/logger.rb +3 -2
- data/lib/semantic_logger/version.rb +1 -1
- data/semantic_logger-0.8.0.gem +0 -0
- metadata +3 -3
data/README.md
CHANGED
@@ -1,14 +1,141 @@
|
|
1
1
|
semantic_logger
|
2
2
|
===============
|
3
3
|
|
4
|
-
|
5
|
-
logging with multiple appenders
|
4
|
+
Improved logging for Ruby
|
6
5
|
|
7
6
|
* http://github.com/ClarityServices/semantic_logger
|
8
7
|
|
8
|
+
### Overview
|
9
|
+
|
10
|
+
Semantic Logger takes logging in Ruby to a new level by adding several new
|
11
|
+
capabilities to the commonly used Logging API:
|
12
|
+
|
13
|
+
Dynamic
|
14
|
+
|
15
|
+
* Increase the log level at runtime for just one class
|
16
|
+
* For example enable debug level logging for a single class (logging instance)
|
17
|
+
while the program is running to get more detailed logging in production for just that class
|
18
|
+
|
19
|
+
Tagged Logging
|
20
|
+
|
21
|
+
* Supply custom data to be added to every log entry within a block of code,
|
22
|
+
including libraries and existing Gems
|
23
|
+
|
24
|
+
High Performance
|
25
|
+
|
26
|
+
* Logging is performed in a separate thread so as not to impact performance of
|
27
|
+
running code
|
28
|
+
|
29
|
+
Customizable
|
30
|
+
|
31
|
+
* Custom formatting by destination
|
32
|
+
* Easy to "roll your own" destination (Appender).
|
33
|
+
For example to log to Hadoop, Redis, etc..
|
34
|
+
|
35
|
+
Payload support
|
36
|
+
|
37
|
+
* Aside from the regular log message, a hash payload can also be supplied with
|
38
|
+
every log entry
|
39
|
+
* Very powerful when logging to NOSQL destinations that allow queries against
|
40
|
+
any data in the payload
|
41
|
+
|
42
|
+
Exceptions
|
43
|
+
|
44
|
+
* Directly log exceptions
|
45
|
+
* Semantic Logger standardizes the logging of exceptions with their backtraces
|
46
|
+
to text destinations and writes the exception elements as a hash to NOSQL
|
47
|
+
destinations
|
48
|
+
|
49
|
+
Drop-in Replacement
|
50
|
+
|
51
|
+
* Simple drop-in replacement for the Ruby, or the Rails loggers
|
52
|
+
* Supports current common logging interface
|
53
|
+
* No changes to existing to code to use new logger ( other than replacing the logger )
|
54
|
+
|
55
|
+
Rails 2 & 3 Support
|
56
|
+
|
57
|
+
* Just include the semantic_logger gem into Rails and it will immediately
|
58
|
+
replace the existing loggers to improve performance and information
|
59
|
+
in the log files
|
60
|
+
|
61
|
+
Thread Aware
|
62
|
+
|
63
|
+
* Includes the process and thread id information in every log entry
|
64
|
+
|
65
|
+
Trace Level
|
66
|
+
|
67
|
+
* :trace is a new level common in other languages and is commonly used for
|
68
|
+
logging trace level detail. It is intended for logging data at level below
|
69
|
+
:debug.
|
70
|
+
* :trace can be used for logging the actual data sent or received over the network
|
71
|
+
that is rarely needed but is critical when things are not working as expected.
|
72
|
+
* Since :trace can be enabled on a per class basis it can even be turned on
|
73
|
+
in production to resolve what was actually sent to an external vendor
|
74
|
+
|
75
|
+
Multiple Destinations
|
76
|
+
|
77
|
+
* Log to multiple destinations at the same time ( File and MongoDB, etc.. )
|
78
|
+
* Each destination can also have its own log level.
|
79
|
+
For example only write :info and above to MongoDB
|
80
|
+
Or have a second log file for :warn and above log entries
|
81
|
+
|
82
|
+
Benchmarking
|
83
|
+
|
84
|
+
* The performance of any block of code can be measured and logged at the same time
|
85
|
+
depending on the active log level
|
86
|
+
|
87
|
+
Semantic Capabilities
|
88
|
+
|
89
|
+
* With Semantic Logger it is simple to mix-in additional semantic information with
|
90
|
+
every log entry
|
91
|
+
* The application or class name is automatically included for every log entry under
|
92
|
+
a specific logging instance
|
93
|
+
* Includes the duration of blocks of code
|
94
|
+
* any Hash containing context specific information such as user_id or location information
|
95
|
+
|
96
|
+
Beyond Tagged Logging
|
97
|
+
|
98
|
+
* Supply entire hash of custom data to be added to the payload of every log entry
|
99
|
+
within a block of code, including libraries and existing Gems
|
100
|
+
|
101
|
+
NOSQL Destinations
|
102
|
+
|
103
|
+
* Every log entry is broken down into elements that NOSQL data stores can understand:
|
104
|
+
|
105
|
+
```json
|
106
|
+
{
|
107
|
+
"_id" : ObjectId("5034fa48e3f3fea945e83ef2"),
|
108
|
+
"time" : ISODate("2012-08-22T15:27:04.409Z"),
|
109
|
+
"host_name" : "release",
|
110
|
+
"pid" : 16112,
|
111
|
+
"thread_name" : "main",
|
112
|
+
"name" : "UserLocator",
|
113
|
+
"level" : "debug",
|
114
|
+
"message" : "Fetch user information",
|
115
|
+
"duration" : 12,
|
116
|
+
"payload" : {
|
117
|
+
"user" : "Jack",
|
118
|
+
"zip_code" : 12345,
|
119
|
+
"location" : "US"
|
120
|
+
}
|
121
|
+
}
|
122
|
+
```
|
123
|
+
|
124
|
+
Thread Safe
|
125
|
+
|
126
|
+
* Semantic Logger is completely thread safe and all methods can be called
|
127
|
+
concurrently from any thread
|
128
|
+
* Tagged logging keeps any tagging data on a per-thread basis to ensure that
|
129
|
+
tags from different threads are not inter-mingled
|
130
|
+
|
9
131
|
### Introduction
|
10
132
|
|
11
133
|
SemanticLogger is a Logger that supports logging of meta-data, along with text messages
|
134
|
+
to multiple appenders
|
135
|
+
|
136
|
+
An appender is a Logging destination such as a File, MongoDB collection, etc..
|
137
|
+
Multiple Appenders can be active at the same time. All log entries are written
|
138
|
+
to each appender.
|
12
139
|
|
13
140
|
Machines can understand the logged data without having to use
|
14
141
|
complex Regular Expressions or other text parsing techniques
|
@@ -205,7 +332,7 @@ as debug calls only if the log level is set to trace
|
|
205
332
|
|
206
333
|
When Semantic Logger is included on a Rails project it automatically replaces the
|
207
334
|
loggers for Rails, ActiveRecord::Base, ActionController::Base, and ActiveResource::Base
|
208
|
-
with wrappers that set their Class name. For example in railtie.rb:
|
335
|
+
with wrappers that set their Class name. For example in semantic_logger/railtie.rb:
|
209
336
|
|
210
337
|
```ruby
|
211
338
|
ActiveRecord::Base.logger = SemanticLogger::Logger.new(ActiveRecord)
|
@@ -246,8 +373,37 @@ This will result in the log output identifying the log entry as from the Externa
|
|
246
373
|
[SyncAttr](https://github.com/ClarityServices/sync_attr) is a gem that supports
|
247
374
|
lazy loading and thread-safe initialization of class attributes
|
248
375
|
|
376
|
+
Extract from a Rails log file after adding the semantic_logger gem:
|
377
|
+
|
378
|
+
```
|
379
|
+
2012-10-19 12:05:46.736 I [35940:JRubyWorker-10] Rails --
|
380
|
+
|
381
|
+
Started GET "/" for 127.0.0.1 at 2012-10-19 12:05:46 +0000
|
382
|
+
2012-10-19 12:05:47.318 I [35940:JRubyWorker-10] ActionController -- Processing by AdminController#index as HTML
|
383
|
+
2012-10-19 12:05:47.633 D [35940:JRubyWorker-10] ActiveRecord -- User Load (2.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
|
384
|
+
2012-10-19 12:05:49.833 D [35940:JRubyWorker-10] ActiveRecord -- Role Load (2.0ms) SELECT `roles`.* FROM `roles`
|
385
|
+
2012-10-19 12:05:49.868 D [35940:JRubyWorker-10] ActiveRecord -- Role Load (1.0ms) SELECT * FROM `roles` INNER JOIN `roles_users` ON `roles`.id = `roles_users`.role_id WHERE (`roles_users`.user_id = 1 )
|
386
|
+
2012-10-19 12:05:49.885 I [35940:JRubyWorker-10] ActionController -- Rendered menus/_control_system.html.erb (98.0ms)
|
387
|
+
2012-10-19 12:05:51.014 I [35940:JRubyWorker-10] ActionController -- Rendered layouts/_top_bar.html.erb (386.0ms)
|
388
|
+
2012-10-19 12:05:51.071 D [35940:JRubyWorker-10] ActiveRecord -- Announcement Load (20.0ms) SELECT `announcements`.* FROM `announcements` WHERE `announcements`.`active` = 1 ORDER BY created_at desc
|
389
|
+
2012-10-19 12:05:51.072 I [35940:JRubyWorker-10] ActionController -- Rendered layouts/_announcement.html.erb (26.0ms)
|
390
|
+
2012-10-19 12:05:51.083 I [35940:JRubyWorker-10] ActionController -- Rendered layouts/_flash.html.erb (4.0ms)
|
391
|
+
2012-10-19 12:05:51.109 I [35940:JRubyWorker-10] ActionController -- Rendered layouts/_footer.html.erb (16.0ms)
|
392
|
+
2012-10-19 12:05:51.109 I [35940:JRubyWorker-10] ActionController -- Rendered admin/index.html.erb within layouts/base (1329.0ms)
|
393
|
+
2012-10-19 12:05:51.113 I [35940:JRubyWorker-10] ActionController -- Completed 200 OK in 3795ms (Views: 1349.0ms | ActiveRecord: 88.0ms | Mongo: 0.0ms)
|
394
|
+
```
|
395
|
+
|
249
396
|
#### Tagged Logging
|
250
397
|
|
398
|
+
Semantic Logger allows any Ruby or Rails program to also include tagged logging.
|
399
|
+
|
400
|
+
This means that any logging performed within a block, including any called
|
401
|
+
libraries or gems to include the specified tag with every log entry.
|
402
|
+
|
403
|
+
Using Tagged logging is critical in any highly concurrent environment so that
|
404
|
+
one can quickly find all related log entries across all levels of code, and even
|
405
|
+
across threads
|
406
|
+
|
251
407
|
```ruby
|
252
408
|
logger.tagged(tracking_number) do
|
253
409
|
logger.debug("Hello World")
|
@@ -255,10 +411,17 @@ logger.tagged(tracking_number) do
|
|
255
411
|
end
|
256
412
|
```
|
257
413
|
|
258
|
-
####
|
414
|
+
#### Beyond Tagged Logging
|
415
|
+
|
416
|
+
Blocks of code can be tagged with not only values, but can be tagged with
|
417
|
+
entire hashes of data. The additional hash of data will be merged into
|
418
|
+
the payload of every log entry
|
419
|
+
|
420
|
+
For example every corresponding log entry could include a hash containing
|
421
|
+
a user_id, name, region, zip_code, tracking_number, etc...
|
259
422
|
|
260
423
|
```ruby
|
261
|
-
logger.with_payload(:user => 'Jack') do
|
424
|
+
logger.with_payload(:user => 'Jack', :zip_code => 12345) do
|
262
425
|
logger.debug("Hello World")
|
263
426
|
# ...
|
264
427
|
end
|
@@ -441,6 +604,52 @@ For example to replace the MongoDB formatter, in the environment configuration f
|
|
441
604
|
end
|
442
605
|
```
|
443
606
|
|
607
|
+
### SysLog and other standard loggers
|
608
|
+
|
609
|
+
To write log entries to a Syslog logger or any other logger of your choice,
|
610
|
+
that conforms the standard Ruby Logger API, Semantic Logger has an Appender to
|
611
|
+
use that logger.
|
612
|
+
|
613
|
+
For example to configure rails to also log to the Syslogger gem:
|
614
|
+
```ruby
|
615
|
+
config.after_initialize do
|
616
|
+
# Besides logging to the local file also log to Syslogger
|
617
|
+
config.semantic_logger.appenders << SemanticLogger::Appender::Wrapper.new(Syslogger.new("yourappname"))
|
618
|
+
end
|
619
|
+
```
|
620
|
+
|
621
|
+
### Performance
|
622
|
+
|
623
|
+
The traditional logging implementations write their log information to file in the
|
624
|
+
same thread of execution as the program itself. This means that for every log entry
|
625
|
+
the program has to wait for the data to be written.
|
626
|
+
|
627
|
+
With Semantic Logger it uses a dedicated thread for logging so that writing to
|
628
|
+
the log file or other appenders does not hold up program execution.
|
629
|
+
|
630
|
+
Also, since the logging is in this separate thread there is no impact to program
|
631
|
+
execution if we decided to add another appender.
|
632
|
+
For example, log to both a file and a MongoDB collection.
|
633
|
+
|
634
|
+
### Log Rotation
|
635
|
+
|
636
|
+
Since the log file is not re-opened with every call, when the log file needs
|
637
|
+
to be rotated, use a copy-truncate operation over deleting the file.
|
638
|
+
|
639
|
+
### Why Semantic logging?
|
640
|
+
|
641
|
+
Just as there is the initiative to add Semantic information to data on the web
|
642
|
+
so that computers can directly understand the content without having to resort
|
643
|
+
to complex regular expressions or machine learning techniques, it is important
|
644
|
+
to be able to do the same with log files or data.
|
645
|
+
|
646
|
+
Semantic Logger allows every log entry to have not only a message, but a payload
|
647
|
+
that can be written to a file or a NOSQL destination.
|
648
|
+
|
649
|
+
Once the logging data is in the NOSQL data store it can be queried quickly and
|
650
|
+
efficiently. Some SQL data stores also allow complex data types that could be used
|
651
|
+
for storing and querying the logging data
|
652
|
+
|
444
653
|
### Architecture & Performance
|
445
654
|
|
446
655
|
In order to ensure that logging does not hinder the performance of the application
|
@@ -449,7 +658,7 @@ for writing the log entries to each of the appenders.
|
|
449
658
|
|
450
659
|
In this way formatting and disk or network write delays will not affect the
|
451
660
|
performance of the application. Also adding more than one appender does not affect
|
452
|
-
the runtime performance of the application
|
661
|
+
the runtime performance of the application.
|
453
662
|
|
454
663
|
The additional thread is automatically started on initialization. When the program
|
455
664
|
terminates it will complete writing out all log data and flush the appenders before
|
@@ -460,9 +669,102 @@ have been written and flushed to their respective appenders before returning.
|
|
460
669
|
Since all logging is now from this thread calling flush is no longer thread
|
461
670
|
specific.
|
462
671
|
|
672
|
+
### Write your own Appender
|
673
|
+
|
674
|
+
To write your own appender it should meet the following requirements:
|
675
|
+
|
676
|
+
* Inherit from SemanticLogger::Base
|
677
|
+
* In the initializer connect to the resource being logged to
|
678
|
+
* Implement #log(log) which needs to write to the relevant resource
|
679
|
+
* Implement #flush if the resource can be flushed
|
680
|
+
* Write a test for the new appender
|
681
|
+
|
682
|
+
The #log method takes the log struct as a parameter which is defined as follows:
|
683
|
+
```ruby
|
684
|
+
Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index)
|
685
|
+
```
|
686
|
+
level
|
687
|
+
|
688
|
+
* Log level of the supplied log call
|
689
|
+
* :trace, :debug, :info, :warn, :error, :fatal
|
690
|
+
|
691
|
+
thread_name
|
692
|
+
|
693
|
+
* Name or id of the thread in which the logging call was called
|
694
|
+
|
695
|
+
name
|
696
|
+
|
697
|
+
* Class name supplied to the logging instance
|
698
|
+
|
699
|
+
message
|
700
|
+
|
701
|
+
* Text message to be logged
|
702
|
+
|
703
|
+
payload [Hash|Exception]
|
704
|
+
|
705
|
+
* Optional Hash or Ruby Exception object to be logged
|
706
|
+
|
707
|
+
time [Time]
|
708
|
+
|
709
|
+
* The time at which the log entry was created
|
710
|
+
|
711
|
+
duration [Float]
|
712
|
+
|
713
|
+
* The time taken in milli-seconds to complete a benchmark call
|
714
|
+
|
715
|
+
tags [Array<String>]
|
716
|
+
|
717
|
+
* Any tags active on the thread when the log call was made
|
718
|
+
|
719
|
+
level_index
|
720
|
+
|
721
|
+
* Internal use only. Index of the log level
|
722
|
+
|
723
|
+
Basic outline for an Appender:
|
724
|
+
|
725
|
+
```ruby
|
726
|
+
require 'semantic_logger'
|
727
|
+
|
728
|
+
class SimpleAppender < SemanticLogger::Base
|
729
|
+
def initialize(level=nil, &block)
|
730
|
+
# Set the log level and formatter if supplied
|
731
|
+
super(level, &block)
|
732
|
+
end
|
733
|
+
|
734
|
+
# Just display the log struct
|
735
|
+
def log(log)
|
736
|
+
p log
|
737
|
+
end
|
738
|
+
|
739
|
+
# Optional
|
740
|
+
def flush
|
741
|
+
puts "Flush :)"
|
742
|
+
end
|
743
|
+
end
|
744
|
+
```
|
745
|
+
|
746
|
+
Sample program calling the above appender:
|
747
|
+
```ruby
|
748
|
+
SemanticLogger::Logger.default_level = :trace
|
749
|
+
# Log to file dev.log
|
750
|
+
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('dev.log')
|
751
|
+
# Also log the above sample appender
|
752
|
+
SemanticLogger::Logger.appenders << SimpleAppender.new
|
753
|
+
|
754
|
+
logger = SemanticLogger::Logger.new('Hello')
|
755
|
+
logger.info "Hello World"
|
756
|
+
```
|
757
|
+
|
758
|
+
Look at the [existing appenders](https://github.com/ClarityServices/semantic_logger/tree/master/lib/semantic_logger/appender) for good examples
|
759
|
+
|
760
|
+
To have your appender included in the standard list of appenders follow the fork
|
761
|
+
instructions below.
|
762
|
+
Very Important: New appenders will not be accepted without complete working tests.
|
763
|
+
See the [MongoDB Appender Test](https://github.com/ClarityServices/semantic_logger/blob/master/test/appender_mongodb_test.rb) for an example.
|
764
|
+
|
463
765
|
### Dependencies
|
464
766
|
|
465
|
-
- Ruby MRI 1.8.7 (or above) Or, JRuby 1.6.3 (or above)
|
767
|
+
- Ruby MRI 1.8.7, 1.9.3 (or above) Or, JRuby 1.6.3 (or above)
|
466
768
|
- Optional: Rails 3.0.10 (or above)
|
467
769
|
- Optional: To log to MongoDB, Mongo Ruby Driver 1.5.2 or above
|
468
770
|
|
@@ -476,7 +778,9 @@ To log to MongoDB
|
|
476
778
|
|
477
779
|
### Future
|
478
780
|
|
479
|
-
-
|
781
|
+
- Configuration file to support setting the log level for a specific class
|
782
|
+
- Configuration file to support adding appenders
|
783
|
+
- Based on demand add direct appenders for: Syslog, hadoop, redis
|
480
784
|
|
481
785
|
Development
|
482
786
|
-----------
|
@@ -487,7 +791,7 @@ First clone the repo and run the tests:
|
|
487
791
|
|
488
792
|
git clone git://github.com/ClarityServices/semantic_logger.git
|
489
793
|
cd semantic_logger
|
490
|
-
|
794
|
+
rake test
|
491
795
|
|
492
796
|
Feel free to ping the mailing list with any issues and we'll try to resolve it.
|
493
797
|
|
@@ -49,6 +49,7 @@ module SemanticLogger
|
|
49
49
|
|
50
50
|
# Initial default Level for all new instances of SemanticLogger::Logger
|
51
51
|
@@default_level = :info
|
52
|
+
@@appender_thread = nil
|
52
53
|
|
53
54
|
# Allow for setting the global default log level
|
54
55
|
# This change only applies to _new_ loggers, existing logger levels
|
@@ -99,7 +100,7 @@ module SemanticLogger
|
|
99
100
|
# Flush all queued log entries disk, database, etc.
|
100
101
|
# All queued log messages are written and then each appender is flushed in turn
|
101
102
|
def self.flush
|
102
|
-
return false unless started?
|
103
|
+
return false unless started? && @@appender_thread && @@appender_thread.alive?
|
103
104
|
|
104
105
|
reply_queue = Queue.new
|
105
106
|
queue << { :command => :flush, :reply_queue => reply_queue }
|
@@ -172,7 +173,7 @@ module SemanticLogger
|
|
172
173
|
#
|
173
174
|
# Should any appender fail to log or flush, the exception is logged and
|
174
175
|
# other appenders will still be called
|
175
|
-
Thread.new do
|
176
|
+
@@appender_thread = Thread.new do
|
176
177
|
logger.debug "SemanticLogger::Logger Appender thread started"
|
177
178
|
begin
|
178
179
|
count = 0
|
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: semantic_logger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sync_attr
|
@@ -111,7 +111,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
111
111
|
version: '0'
|
112
112
|
segments:
|
113
113
|
- 0
|
114
|
-
hash:
|
114
|
+
hash: -3615546680742578594
|
115
115
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|