semantic_logger 2.8.0 → 2.9.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/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
|