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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7010f9d28a473c9cd0c9fb9d52608216bad68043
4
- data.tar.gz: 052152f47b9eb211a26c47a2bbbff8742f497449
3
+ metadata.gz: efbdb066c354033b8f3e16d20855e5cd41a9ca14
4
+ data.tar.gz: cbbf43104fc6fc965f0aceb66c1aa39e83927779
5
5
  SHA512:
6
- metadata.gz: 17d21c7f8bc35b93e52074cfac26bb0c350c989b589a8fb4a096c43a71148b4e245ace66ef36c8af9693355dc3741623896afb48bc2a01473b062a3ee1b6111a
7
- data.tar.gz: 43eaa8a554e95b8936e4a595593977e30a98b849dcaa9d419762420a0f3edcd03d9f1f923cdeda2897b48332c1b5bcd89a9500f1df181d57283e56c11cc296ac
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(file: 'development.log', formatter: :color)
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)
@@ -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
- # Only log if level is warn, or error.
56
- return false if (level_index > (log.level_index || 0)) ||
57
- # Ignore logs coming from Bugsnag itself
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 if (level_index > (log.level_index || 0)) ||
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
- # Ensure minimum log level is met, and check filter
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 if (level_index > (log.level_index || 0)) ||
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 = options.dup
72
- @url = options.delete(:url)
73
- @ssl_options = options.delete(:ssl)
74
- @username = options.delete(:username)
75
- @password = options.delete(:password)
76
- @application = options.delete(:application) || 'Semantic Logger'
77
- @host = options.delete(:host) || SemanticLogger.host
78
- @compress = options.delete(:compress) || false
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 if (level_index > (log.level_index || 0)) ||
117
- !include_message?(log) # Filtered out?
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
- # Ensure minimum log level is met, and check filter
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
- # Ensure minimum log level is met, and check filter
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
- # Note: This appender is Deprecated. Use: SemanticLogger::Appender::SplunkHttp
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
- # DEPRECATED, Please use SemanticLogger::Appender::SplunkHttp
12
- def initialize(options, level=:error, &block)
13
- Kernel.warn('Splunk Appender is deprecated, please use SemanticLogger::Appender::SplunkHttp')
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
- # Parse input options for setting up splunk connection
16
- parse_options(options)
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(level: level, &block)
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
- @service = Splunk::connect(@config)
99
+ self.service = Splunk::connect(config)
29
100
 
30
101
  # The index we are logging to
31
- @service_index = @service.indexes[@index]
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
- # Ensure minimum log level is met, and check filter
37
- return false if (level_index > (log.level_index || 0)) || !include_message?(log)
38
- # Submit the log message
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
- private
44
-
45
- def parse_options(options)
46
- @config = {
47
- scheme: options[:scheme] || :https,
48
- host: options[:host] || 'localhost',
49
- port: options[:port] || 8089,
50
- username: options[:username],
51
- password: options[:password]
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
@@ -1,5 +1,5 @@
1
1
  require 'json'
2
- # Splunk log appender.
2
+ # Splunk log appender using the Splunk HTTP(S) listener.
3
3
  #
4
4
  # Use the newer, faster and more complete JSON over HTTP interface for Splunk.
5
5
  #
@@ -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
- # Ensure minimum log level is met, and check filter
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
- # Ensure minimum log level is met, and check filter
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
@@ -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(file: 'application.log', formatter: :color)
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
- # If the tag being supplied is definitely a string then this fast
122
- # tag api can be used for short lived tags
123
- def fast_tag(tag)
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
- # Previous method for supplying tags
126
+ # :nodoc:
142
127
  alias_method :with_tags, :tagged
143
128
 
144
- # Returns a copy of the [Array] of [String] tags currently active for this thread
145
- # Returns nil if no tags are set
129
+ # :nodoc:
146
130
  def tags
147
- # Since tags are stored on a per thread basis this list is thread-safe
148
- t = Thread.current[:semantic_logger_tags]
149
- t.nil? ? [] : t.clone
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
- # Add tags to the current scope
153
- # Returns the list of tags pushed after flattening them out and removing blanks
154
- def push_tags *tags
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
- # Remove specified number of tags from the current tag list
163
- def pop_tags(quantity=1)
164
- t = Thread.current[:semantic_logger_tags]
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
- # DEPRECATED See SemanticLogger.add_appender
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
- def self.on_metric(object = nil, &block)
111
- raise('When supplying an object, it must support the #call method') if object && !object.respond_to?(:call)
112
- (@@metric_subscribers ||= Concurrent::Array.new) << (object || block)
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? Log
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 'Custom/' prefix for NewRelic
7
- metric = "Custom/#{metric}" unless metric.start_with?('Custom')
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
- $statsd.timing(metric, duration)
35
+ @statsd.timing(metric, duration)
36
36
  else
37
37
  amount = (log.metric_amount || 1).round
38
38
  if amount < 0
39
- amount.times { $statsd.decrement(metric) }
39
+ amount.times { @statsd.decrement(metric) }
40
40
  else
41
- amount.times { $statsd.increment(metric) }
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
- def self.on_metric(object = nil, &block)
203
- SemanticLogger::Logger.on_metric(object, &block)
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 = "SemanticLogger::Appender::#{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 appender class: #{klass} for #{appender}")
465
+ raise(ArgumentError, "Could not find #{root} class: #{klass} for #{appender}")
356
466
  end
357
467
  end
358
468
 
@@ -1,3 +1,3 @@
1
1
  module SemanticLogger #:nodoc
2
- VERSION = '3.1.0'
2
+ VERSION = '3.2.0'
3
3
  end
@@ -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
- describe '#parse_options' do
10
- describe 'argument errors' do
11
- it 'raise argument error for missing username' do
12
- error = assert_raises ArgumentError do
13
- SemanticLogger::Appender::Splunk.new({})
14
- end
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
- assert_equal 'Must supply a username.', error.message
17
- end
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
- it 'raise argument error for missing password' do
20
- error = assert_raises ArgumentError do
21
- SemanticLogger::Appender::Splunk.new(username: 'username')
22
- end
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
- assert_equal 'Must supply a password.', error.message
25
- end
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
- describe 'set default values' do
29
- it 'have default values' do
30
- appender = Splunk.stub(:connect, Splunk::Service.new({})) do
31
- Splunk::Service.stub_any_instance(:indexes, {}) do
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.1.0
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-27 00:00:00.000000000 Z
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