semantic_logger 3.1.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|