semantic_logger 3.1.0 → 3.2.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 +1 -1
- data/lib/semantic_logger.rb +1 -0
- data/lib/semantic_logger/appender/bugsnag.rb +3 -6
- data/lib/semantic_logger/appender/elasticsearch.rb +1 -2
- data/lib/semantic_logger/appender/file.rb +1 -2
- data/lib/semantic_logger/appender/graylog.rb +1 -2
- data/lib/semantic_logger/appender/honeybadger.rb +56 -0
- data/lib/semantic_logger/appender/http.rb +30 -12
- data/lib/semantic_logger/appender/mongodb.rb +1 -2
- data/lib/semantic_logger/appender/new_relic.rb +1 -2
- data/lib/semantic_logger/appender/splunk.rb +93 -33
- data/lib/semantic_logger/appender/splunk_http.rb +1 -1
- data/lib/semantic_logger/appender/syslog.rb +1 -2
- data/lib/semantic_logger/appender/wrapper.rb +1 -2
- data/lib/semantic_logger/base.rb +29 -93
- data/lib/semantic_logger/logger.rb +22 -17
- data/lib/semantic_logger/metrics/new_relic.rb +14 -2
- data/lib/semantic_logger/metrics/statsd.rb +3 -3
- data/lib/semantic_logger/semantic_logger.rb +115 -5
- data/lib/semantic_logger/version.rb +1 -1
- data/test/appender/honeybadger_test.rb +40 -0
- data/test/appender/splunk_test.rb +63 -32
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: efbdb066c354033b8f3e16d20855e5cd41a9ca14
|
4
|
+
data.tar.gz: cbbf43104fc6fc965f0aceb66c1aa39e83927779
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 973c536da77a0c8346c039a2564aa7d01b70b4e5ff859e0d7c45eb5c956affa41e052302d87ef7483c72cf7b2868d73ccc3a6280edc9d78f2aae3d8adfa93db0
|
7
|
+
data.tar.gz: 13e0efe329c732223cb76e12289a934c556830d424b9881e52dcd8023f76b0a8e9c8c17734fa2f1f822d67ee673945fe5c0da3aeb6be6c70cd15489baf9fb4ba
|
data/README.md
CHANGED
@@ -65,7 +65,7 @@ require 'semantic_logger'
|
|
65
65
|
SemanticLogger.default_level = :trace
|
66
66
|
|
67
67
|
# Log to a file, and use the colorized formatter
|
68
|
-
SemanticLogger.add_appender(
|
68
|
+
SemanticLogger.add_appender(file_name: 'development.log', formatter: :color)
|
69
69
|
```
|
70
70
|
|
71
71
|
If running rails, see: [Semantic Logger Rails](http://rocketjob.github.io/semantic_logger/rails.html)
|
data/lib/semantic_logger.rb
CHANGED
@@ -17,6 +17,7 @@ module SemanticLogger
|
|
17
17
|
autoload :Elasticsearch, 'semantic_logger/appender/elasticsearch'
|
18
18
|
autoload :File, 'semantic_logger/appender/file'
|
19
19
|
autoload :Graylog, 'semantic_logger/appender/graylog'
|
20
|
+
autoload :Honeybadger, 'semantic_logger/appender/honeybadger'
|
20
21
|
autoload :Http, 'semantic_logger/appender/http'
|
21
22
|
autoload :MongoDB, 'semantic_logger/appender/mongodb'
|
22
23
|
autoload :NewRelic, 'semantic_logger/appender/new_relic'
|
@@ -52,12 +52,9 @@ class SemanticLogger::Appender::Bugsnag < SemanticLogger::Appender::Base
|
|
52
52
|
|
53
53
|
# Send an error notification to Bugsnag
|
54
54
|
def log(log)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
(log.name == 'Bugsnag') ||
|
59
|
-
# Filtered out?
|
60
|
-
!include_message?(log)
|
55
|
+
return false unless should_log?(log)
|
56
|
+
# Ignore logs coming from Bugsnag itself
|
57
|
+
return false if log.name == 'Bugsnag'
|
61
58
|
|
62
59
|
# Send error messages as Runtime exceptions
|
63
60
|
exception =
|
@@ -47,8 +47,7 @@ class SemanticLogger::Appender::Elasticsearch < SemanticLogger::Appender::Http
|
|
47
47
|
|
48
48
|
# Log to the index for today
|
49
49
|
def log(log)
|
50
|
-
return false
|
51
|
-
!include_message?(log) # Filtered out?
|
50
|
+
return false unless should_log?(log)
|
52
51
|
|
53
52
|
post(formatter.call(log, self), "#{index}-#{log.time.strftime('%Y.%m.%d')}/#{type}")
|
54
53
|
end
|
@@ -109,8 +109,7 @@ module SemanticLogger
|
|
109
109
|
# trace entries are mapped to debug since :trace is not supported by the
|
110
110
|
# Ruby or Rails Loggers
|
111
111
|
def log(log)
|
112
|
-
|
113
|
-
return false if (level_index > (log.level_index || 0)) || !include_message?(log)
|
112
|
+
return false unless should_log?(log)
|
114
113
|
|
115
114
|
# Since only one appender thread will be writing to the file at a time
|
116
115
|
# it is not necessary to protect access to the file with a semaphore
|
@@ -112,8 +112,7 @@ class SemanticLogger::Appender::Graylog < SemanticLogger::Appender::Base
|
|
112
112
|
|
113
113
|
# Forward log messages
|
114
114
|
def log(log)
|
115
|
-
return false
|
116
|
-
!include_message?(log) # Filtered out?
|
115
|
+
return false unless should_log?(log)
|
117
116
|
|
118
117
|
@notifier.notify!(formatter.call(log, self))
|
119
118
|
true
|
@@ -0,0 +1,56 @@
|
|
1
|
+
begin
|
2
|
+
require 'honeybadger'
|
3
|
+
rescue LoadError
|
4
|
+
raise 'Gem honeybadger is required for logging purposes. Please add the gem "honeybadger" to your Gemfile.'
|
5
|
+
end
|
6
|
+
|
7
|
+
# Send log messages to honeybadger
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
# SemanticLogger.add_appender(appender: :honeybadger)
|
11
|
+
#
|
12
|
+
class SemanticLogger::Appender::Honeybadger < SemanticLogger::Appender::Base
|
13
|
+
# Create Appender
|
14
|
+
#
|
15
|
+
# Parameters
|
16
|
+
# level: [:trace | :debug | :info | :warn | :error | :fatal]
|
17
|
+
# Override the log level for this appender.
|
18
|
+
# Default: :error
|
19
|
+
#
|
20
|
+
# formatter: [Object|Proc]
|
21
|
+
# An instance of a class that implements #call, or a Proc to be used to format
|
22
|
+
# the output from this appender
|
23
|
+
# Default: Use the built-in formatter (See: #call)
|
24
|
+
#
|
25
|
+
# filter: [Regexp|Proc]
|
26
|
+
# RegExp: Only include log messages where the class name matches the supplied.
|
27
|
+
# regular expression. All other messages will be ignored.
|
28
|
+
# Proc: Only include log messages where the supplied Proc returns true
|
29
|
+
# The Proc must return true or false.
|
30
|
+
def initialize(options = {}, &block)
|
31
|
+
options = {level: options} unless options.is_a?(Hash)
|
32
|
+
@options = options.dup
|
33
|
+
level = @options.delete(:level) || :error
|
34
|
+
|
35
|
+
super(level, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Send an error notification to honeybadger
|
39
|
+
def log(log)
|
40
|
+
return false unless should_log?(log)
|
41
|
+
|
42
|
+
if log.exception
|
43
|
+
Honeybadger.notify(log.exception, log.to_h)
|
44
|
+
else
|
45
|
+
message = {
|
46
|
+
error_class: log.name,
|
47
|
+
error_message: log.message,
|
48
|
+
backtrace: log.backtrace,
|
49
|
+
context: log.to_h,
|
50
|
+
}
|
51
|
+
Honeybadger.notify(message)
|
52
|
+
end
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -16,7 +16,8 @@ require 'json'
|
|
16
16
|
# url: 'http://localhost:8088/path'
|
17
17
|
# )
|
18
18
|
class SemanticLogger::Appender::Http < SemanticLogger::Appender::Base
|
19
|
-
attr_accessor :username, :application, :host, :compress, :header
|
19
|
+
attr_accessor :username, :application, :host, :compress, :header,
|
20
|
+
:open_timeout, :read_timeout, :continue_timeout
|
20
21
|
attr_reader :http, :url, :server, :port, :path, :ssl_options
|
21
22
|
|
22
23
|
# Create HTTP(S) log appender
|
@@ -67,15 +68,28 @@ class SemanticLogger::Appender::Http < SemanticLogger::Appender::Base
|
|
67
68
|
# regular expression. All other messages will be ignored.
|
68
69
|
# Proc: Only include log messages where the supplied Proc returns true
|
69
70
|
# The Proc must return true or false.
|
71
|
+
#
|
72
|
+
# open_timeout: [Float]
|
73
|
+
# Default: 2.0
|
74
|
+
#
|
75
|
+
# read_timeout: [Float]
|
76
|
+
# Default: 1.0
|
77
|
+
#
|
78
|
+
# continue_timeout: [Float]
|
79
|
+
# Default: 1.0
|
70
80
|
def initialize(options, &block)
|
71
|
-
options
|
72
|
-
@url
|
73
|
-
@ssl_options
|
74
|
-
@username
|
75
|
-
@password
|
76
|
-
@application
|
77
|
-
@host
|
78
|
-
@compress
|
81
|
+
options = options.dup
|
82
|
+
@url = options.delete(:url)
|
83
|
+
@ssl_options = options.delete(:ssl)
|
84
|
+
@username = options.delete(:username)
|
85
|
+
@password = options.delete(:password)
|
86
|
+
@application = options.delete(:application) || 'Semantic Logger'
|
87
|
+
@host = options.delete(:host) || SemanticLogger.host
|
88
|
+
@compress = options.delete(:compress) || false
|
89
|
+
@open_timeout = options.delete(:open_timeout) || 2.0
|
90
|
+
@read_timeout = options.delete(:read_timeout) || 1.0
|
91
|
+
@continue_timeout = options.delete(:continue_timeout) || 1.0
|
92
|
+
|
79
93
|
unless options.has_key?(:formatter)
|
80
94
|
options[:formatter] = block || (respond_to?(:call) ? self : SemanticLogger::Formatters::Json.new)
|
81
95
|
end
|
@@ -109,12 +123,16 @@ class SemanticLogger::Appender::Http < SemanticLogger::Appender::Base
|
|
109
123
|
def reopen
|
110
124
|
# On Ruby v2.0 and greater, Net::HTTP.new uses a persistent connection if the server allows it
|
111
125
|
@http = @ssl_options ? Net::HTTP.new(server, port, @ssl_options) : Net::HTTP.new(server, port)
|
126
|
+
|
127
|
+
@http.open_timeout = @open_timeout
|
128
|
+
@http.read_timeout = @read_timeout
|
129
|
+
@http.continue_timeout = @continue_timeout
|
112
130
|
end
|
113
131
|
|
114
132
|
# Forward log messages to HTTP Server
|
115
133
|
def log(log)
|
116
|
-
return false
|
117
|
-
|
134
|
+
return false unless should_log?(log)
|
135
|
+
|
118
136
|
post(formatter.call(log, self))
|
119
137
|
end
|
120
138
|
|
@@ -158,7 +176,7 @@ class SemanticLogger::Appender::Http < SemanticLogger::Appender::Base
|
|
158
176
|
true
|
159
177
|
else
|
160
178
|
# Failures are logged to the global semantic logger failsafe logger (Usually stderr or file)
|
161
|
-
SemanticLogger::Logger.logger.error("Bad HTTP response code: #{response.code}, #{response.body}")
|
179
|
+
SemanticLogger::Logger.logger.error("Bad HTTP response from: #{url} code: #{response.code}, #{response.body}")
|
162
180
|
false
|
163
181
|
end
|
164
182
|
end
|
@@ -171,8 +171,7 @@ module SemanticLogger
|
|
171
171
|
|
172
172
|
# Log the message to MongoDB
|
173
173
|
def log(log)
|
174
|
-
|
175
|
-
return false if (level_index > (log.level_index || 0)) || !include_message?(log)
|
174
|
+
return false unless should_log?(log)
|
176
175
|
|
177
176
|
# Insert log entry into Mongo
|
178
177
|
collection.insert(formatter.call(log, self), w: @write_concern)
|
@@ -47,8 +47,7 @@ class SemanticLogger::Appender::NewRelic < SemanticLogger::Appender::Base
|
|
47
47
|
|
48
48
|
# Send an error notification to New Relic
|
49
49
|
def log(log)
|
50
|
-
|
51
|
-
return false if (level_index > (log.level_index || 0)) || !include_message?(log)
|
50
|
+
return false unless should_log?(log)
|
52
51
|
|
53
52
|
# Send error messages as Runtime exceptions
|
54
53
|
exception =
|
@@ -4,61 +4,121 @@ rescue LoadError
|
|
4
4
|
raise 'Gem splunk-sdk-ruby is required for logging to Splunk. Please add the gem "splunk-sdk-ruby" to your Gemfile.'
|
5
5
|
end
|
6
6
|
|
7
|
-
#
|
7
|
+
# Splunk log appender.
|
8
|
+
#
|
9
|
+
# Use the official splunk gem to log messages to Splunk.
|
10
|
+
#
|
11
|
+
# Example
|
12
|
+
# SemanticLogger.add_appender(
|
13
|
+
# appender: :splunk,
|
14
|
+
# username: 'username',
|
15
|
+
# password: 'password',
|
16
|
+
# host: 'localhost',
|
17
|
+
# port: 8089,
|
18
|
+
# scheme: :https,
|
19
|
+
# index: 'main'
|
20
|
+
# )
|
8
21
|
class SemanticLogger::Appender::Splunk < SemanticLogger::Appender::Base
|
9
22
|
attr_reader :config, :index, :service, :service_index
|
10
23
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
24
|
+
# Write to Splunk.
|
25
|
+
#
|
26
|
+
# Parameters
|
27
|
+
# :username [String]
|
28
|
+
# User name to log into splunk with.
|
29
|
+
# Not required if :token has been supplied.
|
30
|
+
#
|
31
|
+
# :password [String]
|
32
|
+
# Password to log into splunk with.
|
33
|
+
# Not required if :token has been supplied.
|
34
|
+
#
|
35
|
+
# :token
|
36
|
+
# Supply a preauthenticated Splunk token instead of username and password.
|
37
|
+
# Not required if username and password are supplied.
|
38
|
+
#
|
39
|
+
# :host [String]
|
40
|
+
# Splunk host name.
|
41
|
+
# Default: 'localhost'
|
42
|
+
#
|
43
|
+
# :port [Integer]
|
44
|
+
# The Splunk management port.
|
45
|
+
# Default: 8089
|
46
|
+
#
|
47
|
+
# :scheme [Symbol]
|
48
|
+
# Either :https or :http
|
49
|
+
# Default: :https
|
50
|
+
#
|
51
|
+
# :index [String]
|
52
|
+
# Splunk index to use.
|
53
|
+
# Default: 'main'
|
54
|
+
#
|
55
|
+
# :namespace [Namespace]
|
56
|
+
# Application namespace instance.
|
57
|
+
#
|
58
|
+
# :ssl_client_cert [OpenSSL::X509::Certificate]
|
59
|
+
# Client certificate.
|
60
|
+
#
|
61
|
+
# :ssl_client_key [OpenSSL::PKey::RSA | OpenSSL::PKey::DSA]
|
62
|
+
# Client key.
|
63
|
+
#
|
64
|
+
# level: [:trace | :debug | :info | :warn | :error | :fatal]
|
65
|
+
# Override the log level for this appender.
|
66
|
+
# Default: SemanticLogger.default_level
|
67
|
+
#
|
68
|
+
# formatter: [Object|Proc]
|
69
|
+
# An instance of a class that implements #call, or a Proc to be used to format
|
70
|
+
# the output from this appender
|
71
|
+
# Default: Use the built-in formatter (See: #call)
|
72
|
+
#
|
73
|
+
# filter: [Regexp|Proc]
|
74
|
+
# RegExp: Only include log messages where the class name matches the supplied.
|
75
|
+
# regular expression. All other messages will be ignored.
|
76
|
+
# Proc: Only include log messages where the supplied Proc returns true
|
77
|
+
# The Proc must return true or false.
|
78
|
+
def initialize(options, _deprecated_level = nil, &block)
|
79
|
+
@config = options.dup
|
80
|
+
@config[:level] = _deprecated_level if _deprecated_level
|
81
|
+
@index = @config.delete(:index) || 'main'
|
14
82
|
|
15
|
-
|
16
|
-
|
83
|
+
options = {
|
84
|
+
level: @config.delete(:level) || :error,
|
85
|
+
formatter: @config.delete(:formatter),
|
86
|
+
filter: @config.delete(:filter)
|
87
|
+
}
|
17
88
|
|
18
89
|
reopen
|
19
90
|
|
20
91
|
# Pass on the level and custom formatter if supplied
|
21
|
-
super(
|
92
|
+
super(options, &block)
|
22
93
|
end
|
23
94
|
|
24
95
|
# After forking an active process call #reopen to re-open
|
25
96
|
# open the handles to resources
|
26
97
|
def reopen
|
27
98
|
# Connect to splunk. Connect is a synonym for creating a Service by hand and calling login.
|
28
|
-
|
99
|
+
self.service = Splunk::connect(config)
|
29
100
|
|
30
101
|
# The index we are logging to
|
31
|
-
|
102
|
+
self.service_index = service.indexes[index]
|
32
103
|
end
|
33
104
|
|
34
105
|
# Log the message to Splunk
|
35
106
|
def log(log)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@service_index.submit(formatter.call(log, self))
|
107
|
+
return false unless should_log?(log)
|
108
|
+
|
109
|
+
service_index.submit(log.message, formatter.call(log, self))
|
40
110
|
true
|
41
111
|
end
|
42
112
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
@index = options[:index] || 'main'
|
55
|
-
|
56
|
-
if @config[:username].nil?
|
57
|
-
raise ArgumentError, 'Must supply a username.'
|
58
|
-
elsif @config[:password].nil?
|
59
|
-
raise ArgumentError, 'Must supply a password.'
|
60
|
-
elsif @index.nil?
|
61
|
-
raise ArgumentError, 'Must supply an index.'
|
62
|
-
end
|
113
|
+
# Returns [String] JSON to send to Splunk
|
114
|
+
# For splunk format requirements see:
|
115
|
+
# http://dev.splunk.com/view/event-collector/SP-CAAAE6P
|
116
|
+
def call(log, _logger)
|
117
|
+
h = log.to_h
|
118
|
+
h.delete(:message)
|
119
|
+
h.delete(:application)
|
120
|
+
h.delete(:host)
|
121
|
+
h.delete(:time)
|
122
|
+
h
|
63
123
|
end
|
64
124
|
end
|
@@ -211,8 +211,7 @@ module SemanticLogger
|
|
211
211
|
|
212
212
|
# Write the log using the specified protocol and server.
|
213
213
|
def log(log)
|
214
|
-
|
215
|
-
return false if (level_index > (log.level_index || 0)) || !include_message?(log)
|
214
|
+
return false unless should_log?(log)
|
216
215
|
|
217
216
|
case @protocol
|
218
217
|
when :syslog
|
@@ -58,8 +58,7 @@ module SemanticLogger
|
|
58
58
|
# trace entries are mapped to debug since :trace is not supported by the
|
59
59
|
# Ruby or Rails Loggers
|
60
60
|
def log(log)
|
61
|
-
|
62
|
-
return false if (level_index > (log.level_index || 0)) || !include_message?(log)
|
61
|
+
return false unless should_log?(log)
|
63
62
|
|
64
63
|
@logger.send(log.level == :trace ? :debug : log.level, formatter.call(log, self))
|
65
64
|
true
|
data/lib/semantic_logger/base.rb
CHANGED
@@ -62,7 +62,7 @@ module SemanticLogger
|
|
62
62
|
# SemanticLogger.add_appender(io: STDOUT, formatter: :color)
|
63
63
|
#
|
64
64
|
# # And log to a file at the same time
|
65
|
-
# SemanticLogger.add_appender(
|
65
|
+
# SemanticLogger.add_appender(file_name: 'application.log', formatter: :color)
|
66
66
|
#
|
67
67
|
# logger = SemanticLogger['MyApplication']
|
68
68
|
# logger.debug("Only display this if log level is set to Debug or lower")
|
@@ -118,51 +118,37 @@ module SemanticLogger
|
|
118
118
|
|
119
119
|
alias_method :benchmark, :measure
|
120
120
|
|
121
|
-
#
|
122
|
-
|
123
|
-
|
124
|
-
(Thread.current[:semantic_logger_tags] ||= []) << tag
|
125
|
-
yield
|
126
|
-
ensure
|
127
|
-
Thread.current[:semantic_logger_tags].pop
|
128
|
-
end
|
129
|
-
|
130
|
-
# Add the supplied tags to the list of tags to log for this thread whilst
|
131
|
-
# the supplied block is active
|
132
|
-
# Returns nil if no tags are currently set
|
133
|
-
# To support: ActiveSupport::TaggedLogging V3 and above
|
134
|
-
def tagged(*tags)
|
135
|
-
new_tags = push_tags(*tags)
|
136
|
-
yield self
|
137
|
-
ensure
|
138
|
-
pop_tags(new_tags.size)
|
121
|
+
# :nodoc:
|
122
|
+
def tagged(*tags, &block)
|
123
|
+
SemanticLogger.tagged(*tags, &block)
|
139
124
|
end
|
140
125
|
|
141
|
-
#
|
126
|
+
# :nodoc:
|
142
127
|
alias_method :with_tags, :tagged
|
143
128
|
|
144
|
-
#
|
145
|
-
# Returns nil if no tags are set
|
129
|
+
# :nodoc:
|
146
130
|
def tags
|
147
|
-
|
148
|
-
|
149
|
-
|
131
|
+
SemanticLogger.tags
|
132
|
+
end
|
133
|
+
|
134
|
+
# :nodoc:
|
135
|
+
def push_tags(*tags)
|
136
|
+
SemanticLogger.push_tags(*tags)
|
137
|
+
end
|
138
|
+
|
139
|
+
# :nodoc:
|
140
|
+
def pop_tags(quantity = 1)
|
141
|
+
SemanticLogger.pop_tags(quantity)
|
150
142
|
end
|
151
143
|
|
152
|
-
#
|
153
|
-
|
154
|
-
|
155
|
-
# Need to flatten and reject empties to support calls from Rails 4
|
156
|
-
new_tags = tags.flatten.collect(&:to_s).reject(&:empty?)
|
157
|
-
t = Thread.current[:semantic_logger_tags]
|
158
|
-
Thread.current[:semantic_logger_tags] = t.nil? ? new_tags : t.concat(new_tags)
|
159
|
-
new_tags
|
144
|
+
# :nodoc:
|
145
|
+
def silence(new_level = :error, &block)
|
146
|
+
SemanticLogger.silence(new_level, &block)
|
160
147
|
end
|
161
148
|
|
162
|
-
#
|
163
|
-
def
|
164
|
-
|
165
|
-
t.pop(quantity) unless t.nil?
|
149
|
+
# :nodoc:
|
150
|
+
def fast_tag(tag, &block)
|
151
|
+
SemanticLogger.fast_tag(tag, &block)
|
166
152
|
end
|
167
153
|
|
168
154
|
# Thread specific context information to be logged with every log entry
|
@@ -194,62 +180,6 @@ module SemanticLogger
|
|
194
180
|
Thread.current[:semantic_logger_payload]
|
195
181
|
end
|
196
182
|
|
197
|
-
# Silence noisy log levels by changing the default_level within the block
|
198
|
-
#
|
199
|
-
# This setting is thread-safe and only applies to the current thread
|
200
|
-
#
|
201
|
-
# Any threads spawned within the block will not be affected by this setting
|
202
|
-
#
|
203
|
-
# #silence can be used to both raise and lower the log level within
|
204
|
-
# the supplied block.
|
205
|
-
#
|
206
|
-
# Example:
|
207
|
-
#
|
208
|
-
# # Perform trace level logging within the block when the default is higher
|
209
|
-
# SemanticLogger.default_level = :info
|
210
|
-
#
|
211
|
-
# logger.debug 'this will _not_ be logged'
|
212
|
-
#
|
213
|
-
# logger.silence(:trace) do
|
214
|
-
# logger.debug "this will be logged"
|
215
|
-
# end
|
216
|
-
#
|
217
|
-
# Parameters
|
218
|
-
# new_level
|
219
|
-
# The new log level to apply within the block
|
220
|
-
# Default: :error
|
221
|
-
#
|
222
|
-
# Example:
|
223
|
-
# # Silence all logging below :error level
|
224
|
-
# logger.silence do
|
225
|
-
# logger.info "this will _not_ be logged"
|
226
|
-
# logger.warn "this neither"
|
227
|
-
# logger.error "but errors will be logged"
|
228
|
-
# end
|
229
|
-
#
|
230
|
-
# Note:
|
231
|
-
# #silence does not affect any loggers which have had their log level set
|
232
|
-
# explicitly. I.e. That do not rely on the global default level
|
233
|
-
def silence(new_level = :error)
|
234
|
-
current_index = Thread.current[:semantic_logger_silence]
|
235
|
-
Thread.current[:semantic_logger_silence] = SemanticLogger.level_to_index(new_level)
|
236
|
-
yield
|
237
|
-
ensure
|
238
|
-
Thread.current[:semantic_logger_silence] = current_index
|
239
|
-
end
|
240
|
-
|
241
|
-
# DEPRECATED See SemanticLogger.default_level=
|
242
|
-
def self.default_level=(level)
|
243
|
-
warn '[DEPRECATION] SemanticLogger::Logger.default_level= is deprecated. Please use SemanticLogger.default_level= instead.'
|
244
|
-
SemanticLogger.default_level = level
|
245
|
-
end
|
246
|
-
|
247
|
-
# DEPRECATED See SemanticLogger.default_level
|
248
|
-
def self.default_level
|
249
|
-
warn '[DEPRECATION] SemanticLogger::Logger.default_level is deprecated. Please use SemanticLogger.default_level instead.'
|
250
|
-
SemanticLogger.default_level
|
251
|
-
end
|
252
|
-
|
253
183
|
protected
|
254
184
|
|
255
185
|
# Write log data to underlying data storage
|
@@ -304,6 +234,12 @@ module SemanticLogger
|
|
304
234
|
end
|
305
235
|
end
|
306
236
|
|
237
|
+
# Whether the log message should be logged for the current logger or appender
|
238
|
+
def should_log?(log)
|
239
|
+
# Ensure minimum log level is met, and check filter
|
240
|
+
(level_index <= (log.level_index || 0)) && include_message?(log)
|
241
|
+
end
|
242
|
+
|
307
243
|
# Log message at the specified level
|
308
244
|
def log_internal(level, index, message=nil, payload=nil, exception=nil)
|
309
245
|
# Exception being logged?
|
@@ -85,21 +85,14 @@ module SemanticLogger
|
|
85
85
|
@@logger = logger
|
86
86
|
end
|
87
87
|
|
88
|
-
#
|
89
|
-
def self.appenders
|
90
|
-
warn '[DEPRECATION] SemanticLogger::Logger.appenders is deprecated. Please use SemanticLogger.add_appender instead.'
|
91
|
-
SemanticLogger.appenders
|
92
|
-
end
|
93
|
-
|
94
|
-
# DEPRECATED: Please use queue_size instead.
|
95
|
-
def self.cache_count
|
96
|
-
warn '[DEPRECATION] SemanticLogger::Logger.cache_count is deprecated. Please use SemanticLogger::Logger.queue_size instead.'
|
97
|
-
queue_size
|
98
|
-
end
|
99
|
-
|
100
|
-
# Supply a block to be called whenever a metric is seen during measure logging
|
88
|
+
# Supply a metrics appender to be called whenever a logging metric is encountered
|
101
89
|
#
|
102
90
|
# Parameters
|
91
|
+
# appender: [Symbol | Object | Proc]
|
92
|
+
# [Proc] the block to call.
|
93
|
+
# [Object] the block on which to call #call.
|
94
|
+
# [Symbol] :new_relic, or :statsd to forward metrics to
|
95
|
+
#
|
103
96
|
# block
|
104
97
|
# The block to be called
|
105
98
|
#
|
@@ -107,9 +100,21 @@ module SemanticLogger
|
|
107
100
|
# SemanticLogger.on_metric do |log|
|
108
101
|
# puts "#{log.metric} was received. Log Struct: #{log.inspect}"
|
109
102
|
# end
|
110
|
-
|
111
|
-
|
112
|
-
|
103
|
+
#
|
104
|
+
# Note:
|
105
|
+
# * This callback is called in the logging thread.
|
106
|
+
# * Does not slow down the application.
|
107
|
+
# * Only context is what is passed in the log struct, the original thread context is not available.
|
108
|
+
def self.on_metric(options = {}, &block)
|
109
|
+
# Backward compatibility
|
110
|
+
options = options.is_a?(Hash) ? options.dup : {appender: options}
|
111
|
+
appender = block || options.delete(:appender)
|
112
|
+
|
113
|
+
# Convert symbolized metrics appender to an actual object
|
114
|
+
appender = named_appender(appender, 'SemanticLogger::Appender::Metrics').new(options) if appender.is_a?(Symbol)
|
115
|
+
|
116
|
+
raise('When supplying a metrics appender, it must support the #call method') unless appender.is_a?(Proc) || appender.respond_to?(:call)
|
117
|
+
(@@metric_subscribers ||= Concurrent::Array.new) << appender
|
113
118
|
end
|
114
119
|
|
115
120
|
# Place log request on the queue for the Appender thread to write to each
|
@@ -168,7 +173,7 @@ module SemanticLogger
|
|
168
173
|
begin
|
169
174
|
count = 0
|
170
175
|
while message = queue.pop
|
171
|
-
if message.is_a?
|
176
|
+
if message.is_a?(Log)
|
172
177
|
SemanticLogger.appenders.each do |appender|
|
173
178
|
begin
|
174
179
|
appender.log(message)
|
@@ -1,10 +1,22 @@
|
|
1
1
|
module SemanticLogger
|
2
2
|
module Metrics
|
3
3
|
class NewRelic
|
4
|
+
attr_accessor :prefix
|
5
|
+
|
6
|
+
# Parameters:
|
7
|
+
# :prefix [String]
|
8
|
+
# Prefix to add to every metric before forwarding to NewRelic
|
9
|
+
# Default: 'Custom'
|
10
|
+
def initialize(options = {})
|
11
|
+
options = options.dup
|
12
|
+
@prefix = options.delete(:prefix) || 'Custom'
|
13
|
+
raise(ArgumentError, "Unknown options: #{options.inspect}") if options.size > 0
|
14
|
+
end
|
15
|
+
|
4
16
|
def call(log)
|
5
17
|
metric = log.metric
|
6
|
-
# Add
|
7
|
-
metric = "
|
18
|
+
# Add prefix for NewRelic
|
19
|
+
metric = "#{prefix}/#{metric}" unless metric.start_with?(prefix)
|
8
20
|
|
9
21
|
if duration = log.duration
|
10
22
|
# Convert duration to seconds
|
@@ -32,13 +32,13 @@ module SemanticLogger
|
|
32
32
|
def call(log)
|
33
33
|
metric = log.metric
|
34
34
|
if duration = log.duration
|
35
|
-
|
35
|
+
@statsd.timing(metric, duration)
|
36
36
|
else
|
37
37
|
amount = (log.metric_amount || 1).round
|
38
38
|
if amount < 0
|
39
|
-
amount.times {
|
39
|
+
amount.times { @statsd.decrement(metric) }
|
40
40
|
else
|
41
|
-
amount.times {
|
41
|
+
amount.times { @statsd.increment(metric) }
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
@@ -192,6 +192,11 @@ module SemanticLogger
|
|
192
192
|
# Supply a block to be called whenever a metric is seen during measure logging
|
193
193
|
#
|
194
194
|
# Parameters
|
195
|
+
# appender: [Symbol | Object | Proc]
|
196
|
+
# [Proc] the block to call.
|
197
|
+
# [Object] the block on which to call #call.
|
198
|
+
# [Symbol] :new_relic, or :statsd to forward metrics to
|
199
|
+
#
|
195
200
|
# block
|
196
201
|
# The block to be called
|
197
202
|
#
|
@@ -199,8 +204,13 @@ module SemanticLogger
|
|
199
204
|
# SemanticLogger.on_metric do |log|
|
200
205
|
# puts "#{log.metric} was received. Log Struct: #{log.inspect}"
|
201
206
|
# end
|
202
|
-
|
203
|
-
|
207
|
+
#
|
208
|
+
# Note:
|
209
|
+
# * This callback is called in the logging thread.
|
210
|
+
# * Does not slow down the application.
|
211
|
+
# * Only context is what is passed in the log struct, the original thread context is not available.
|
212
|
+
def self.on_metric(options = {}, &block)
|
213
|
+
SemanticLogger::Logger.on_metric(options, &block)
|
204
214
|
end
|
205
215
|
|
206
216
|
# Add signal handlers for Semantic Logger
|
@@ -271,6 +281,106 @@ module SemanticLogger
|
|
271
281
|
true
|
272
282
|
end
|
273
283
|
|
284
|
+
# If the tag being supplied is definitely a string then this fast
|
285
|
+
# tag api can be used for short lived tags
|
286
|
+
def self.fast_tag(tag)
|
287
|
+
(Thread.current[:semantic_logger_tags] ||= []) << tag
|
288
|
+
yield
|
289
|
+
ensure
|
290
|
+
Thread.current[:semantic_logger_tags].pop
|
291
|
+
end
|
292
|
+
|
293
|
+
# Add the supplied named tags to the list of tags to log for this thread whilst
|
294
|
+
# the supplied block is active.
|
295
|
+
#
|
296
|
+
# Returns result of block
|
297
|
+
#
|
298
|
+
# Example:
|
299
|
+
def self.named_tags(tag)
|
300
|
+
(Thread.current[:semantic_logger_tags] ||= []) << tag
|
301
|
+
yield
|
302
|
+
ensure
|
303
|
+
Thread.current[:semantic_logger_tags].pop
|
304
|
+
end
|
305
|
+
|
306
|
+
# Add the supplied tags to the list of tags to log for this thread whilst
|
307
|
+
# the supplied block is active.
|
308
|
+
# Returns result of block
|
309
|
+
def self.tagged(*tags)
|
310
|
+
new_tags = push_tags(*tags)
|
311
|
+
yield self
|
312
|
+
ensure
|
313
|
+
pop_tags(new_tags.size)
|
314
|
+
end
|
315
|
+
|
316
|
+
# Returns a copy of the [Array] of [String] tags currently active for this thread
|
317
|
+
# Returns nil if no tags are set
|
318
|
+
def self.tags
|
319
|
+
# Since tags are stored on a per thread basis this list is thread-safe
|
320
|
+
t = Thread.current[:semantic_logger_tags]
|
321
|
+
t.nil? ? [] : t.clone
|
322
|
+
end
|
323
|
+
|
324
|
+
# Add tags to the current scope
|
325
|
+
# Returns the list of tags pushed after flattening them out and removing blanks
|
326
|
+
def self.push_tags(*tags)
|
327
|
+
# Need to flatten and reject empties to support calls from Rails 4
|
328
|
+
new_tags = tags.flatten.collect(&:to_s).reject(&:empty?)
|
329
|
+
t = Thread.current[:semantic_logger_tags]
|
330
|
+
Thread.current[:semantic_logger_tags] = t.nil? ? new_tags : t.concat(new_tags)
|
331
|
+
new_tags
|
332
|
+
end
|
333
|
+
|
334
|
+
# Remove specified number of tags from the current tag list
|
335
|
+
def self.pop_tags(quantity=1)
|
336
|
+
t = Thread.current[:semantic_logger_tags]
|
337
|
+
t.pop(quantity) unless t.nil?
|
338
|
+
end
|
339
|
+
|
340
|
+
# Silence noisy log levels by changing the default_level within the block
|
341
|
+
#
|
342
|
+
# This setting is thread-safe and only applies to the current thread
|
343
|
+
#
|
344
|
+
# Any threads spawned within the block will not be affected by this setting
|
345
|
+
#
|
346
|
+
# #silence can be used to both raise and lower the log level within
|
347
|
+
# the supplied block.
|
348
|
+
#
|
349
|
+
# Example:
|
350
|
+
#
|
351
|
+
# # Perform trace level logging within the block when the default is higher
|
352
|
+
# SemanticLogger.default_level = :info
|
353
|
+
#
|
354
|
+
# logger.debug 'this will _not_ be logged'
|
355
|
+
#
|
356
|
+
# SemanticLogger.silence(:trace) do
|
357
|
+
# logger.debug "this will be logged"
|
358
|
+
# end
|
359
|
+
#
|
360
|
+
# Parameters
|
361
|
+
# new_level
|
362
|
+
# The new log level to apply within the block
|
363
|
+
# Default: :error
|
364
|
+
#
|
365
|
+
# Example:
|
366
|
+
# # Silence all logging for this thread below :error level
|
367
|
+
# SemanticLogger.silence do
|
368
|
+
# logger.info "this will _not_ be logged"
|
369
|
+
# logger.warn "this neither"
|
370
|
+
# logger.error "but errors will be logged"
|
371
|
+
# end
|
372
|
+
#
|
373
|
+
# Note:
|
374
|
+
# #silence does not affect any loggers which have had their log level set
|
375
|
+
# explicitly. I.e. That do not rely on the global default level
|
376
|
+
def self.silence(new_level = :error)
|
377
|
+
current_index = Thread.current[:semantic_logger_silence]
|
378
|
+
Thread.current[:semantic_logger_silence] = SemanticLogger.level_to_index(new_level)
|
379
|
+
yield
|
380
|
+
ensure
|
381
|
+
Thread.current[:semantic_logger_silence] = current_index
|
382
|
+
end
|
383
|
+
|
274
384
|
private
|
275
385
|
|
276
386
|
@@appenders = Concurrent::Array.new
|
@@ -345,14 +455,14 @@ module SemanticLogger
|
|
345
455
|
end
|
346
456
|
end
|
347
457
|
|
348
|
-
def self.named_appender(appender)
|
458
|
+
def self.named_appender(appender, root = 'SemanticLogger::Appender')
|
349
459
|
appender = appender.to_s
|
350
460
|
klass = appender.respond_to?(:camelize) ? appender.camelize : camelize(appender)
|
351
|
-
klass = "
|
461
|
+
klass = "#{root}::#{klass}"
|
352
462
|
begin
|
353
463
|
appender.respond_to?(:constantize) ? klass.constantize : eval(klass)
|
354
464
|
rescue NameError
|
355
|
-
raise(ArgumentError, "Could not find
|
465
|
+
raise(ArgumentError, "Could not find #{root} class: #{klass} for #{appender}")
|
356
466
|
end
|
357
467
|
end
|
358
468
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
# Unit Test for SemanticLogger::Appender::Bugsnag
|
4
|
+
module Appender
|
5
|
+
class HoneybadgerTest < Minitest::Test
|
6
|
+
describe SemanticLogger::Appender::Honeybadger do
|
7
|
+
before do
|
8
|
+
@appender = SemanticLogger::Appender::Honeybadger.new(:trace)
|
9
|
+
@message = 'AppenderHoneybadgerTest log message'
|
10
|
+
end
|
11
|
+
|
12
|
+
SemanticLogger::LEVELS.each do |level|
|
13
|
+
it "sends #{level} message" do
|
14
|
+
hash = nil
|
15
|
+
Honeybadger.stub(:notify, -> h { hash = h }) do
|
16
|
+
@appender.send(level, @message)
|
17
|
+
end
|
18
|
+
assert_equal @message, hash[:error_message]
|
19
|
+
assert_equal 'SemanticLogger::Appender::Honeybadger', hash[:error_class]
|
20
|
+
assert_equal true, hash.has_key?(:backtrace)
|
21
|
+
assert_equal true, hash.has_key?(:context)
|
22
|
+
assert_equal level, hash[:context][:level]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "sends #{level} exceptions" do
|
26
|
+
error = RuntimeError.new('Oh no, Error.')
|
27
|
+
exception = hash = nil
|
28
|
+
Honeybadger.stub(:notify, -> exc, h { exception = exc; hash = h }) do
|
29
|
+
@appender.send(level, @message, error)
|
30
|
+
end
|
31
|
+
|
32
|
+
assert_equal error.class.to_s, exception.class.to_s
|
33
|
+
assert_equal error.message, exception.message
|
34
|
+
assert_equal @message, hash[:message], hash
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -4,45 +4,76 @@ require_relative '../test_helper'
|
|
4
4
|
#
|
5
5
|
module Appender
|
6
6
|
class SplunkTest < Minitest::Test
|
7
|
-
describe SemanticLogger::Appender::Splunk do
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
class Mock
|
9
|
+
attr_accessor :message, :event
|
10
|
+
|
11
|
+
def submit(message, event)
|
12
|
+
self.message = message
|
13
|
+
self.event = event
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe SemanticLogger::Appender::Splunk do
|
18
|
+
before do
|
19
|
+
SemanticLogger::Appender::Splunk.stub_any_instance(:reopen, nil) do
|
20
|
+
@appender = SemanticLogger::Appender::Splunk.new(level: :info)
|
21
|
+
end
|
22
|
+
@message = 'AppenderSplunkTest log message'
|
23
|
+
end
|
15
24
|
|
16
|
-
|
17
|
-
|
25
|
+
it 'not send :trace notifications to Splunk when set to :error' do
|
26
|
+
mock = Mock.new
|
27
|
+
@appender.stub(:service_index, mock) do
|
28
|
+
@appender.trace('AppenderSplunkTest trace message')
|
29
|
+
end
|
30
|
+
assert_nil mock.event
|
31
|
+
assert_nil mock.message
|
32
|
+
end
|
18
33
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
34
|
+
it 'send exception notifications to Splunk with severity' do
|
35
|
+
hash = nil
|
36
|
+
exc = nil
|
37
|
+
begin
|
38
|
+
Uh oh
|
39
|
+
rescue Exception => e
|
40
|
+
exc = e
|
41
|
+
end
|
42
|
+
mock = Mock.new
|
43
|
+
@appender.stub(:service_index, mock) do
|
44
|
+
@appender.error 'Reading File', exc
|
45
|
+
end
|
46
|
+
assert_equal 'Reading File', mock.message
|
47
|
+
hash = mock.event
|
48
|
+
refute hash[:message]
|
49
|
+
assert 'NameError', hash[:exception][:name]
|
50
|
+
assert 'undefined local variable or method', hash[:exception][:message]
|
51
|
+
assert_equal 4, hash[:level_index], 'Should be error level (4)'
|
52
|
+
assert_equal :error, hash[:level]
|
53
|
+
assert hash[:exception][:stack_trace].first.include?(__FILE__), hash[:exception]
|
54
|
+
end
|
23
55
|
|
24
|
-
|
25
|
-
|
56
|
+
it 'send error notifications to Splunk with severity' do
|
57
|
+
mock = Mock.new
|
58
|
+
@appender.stub(:service_index, mock) do
|
59
|
+
@appender.error @message
|
26
60
|
end
|
61
|
+
assert_equal @message, mock.message
|
62
|
+
assert_equal :error, mock.event[:level]
|
63
|
+
refute mock.event[:stack_trace]
|
64
|
+
end
|
27
65
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
SemanticLogger::Appender::Splunk.new(username: 'username', password: 'password')
|
33
|
-
end
|
34
|
-
end
|
35
|
-
config = appender.config
|
36
|
-
# Default host
|
37
|
-
assert_equal 'localhost', config[:host]
|
38
|
-
# Default port
|
39
|
-
assert_equal 8089, config[:port]
|
40
|
-
# Default scheme
|
41
|
-
assert_equal :https, config[:scheme]
|
42
|
-
#Default index
|
43
|
-
assert_equal 'main', appender.index
|
44
|
-
end
|
66
|
+
it 'send notification to Splunk with custom attributes' do
|
67
|
+
mock = Mock.new
|
68
|
+
@appender.stub(:service_index, mock) do
|
69
|
+
@appender.error @message, {key1: 1, key2: 'a'}
|
45
70
|
end
|
71
|
+
assert_equal @message, mock.message
|
72
|
+
hash = mock.event
|
73
|
+
assert_equal :error, hash[:level]
|
74
|
+
refute hash[:stack_trace]
|
75
|
+
assert_equal(1, hash[:key1], hash)
|
76
|
+
assert_equal('a', hash[:key2], hash)
|
46
77
|
end
|
47
78
|
|
48
79
|
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: 3.
|
4
|
+
version: 3.2.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: 2016-02
|
11
|
+
date: 2016-03-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -42,6 +42,7 @@ files:
|
|
42
42
|
- lib/semantic_logger/appender/elasticsearch.rb
|
43
43
|
- lib/semantic_logger/appender/file.rb
|
44
44
|
- lib/semantic_logger/appender/graylog.rb
|
45
|
+
- lib/semantic_logger/appender/honeybadger.rb
|
45
46
|
- lib/semantic_logger/appender/http.rb
|
46
47
|
- lib/semantic_logger/appender/mongodb.rb
|
47
48
|
- lib/semantic_logger/appender/new_relic.rb
|
@@ -68,6 +69,7 @@ files:
|
|
68
69
|
- test/appender/elasticsearch_test.rb
|
69
70
|
- test/appender/file_test.rb
|
70
71
|
- test/appender/graylog_test.rb
|
72
|
+
- test/appender/honeybadger_test.rb
|
71
73
|
- test/appender/http_test.rb
|
72
74
|
- test/appender/mongodb_test.rb
|
73
75
|
- test/appender/new_relic_test.rb
|
@@ -111,6 +113,7 @@ test_files:
|
|
111
113
|
- test/appender/elasticsearch_test.rb
|
112
114
|
- test/appender/file_test.rb
|
113
115
|
- test/appender/graylog_test.rb
|
116
|
+
- test/appender/honeybadger_test.rb
|
114
117
|
- test/appender/http_test.rb
|
115
118
|
- test/appender/mongodb_test.rb
|
116
119
|
- test/appender/new_relic_test.rb
|