semantic_logger 2.8.0 → 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +7 -1035
- data/lib/semantic_logger.rb +9 -8
- data/lib/semantic_logger/appender/base.rb +4 -0
- data/lib/semantic_logger/appender/mongodb.rb +1 -1
- data/lib/semantic_logger/appender/new_relic.rb +105 -0
- data/lib/semantic_logger/base.rb +4 -1
- data/lib/semantic_logger/semantic_logger.rb +8 -8
- data/lib/semantic_logger/version.rb +1 -1
- data/test/appender_mongodb_test.rb +5 -5
- data/test/appender_new_relic_test.rb +79 -0
- data/test/newrelic_rpm.rb +21 -0
- metadata +9 -4
data/lib/semantic_logger.rb
CHANGED
@@ -3,16 +3,17 @@ require 'semantic_logger/version'
|
|
3
3
|
require 'semantic_logger/semantic_logger'
|
4
4
|
|
5
5
|
module SemanticLogger
|
6
|
-
autoload :Base,
|
7
|
-
autoload :Logger,
|
8
|
-
autoload :Loggable,
|
6
|
+
autoload :Base, 'semantic_logger/base'
|
7
|
+
autoload :Logger, 'semantic_logger/logger'
|
8
|
+
autoload :Loggable, 'semantic_logger/loggable'
|
9
9
|
|
10
10
|
module Appender
|
11
|
-
autoload :Base,
|
12
|
-
autoload :File,
|
13
|
-
autoload :Wrapper,
|
14
|
-
autoload :MongoDB,
|
15
|
-
autoload :Syslog,
|
11
|
+
autoload :Base, 'semantic_logger/appender/base'
|
12
|
+
autoload :File, 'semantic_logger/appender/file'
|
13
|
+
autoload :Wrapper, 'semantic_logger/appender/wrapper'
|
14
|
+
autoload :MongoDB, 'semantic_logger/appender/mongodb'
|
15
|
+
autoload :Syslog, 'semantic_logger/appender/syslog'
|
16
|
+
autoload :NewRelic, 'semantic_logger/appender/new_relic'
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
@@ -0,0 +1,105 @@
|
|
1
|
+
=begin
|
2
|
+
New Relic appender for SemanticLogger
|
3
|
+
|
4
|
+
The :error and :fatal log entries will show up under Applications > "Application Name" > Events > Errors in New Relic
|
5
|
+
|
6
|
+
Note: Payload information is not filtered, so take care not to push any sensitive information when logging with tags or a payload.
|
7
|
+
|
8
|
+
|
9
|
+
Example 1
|
10
|
+
|
11
|
+
Adding the New Relic appender will send :error and :fatal log entries to New Relic as error events.
|
12
|
+
|
13
|
+
For a Rails application already configured to use SemanticLogger and New Relic, create a file called <Rails Root>/config/initializers/newrelic_appender.rb with the following contents and restart the application:
|
14
|
+
|
15
|
+
# Send :error and :fatal log messages to New Relic
|
16
|
+
SemanticLogger.add_appender(SemanticLogger::Appender::NewRelic.new)
|
17
|
+
Rails.logger.info 'SemanticLogger New Relic Appender added.'
|
18
|
+
|
19
|
+
|
20
|
+
Example 2
|
21
|
+
|
22
|
+
For a non-Rails application, send :info and more severe log entries to a file called application.log and also send :error and :fatal log entries to New Relic.
|
23
|
+
|
24
|
+
# ./newrelic.yml needs to be set up -- see https://docs.newrelic.com/docs/ruby/ruby-agent-installation for more information.
|
25
|
+
|
26
|
+
require 'semantic_logger'
|
27
|
+
require 'newrelic_rpm'
|
28
|
+
|
29
|
+
# New Relic setup
|
30
|
+
NewRelic::Agent.manual_start
|
31
|
+
|
32
|
+
# SemanticLogger setup
|
33
|
+
SemanticLogger.default_level = :info
|
34
|
+
SemanticLogger.add_appender('application.log')
|
35
|
+
SemanticLogger.add_appender(SemanticLogger::Appender::NewRelic.new)
|
36
|
+
logger = SemanticLogger['Example']
|
37
|
+
|
38
|
+
# Log some messages
|
39
|
+
logger.info 'This is only written to application.log'
|
40
|
+
logger.error 'This is written to application.log and will also be sent to New Relic as an error event'
|
41
|
+
|
42
|
+
# The appender will send tags, payloads and benchmark duration to New Relic
|
43
|
+
logger.tagged('test') do
|
44
|
+
logger.with_payload( {key1: 123, key2: 'abc'} ) do
|
45
|
+
logger.benchmark_error(@message) do
|
46
|
+
sleep 0.001
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# New Relic does not seem to receive any errors if the application exits too soon after sending error alerts.
|
52
|
+
sleep 10
|
53
|
+
|
54
|
+
# New Relic shutdown - should send any queued data before exiting
|
55
|
+
::NewRelic::Agent.shutdown
|
56
|
+
=end
|
57
|
+
|
58
|
+
|
59
|
+
require 'newrelic_rpm'
|
60
|
+
|
61
|
+
class SemanticLogger::Appender::NewRelic < SemanticLogger::Appender::Base
|
62
|
+
|
63
|
+
# Allow the level for this appender to be overwritten
|
64
|
+
# Default: :error
|
65
|
+
# Note: Not recommended to set the log level to :info, :debug, or :trace as that would flood NewRelic with Error notices
|
66
|
+
def initialize(level=:error,&block)
|
67
|
+
# Pass on the level and custom formatter if supplied
|
68
|
+
super(level, &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
# The application may send a multiline string to the appender... use the first non-blank line as a shorter message.
|
72
|
+
def self.first_non_empty_line(string)
|
73
|
+
string.strip.split("\n").first.to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns [Hash] of parameters to send to New Relic.
|
77
|
+
def default_formatter
|
78
|
+
Proc.new do |log|
|
79
|
+
short_message = self.class.first_non_empty_line(log.message)
|
80
|
+
metric = log.metric || "#{log.name}/#{short_message}"
|
81
|
+
|
82
|
+
custom_params = { :thread_name => log.thread_name }
|
83
|
+
# Only show the message under custom attributes if the error message uses an exception or shortened message (first non-empty line).
|
84
|
+
custom_params[:message] = log.message if log.message && (log.exception || log.message != short_message)
|
85
|
+
custom_params[:duration] = "#{log.duration} ms" if log.duration
|
86
|
+
custom_params[:payload] = log.payload if log.payload
|
87
|
+
custom_params[:tags] = log.tags if log.tags && (log.tags.size > 0)
|
88
|
+
|
89
|
+
{ :metric => metric, :custom_params => custom_params }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Send an error notification to New Relic
|
94
|
+
def log(log)
|
95
|
+
# Ensure minimum log level is met.
|
96
|
+
return false unless level_index <= (log.level_index || 0)
|
97
|
+
|
98
|
+
# For more documentation on the NewRelic::Agent.notice_error method see:
|
99
|
+
# http://rubydoc.info/github/newrelic/rpm/NewRelic/Agent#notice_error-instance_method
|
100
|
+
# and https://docs.newrelic.com/docs/ruby/ruby-agent-api
|
101
|
+
NewRelic::Agent.notice_error(log.exception || self.class.first_non_empty_line(log.message), formatter.call(log))
|
102
|
+
return true
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
data/lib/semantic_logger/base.rb
CHANGED
@@ -18,7 +18,7 @@ module SemanticLogger
|
|
18
18
|
# SemanticLogger.default_level
|
19
19
|
#
|
20
20
|
# Must be one of the values in SemanticLogger::LEVELS, or
|
21
|
-
# nil if this logger instance should use the global default
|
21
|
+
# nil if this logger instance should use the global default level
|
22
22
|
def level=(level)
|
23
23
|
@level_index = SemanticLogger.level_to_index(level)
|
24
24
|
@level = level
|
@@ -263,6 +263,9 @@ module SemanticLogger
|
|
263
263
|
# level_index
|
264
264
|
# Internal index of the log level
|
265
265
|
#
|
266
|
+
# exception
|
267
|
+
# Ruby Exception object to log
|
268
|
+
#
|
266
269
|
# metric [Object]
|
267
270
|
# Object supplied when benchmark_x was called
|
268
271
|
Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric)
|
@@ -47,9 +47,9 @@ module SemanticLogger
|
|
47
47
|
# Ruby built-in Logger, or any logger that implements the following methods:
|
48
48
|
# :debug, :info, :warn, :error, :fatal
|
49
49
|
#
|
50
|
-
#
|
50
|
+
# level [Symbol]
|
51
51
|
# Optional
|
52
|
-
# By setting the
|
52
|
+
# By setting the level higher than the SemanticLogger::default_level
|
53
53
|
# this appender can exclude lower level log messages
|
54
54
|
# Any one of SemanticLogger::LEVELS. For example: :trace, :debug, :info, :warn, :error, :fatal
|
55
55
|
#
|
@@ -82,13 +82,13 @@ module SemanticLogger
|
|
82
82
|
# logger.info "Hello World"
|
83
83
|
# logger.debug("Login time", :user => 'Joe', :duration => 100, :ip_address=>'127.0.0.1')
|
84
84
|
#
|
85
|
-
def self.add_appender(appender,
|
85
|
+
def self.add_appender(appender, level=nil, &block)
|
86
86
|
appender_instance = if appender.is_a?(String) || appender.is_a?(IO)
|
87
87
|
# $stderr, STDOUT, other IO, or a filename
|
88
|
-
SemanticLogger::Appender::File.new(appender,
|
88
|
+
SemanticLogger::Appender::File.new(appender, level, &block)
|
89
89
|
elsif appender.is_a? Appender::Base
|
90
90
|
# Already an instance of an appender
|
91
|
-
appender.
|
91
|
+
appender.level = level if level
|
92
92
|
appender.formatter = block if block
|
93
93
|
appender
|
94
94
|
else
|
@@ -97,7 +97,7 @@ module SemanticLogger
|
|
97
97
|
raise "Supplied appender does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..-1].inspect}"
|
98
98
|
end
|
99
99
|
|
100
|
-
raise "Change the log level to #{
|
100
|
+
raise "Change the log level to #{level}, update the log level directly against the supplied appender" if level
|
101
101
|
SemanticLogger::Appender::Wrapper.new(appender, &block)
|
102
102
|
end
|
103
103
|
@@appenders << appender_instance
|
@@ -192,7 +192,7 @@ module SemanticLogger
|
|
192
192
|
# Also supports mapping the ::Logger levels to SemanticLogger levels
|
193
193
|
def self.level_to_index(level)
|
194
194
|
return if level.nil?
|
195
|
-
|
195
|
+
|
196
196
|
index = if level.is_a?(Symbol)
|
197
197
|
LEVELS.index(level)
|
198
198
|
elsif level.is_a?(String)
|
@@ -216,4 +216,4 @@ module SemanticLogger
|
|
216
216
|
# Initial default Level for all new instances of SemanticLogger::Logger
|
217
217
|
@@default_level = :info
|
218
218
|
@@default_level_index = level_to_index(@@default_level)
|
219
|
-
end
|
219
|
+
end
|
@@ -37,7 +37,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
|
|
37
37
|
assert_equal nil, document['thread_name']
|
38
38
|
assert_equal nil, document['time']
|
39
39
|
assert_equal nil, document['payload']
|
40
|
-
assert_equal
|
40
|
+
assert_equal $$, document['pid']
|
41
41
|
assert_equal 'test', document['host_name']
|
42
42
|
assert_equal 'test_application', document['application']
|
43
43
|
end
|
@@ -53,7 +53,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
|
|
53
53
|
assert_equal nil, document['thread_name']
|
54
54
|
assert_equal nil, document['time']
|
55
55
|
assert_equal({ "tracking_number" => 12345, "session_id" => 'HSSKLEU@JDK767'}, document['payload'])
|
56
|
-
assert_equal
|
56
|
+
assert_equal $$, document['pid']
|
57
57
|
assert_equal 'test', document['host_name']
|
58
58
|
assert_equal 'test_application', document['application']
|
59
59
|
end
|
@@ -72,7 +72,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
|
|
72
72
|
assert_equal 'thread', document['thread_name']
|
73
73
|
assert_equal @time.to_i, document['time'].to_i
|
74
74
|
assert_equal({ "tracking_number" => 12345, "session_id" => 'HSSKLEU@JDK767'}, document['payload'])
|
75
|
-
assert_equal
|
75
|
+
assert_equal $$, document['pid']
|
76
76
|
assert_equal 'test', document['host_name']
|
77
77
|
assert_equal 'test_application', document['application']
|
78
78
|
end
|
@@ -90,7 +90,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
|
|
90
90
|
assert_equal 'thread', document['thread_name']
|
91
91
|
assert_equal @time.to_i, document['time'].to_i
|
92
92
|
assert_equal nil, document['payload']
|
93
|
-
assert_equal
|
93
|
+
assert_equal $$, document['pid']
|
94
94
|
assert_equal 'test', document['host_name']
|
95
95
|
assert_equal 'test_application', document['application']
|
96
96
|
end
|
@@ -107,7 +107,7 @@ class AppenderMongoDBTest < Test::Unit::TestCase
|
|
107
107
|
assert_equal 'thread', document['thread_name']
|
108
108
|
assert_equal @time.to_i, document['time'].to_i
|
109
109
|
assert_equal({ "tracking_number" => 12345, "session_id" => 'HSSKLEU@JDK767'}, document['payload'])
|
110
|
-
assert_equal
|
110
|
+
assert_equal $$, document['pid']
|
111
111
|
assert_equal 'test', document['host_name']
|
112
112
|
assert_equal 'test_application', document['application']
|
113
113
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Allow test to be run in-place without requiring a gem install
|
2
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
# Load mocked newrelic_rpm from test directory
|
4
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
5
|
+
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'test/unit'
|
9
|
+
require 'shoulda'
|
10
|
+
require 'mocha/setup'
|
11
|
+
require 'semantic_logger'
|
12
|
+
|
13
|
+
# Unit Test for SemanticLogger::Appender::NewRelic
|
14
|
+
#
|
15
|
+
class AppenderNewRelicTest < Test::Unit::TestCase
|
16
|
+
context SemanticLogger::Appender::NewRelic do
|
17
|
+
|
18
|
+
setup do
|
19
|
+
@appender = SemanticLogger::Appender::NewRelic.new
|
20
|
+
@message = 'AppenderNewRelicTest log message'
|
21
|
+
@multi_line_message = <<-EOSTR
|
22
|
+
|
23
|
+
|
24
|
+
first non-blank line
|
25
|
+
second non-blank line
|
26
|
+
|
27
|
+
third non-blank line
|
28
|
+
|
29
|
+
EOSTR
|
30
|
+
end
|
31
|
+
|
32
|
+
(SemanticLogger::LEVELS - [:error, :fatal]).each do |level|
|
33
|
+
should "not send :#{level.to_s} notifications to New Relic" do
|
34
|
+
@appender.tagged('test') do
|
35
|
+
@appender.send(level, "AppenderNewRelicTest #{level.to_s} message")
|
36
|
+
end
|
37
|
+
assert_nil ::NewRelic::Agent.message
|
38
|
+
assert_nil ::NewRelic::Agent.hash, ::NewRelic::Agent.hash
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
[:error, :fatal].each do |level|
|
43
|
+
should "send :#{level.to_s} notifications to New Relic" do
|
44
|
+
@appender.tagged('test') do
|
45
|
+
@appender.send(level, @message)
|
46
|
+
end
|
47
|
+
assert_equal @message, ::NewRelic::Agent.message
|
48
|
+
assert_equal ['test'], ::NewRelic::Agent.hash[:custom_params][:tags], ::NewRelic::Agent.hash
|
49
|
+
assert_equal "SemanticLogger::Appender::NewRelic/#{@message}", ::NewRelic::Agent.hash[:metric]
|
50
|
+
assert_nil ::NewRelic::Agent.hash[:custom_params][:duration], ::NewRelic::Agent.hash
|
51
|
+
assert_not_nil ::NewRelic::Agent.hash[:custom_params][:thread_name], ::NewRelic::Agent.hash
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
should 'send notification to New Relic with custom attributes' do
|
56
|
+
@appender.tagged('test') do
|
57
|
+
@appender.with_payload({:key1 => 1, :key2 => 'a'}) do
|
58
|
+
@appender.benchmark(:error, @message) do
|
59
|
+
sleep 0.001
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
assert_equal @message, ::NewRelic::Agent.message
|
64
|
+
assert_equal ['test'], ::NewRelic::Agent.hash[:custom_params][:tags], ::NewRelic::Agent.hash
|
65
|
+
assert_equal "SemanticLogger::Appender::NewRelic/#{@message}", ::NewRelic::Agent.hash[:metric]
|
66
|
+
assert_equal({:key1 => 1, :key2 => 'a'}, ::NewRelic::Agent.hash[:custom_params][:payload], ::NewRelic::Agent.hash)
|
67
|
+
assert_not_nil ::NewRelic::Agent.hash[:custom_params][:duration], ::NewRelic::Agent.hash
|
68
|
+
assert_not_nil ::NewRelic::Agent.hash[:custom_params][:thread_name], ::NewRelic::Agent.hash
|
69
|
+
end
|
70
|
+
|
71
|
+
should 'use the first non-blank line for a multi-line message' do
|
72
|
+
@appender.tagged('test') do
|
73
|
+
@appender.error @multi_line_message
|
74
|
+
end
|
75
|
+
assert_equal 'first non-blank line', ::NewRelic::Agent.message
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Mocked NewRelic::Agent used for testing
|
2
|
+
|
3
|
+
module NewRelic
|
4
|
+
module Agent
|
5
|
+
@@message = nil
|
6
|
+
@@hash = nil
|
7
|
+
|
8
|
+
def self.notice_error(message, hash)
|
9
|
+
@@message = message
|
10
|
+
@@hash = hash
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.message
|
14
|
+
@@message
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.hash
|
18
|
+
@@hash
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
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.9.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-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sync_attr
|
@@ -38,8 +38,8 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 0.1.0
|
41
|
-
description:
|
42
|
-
|
41
|
+
description: Next generation logging system for Ruby to support highly concurrent,
|
42
|
+
high throughput, low latency systems
|
43
43
|
email:
|
44
44
|
- reidmo@gmail.com
|
45
45
|
executables: []
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- lib/semantic_logger/appender/base.rb
|
54
54
|
- lib/semantic_logger/appender/file.rb
|
55
55
|
- lib/semantic_logger/appender/mongodb.rb
|
56
|
+
- lib/semantic_logger/appender/new_relic.rb
|
56
57
|
- lib/semantic_logger/appender/syslog.rb
|
57
58
|
- lib/semantic_logger/appender/wrapper.rb
|
58
59
|
- lib/semantic_logger/base.rb
|
@@ -63,11 +64,13 @@ files:
|
|
63
64
|
- lib/semantic_logger/version.rb
|
64
65
|
- test/appender_file_test.rb
|
65
66
|
- test/appender_mongodb_test.rb
|
67
|
+
- test/appender_new_relic_test.rb
|
66
68
|
- test/appender_syslog_test.rb
|
67
69
|
- test/appender_wrapper_test.rb
|
68
70
|
- test/loggable_test.rb
|
69
71
|
- test/logger_test.rb
|
70
72
|
- test/mock_logger.rb
|
73
|
+
- test/newrelic_rpm.rb
|
71
74
|
homepage: https://github.com/reidmorrison/semantic_logger
|
72
75
|
licenses:
|
73
76
|
- Apache License V2.0
|
@@ -95,8 +98,10 @@ summary: Scalable, next generation logging for Ruby
|
|
95
98
|
test_files:
|
96
99
|
- test/appender_file_test.rb
|
97
100
|
- test/appender_mongodb_test.rb
|
101
|
+
- test/appender_new_relic_test.rb
|
98
102
|
- test/appender_syslog_test.rb
|
99
103
|
- test/appender_wrapper_test.rb
|
100
104
|
- test/loggable_test.rb
|
101
105
|
- test/logger_test.rb
|
102
106
|
- test/mock_logger.rb
|
107
|
+
- test/newrelic_rpm.rb
|