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 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