semantic_logger 3.4.1 → 4.0.0.beta1

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -0
  3. data/Rakefile +4 -8
  4. data/lib/semantic_logger.rb +2 -31
  5. data/lib/semantic_logger/appender.rb +76 -0
  6. data/lib/semantic_logger/appender/bugsnag.rb +3 -8
  7. data/lib/semantic_logger/appender/file.rb +1 -1
  8. data/lib/semantic_logger/appender/honeybadger.rb +1 -1
  9. data/lib/semantic_logger/appender/http.rb +1 -1
  10. data/lib/semantic_logger/appender/mongodb.rb +30 -28
  11. data/lib/semantic_logger/appender/sentry.rb +2 -2
  12. data/lib/semantic_logger/appender/splunk_http.rb +4 -4
  13. data/lib/semantic_logger/appender/syslog.rb +2 -2
  14. data/lib/semantic_logger/appender/tcp.rb +9 -5
  15. data/lib/semantic_logger/appender/udp.rb +1 -0
  16. data/lib/semantic_logger/base.rb +73 -140
  17. data/lib/semantic_logger/core_ext/thread.rb +4 -1
  18. data/lib/semantic_logger/formatters/color.rb +7 -0
  19. data/lib/semantic_logger/formatters/default.rb +7 -0
  20. data/lib/semantic_logger/formatters/syslog.rb +1 -1
  21. data/lib/semantic_logger/log.rb +115 -12
  22. data/lib/semantic_logger/logger.rb +6 -215
  23. data/lib/semantic_logger/metrics/new_relic.rb +1 -1
  24. data/lib/semantic_logger/metrics/statsd.rb +5 -1
  25. data/lib/semantic_logger/metrics/udp.rb +80 -0
  26. data/lib/semantic_logger/processor.rb +235 -0
  27. data/lib/semantic_logger/semantic_logger.rb +36 -65
  28. data/lib/semantic_logger/subscriber.rb +2 -2
  29. data/lib/semantic_logger/version.rb +1 -1
  30. data/test/appender/bugsnag_test.rb +10 -9
  31. data/test/appender/elasticsearch_test.rb +3 -2
  32. data/test/appender/graylog_test.rb +4 -3
  33. data/test/appender/honeybadger_test.rb +2 -2
  34. data/test/appender/http_test.rb +3 -2
  35. data/test/appender/mongodb_test.rb +24 -23
  36. data/test/appender/new_relic_test.rb +15 -8
  37. data/test/appender/sentry_test.rb +2 -2
  38. data/test/appender/splunk_http_test.rb +8 -7
  39. data/test/appender/splunk_test.rb +6 -5
  40. data/test/appender/tcp_test.rb +3 -4
  41. data/test/appender/udp_test.rb +4 -5
  42. data/test/appender/wrapper_test.rb +37 -38
  43. data/test/concerns/compatibility_test.rb +2 -2
  44. data/test/loggable_test.rb +1 -1
  45. data/test/logger_test.rb +149 -528
  46. data/test/measure_test.rb +249 -0
  47. data/test/semantic_logger_test.rb +257 -0
  48. metadata +24 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b1be5c6c00e649ac6ae78e3e9e29737aa615e6ae
4
- data.tar.gz: db7f64e434f93a22cc8f2ae66fe1e0b7d79d9cdc
3
+ metadata.gz: 0753c0265adc4668e64539c5014d6ea98a9f5659
4
+ data.tar.gz: 4af0ca018bd158bccc8506985c84f8e29dad6d1f
5
5
  SHA512:
6
- metadata.gz: 9dc57b3e2722e32478cc33c514cb1ce9c6e00b564da8dcfcae5731fbb9b704c8e05f8baf59d415294555f1c27f934c1823c7dc220ffabc5345afc656f6d1c30a
7
- data.tar.gz: aca9d4715197ff06ce760a1725b199447dd9fabff90df4c6aded226b6cd1ce5913482c0f7d53c19647e1b65167f86b814f1a067cf0369c96eb4817c6a5361559
6
+ metadata.gz: eba4d620e6930c556912f8fc089326d3dbfca5bc8224e2949487509c70a37f332580ce96c78d06ae1249a15169930395a1697a012c687b2bbcf6aa58ce4d6138
7
+ data.tar.gz: 0206b4fedff5b5850d9f8541588263643f8a21a7c732a3845a12c6f4f94272ddc2042a613159fe8030e9f3855740d7461dc10885b24cbf2bd8071ce613361651
data/README.md CHANGED
@@ -50,6 +50,16 @@ and are therefore not automatically included by this gem:
50
50
  - Syslog Appender to a remote syslogng server over TCP or UDP: gem 'net_tcp_client'
51
51
  - Splunk Appender: gem 'splunk-sdk-ruby'
52
52
 
53
+ ## V4 Upgrade notes
54
+
55
+ The following changes need to be made when upgrading to V4:
56
+ - Ruby V2.1 / JRuby V9.1 is now the minimum runtime version.
57
+ - Replace calls to Logger#with_payload with SemanticLogger.named_tagged.
58
+ - Replace calls to Logger#payload with SemanticLogger.named_tags.
59
+ - Appenders now write payload data in a seperate :payload tag instead of mixing them.
60
+ directly into the root elements to avoid name clashes.
61
+ - MongoDB Appender requires Mongo Ruby Client V2 or greater.
62
+
53
63
  ## Install
54
64
 
55
65
  gem install semantic_logger
data/Rakefile CHANGED
@@ -15,14 +15,10 @@ task publish: :gem do
15
15
  system "rm semantic_logger-#{SemanticLogger::VERSION}.gem"
16
16
  end
17
17
 
18
- desc 'Run Test Suite'
19
- task :test do
20
- Rake::TestTask.new(:functional) do |t|
21
- t.test_files = FileList['test/**/*_test.rb']
22
- t.verbose = true
23
- end
24
-
25
- Rake::Task['functional'].invoke
18
+ Rake::TestTask.new(:test) do |t|
19
+ t.pattern = 'test/**/*_test.rb'
20
+ t.verbose = true
21
+ t.warning = false
26
22
  end
27
23
 
28
24
  task default: :test
@@ -5,44 +5,15 @@ require 'semantic_logger/semantic_logger'
5
5
  # @formatter:off
6
6
  module SemanticLogger
7
7
  autoload :AnsiColors, 'semantic_logger/ansi_colors'
8
+ autoload :Appender, 'semantic_logger/appender'
8
9
  autoload :Base, 'semantic_logger/base'
9
10
  autoload :DebugAsTraceLogger, 'semantic_logger/debug_as_trace_logger'
10
11
  autoload :Log, 'semantic_logger/log'
11
12
  autoload :Logger, 'semantic_logger/logger'
12
13
  autoload :Loggable, 'semantic_logger/loggable'
14
+ autoload :Processor, 'semantic_logger/processor'
13
15
  autoload :Subscriber, 'semantic_logger/subscriber'
14
16
 
15
- module Appender
16
- # DEPRECATED, use SemanticLogger::AnsiColors
17
- AnsiColors = SemanticLogger::AnsiColors
18
-
19
- # DEPRECATED: use SemanticLogger::Formatters::Color.new
20
- def self.colorized_formatter
21
- SemanticLogger::Formatters::Color.new
22
- end
23
-
24
- # DEPRECATED: use SemanticLogger::Formatters::Json.new
25
- def self.json_formatter
26
- SemanticLogger::Formatters::Json.new
27
- end
28
-
29
- autoload :Bugsnag, 'semantic_logger/appender/bugsnag'
30
- autoload :Elasticsearch, 'semantic_logger/appender/elasticsearch'
31
- autoload :File, 'semantic_logger/appender/file'
32
- autoload :Graylog, 'semantic_logger/appender/graylog'
33
- autoload :Honeybadger, 'semantic_logger/appender/honeybadger'
34
- autoload :Sentry, 'semantic_logger/appender/sentry'
35
- autoload :Http, 'semantic_logger/appender/http'
36
- autoload :MongoDB, 'semantic_logger/appender/mongodb'
37
- autoload :NewRelic, 'semantic_logger/appender/new_relic'
38
- autoload :Splunk, 'semantic_logger/appender/splunk'
39
- autoload :SplunkHttp, 'semantic_logger/appender/splunk_http'
40
- autoload :Syslog, 'semantic_logger/appender/syslog'
41
- autoload :Tcp, 'semantic_logger/appender/tcp'
42
- autoload :Udp, 'semantic_logger/appender/udp'
43
- autoload :Wrapper, 'semantic_logger/appender/wrapper'
44
- end
45
-
46
17
  module Concerns
47
18
  autoload :Compatibility, 'semantic_logger/concerns/compatibility'
48
19
  end
@@ -0,0 +1,76 @@
1
+ module SemanticLogger
2
+ module Appender
3
+ # @formatter:off
4
+ autoload :Bugsnag, 'semantic_logger/appender/bugsnag'
5
+ autoload :Elasticsearch, 'semantic_logger/appender/elasticsearch'
6
+ autoload :File, 'semantic_logger/appender/file'
7
+ autoload :Graylog, 'semantic_logger/appender/graylog'
8
+ autoload :Honeybadger, 'semantic_logger/appender/honeybadger'
9
+ autoload :Sentry, 'semantic_logger/appender/sentry'
10
+ autoload :Http, 'semantic_logger/appender/http'
11
+ autoload :MongoDB, 'semantic_logger/appender/mongodb'
12
+ autoload :NewRelic, 'semantic_logger/appender/new_relic'
13
+ autoload :Splunk, 'semantic_logger/appender/splunk'
14
+ autoload :SplunkHttp, 'semantic_logger/appender/splunk_http'
15
+ autoload :Syslog, 'semantic_logger/appender/syslog'
16
+ autoload :Tcp, 'semantic_logger/appender/tcp'
17
+ autoload :Udp, 'semantic_logger/appender/udp'
18
+ autoload :Wrapper, 'semantic_logger/appender/wrapper'
19
+ # @formatter:on
20
+
21
+ # DEPRECATED, use SemanticLogger::AnsiColors
22
+ AnsiColors = SemanticLogger::AnsiColors
23
+
24
+ # DEPRECATED: use SemanticLogger::Formatters::Color.new
25
+ def self.colorized_formatter
26
+ SemanticLogger::Formatters::Color.new
27
+ end
28
+
29
+ # DEPRECATED: use SemanticLogger::Formatters::Json.new
30
+ def self.json_formatter
31
+ SemanticLogger::Formatters::Json.new
32
+ end
33
+
34
+ # Returns [SemanticLogger::Subscriber] appender for the supplied options
35
+ def self.create(options, &block)
36
+ if options[:io] || options[:file_name]
37
+ SemanticLogger::Appender::File.new(options, &block)
38
+ elsif appender = options.delete(:appender)
39
+ if appender.is_a?(Symbol)
40
+ constantize_symbol(appender).new(options)
41
+ elsif appender.is_a?(Subscriber)
42
+ appender
43
+ else
44
+ raise(ArgumentError, "Parameter :appender must be either a Symbol or an object derived from SemanticLogger::Subscriber, not: #{appender.inspect}")
45
+ end
46
+ elsif options[:logger]
47
+ SemanticLogger::Appender::Wrapper.new(options, &block)
48
+ end
49
+ end
50
+
51
+ def self.constantize_symbol(symbol, namespace = 'SemanticLogger::Appender')
52
+ klass = "#{namespace}::#{camelize(symbol.to_s)}"
53
+ begin
54
+ if RUBY_VERSION.to_i >= 2
55
+ Object.const_get(klass)
56
+ else
57
+ klass.split('::').inject(Object) { |o, name| o.const_get(name) }
58
+ end
59
+ rescue NameError
60
+ raise(ArgumentError, "Could not convert symbol: #{symbol} to a class in: #{namespace}. Looking for: #{klass}")
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ # Borrow from Rails, when not running Rails
67
+ def self.camelize(term)
68
+ string = term.to_s
69
+ string = string.sub(/^[a-z\d]*/) { |match| match.capitalize }
70
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
71
+ string.gsub!('/'.freeze, '::'.freeze)
72
+ string
73
+ end
74
+
75
+ end
76
+ end
@@ -27,18 +27,13 @@ class SemanticLogger::Appender::Bugsnag < SemanticLogger::Subscriber
27
27
  # regular expression. All other messages will be ignored.
28
28
  # Proc: Only include log messages where the supplied Proc returns true
29
29
  # The Proc must return true or false.
30
- def initialize(options = {}, &block)
31
- # Backward compatibility
32
- options = {level: options} unless options.is_a?(Hash)
33
- options = options.dup
34
- options[:level] = :error unless options.has_key?(:level)
35
-
36
- raise 'Bugsnag only supports :info, :warn, or :error log levels' unless [:info, :warn, :error].include?(options[:level])
30
+ def initialize(level: :error, formatter: nil, filter: nil, host: SemanticLogger.host, application: SemanticLogger.application, &block)
31
+ raise 'Bugsnag only supports :info, :warn, or :error log levels' unless [:info, :warn, :error].include?(level)
37
32
 
38
33
  # Replace the Bugsnag logger so that we can identify its log messages and not forward them to Bugsnag
39
34
  Bugsnag.configure { |config| config.logger = SemanticLogger[Bugsnag] }
40
35
 
41
- super(options, &block)
36
+ super(level: level, formatter: formatter, filter: filter, host: host, application: application, &block)
42
37
  end
43
38
 
44
39
  # Returns [Hash] of parameters to send to Bugsnag.
@@ -38,7 +38,7 @@ module SemanticLogger
38
38
  # SemanticLogger.add_appender(io: STDOUT, formatter: :color)
39
39
  #
40
40
  # # And log to a file at the same time
41
- # SemanticLogger::Logger.add_appender(file_name: 'application.log', formatter: :color)
41
+ # SemanticLogger.add_appender(file_name: 'application.log', formatter: :color)
42
42
  #
43
43
  # logger = SemanticLogger['test']
44
44
  # logger.info 'Hello World'
@@ -50,7 +50,7 @@ class SemanticLogger::Appender::Honeybadger < SemanticLogger::Subscriber
50
50
  context.delete(:exception)
51
51
  Honeybadger.notify(log.exception, context)
52
52
  else
53
- message = {
53
+ message = {
54
54
  error_class: context.delete(:name),
55
55
  error_message: context.delete(:message),
56
56
  context: context
@@ -199,7 +199,7 @@ class SemanticLogger::Appender::Http < SemanticLogger::Subscriber
199
199
  true
200
200
  else
201
201
  # Failures are logged to the global semantic logger failsafe logger (Usually stderr or file)
202
- SemanticLogger::Logger.logger.error("Bad HTTP response from: #{url} code: #{response.code}, #{response.body}")
202
+ SemanticLogger::Processor.logger.error("Bad HTTP response from: #{url} code: #{response.code}, #{response.body}")
203
203
  false
204
204
  end
205
205
  end
@@ -2,7 +2,7 @@ require 'socket'
2
2
  begin
3
3
  require 'mongo'
4
4
  rescue LoadError
5
- raise 'Gem mongo is required for logging to MongoDB. Please add the gem "mongo" to your Gemfile.'
5
+ raise 'Gem mongo is required for logging to MongoDB. Please add the gem "mongo" v2.0 or greater to your Gemfile.'
6
6
  end
7
7
 
8
8
  module SemanticLogger
@@ -49,14 +49,15 @@ module SemanticLogger
49
49
  # # Log some messages
50
50
  # logger.info 'This message is written to mongo as a document'
51
51
  class MongoDB < SemanticLogger::Subscriber
52
- attr_reader :db, :collection_name, :collection
53
- attr_accessor :write_concern
52
+ attr_reader :client, :collection
54
53
 
55
54
  # Create a MongoDB Appender instance
56
55
  #
57
56
  # Parameters:
58
- # db: [Mongo::Database]
59
- # The MongoDB database connection to use, not the database name
57
+ # uri: [String]
58
+ # Mongo connection string.
59
+ # Example:
60
+ # mongodb://127.0.0.1:27017/test
60
61
  #
61
62
  # collection_name: [String]
62
63
  # Name of the collection to store log data in
@@ -78,6 +79,7 @@ module SemanticLogger
78
79
  #
79
80
  # collection_max: [Integer]
80
81
  # Maximum number of log entries that the capped collection will hold.
82
+ # Default: no max limit
81
83
  #
82
84
  # level: [:trace | :debug | :info | :warn | :error | :fatal]
83
85
  # Override the log level for this appender.
@@ -101,28 +103,30 @@ module SemanticLogger
101
103
  # application: [String]
102
104
  # Name of this application to appear in log messages.
103
105
  # Default: SemanticLogger.application
104
- def initialize(options = {}, &block)
105
- options = options.dup
106
- @db = options.delete(:db) || raise('Missing mandatory parameter :db')
107
- @collection_name = options.delete(:collection_name) || 'semantic_logger'
108
- @write_concern = options.delete(:write_concern) || 0
106
+ def initialize(uri:, collection_name: 'semantic_logger', write_concern: 0, collection_size: 1024**3, collection_max: nil,
107
+ level: nil, formatter: nil, filter: nil, host: SemanticLogger.host, application: SemanticLogger.application, &block)
108
+ @client = Mongo::Client.new(uri, logger: SemanticLogger::Processor.logger.clone)
109
+ @collection_name = collection_name
110
+ @options = {
111
+ capped: true,
112
+ size: collection_size,
113
+ write: {w: write_concern}
114
+ }
115
+ @options[:max] = collection_max if collection_max
109
116
 
110
- # Create a collection that will hold the lesser of 1GB space or 10K documents
111
- @collection_size = options.delete(:collection_size) || 1024**3
112
- @collection_max = options.delete(:collection_max)
117
+ reopen
113
118
 
114
119
  # Create the collection and necessary indexes
115
120
  create_indexes
116
121
 
117
122
  # Set the log level and formatter
118
- super(options, &block)
119
- reopen
123
+ super(level: level, formatter: formatter, filter: filter, host: host, application: application, &block)
120
124
  end
121
125
 
122
126
  # After forking an active process call #reopen to re-open
123
127
  # open the handles to resources
124
128
  def reopen
125
- @collection = db[@collection_name]
129
+ @collection = client[@collection_name, @options]
126
130
  end
127
131
 
128
132
  # Create the required capped collection.
@@ -136,10 +140,14 @@ module SemanticLogger
136
140
  #
137
141
  # Creates an index based on tags to support faster searches.
138
142
  def create_indexes
139
- options = {capped: true, size: @collection_size}
140
- options[:max] = @collection_max if @collection_max
141
- db.create_collection(collection_name, options)
142
- db[@collection_name].ensure_index('tags')
143
+ # Create Capped collection
144
+ begin
145
+ @collection.create
146
+ rescue Mongo::Error::OperationFailure
147
+ # Already exists
148
+ end
149
+
150
+ @collection.indexes.create_one({tags: 1})
143
151
  end
144
152
 
145
153
  # Purge all data from the capped collection by dropping the collection
@@ -147,22 +155,16 @@ module SemanticLogger
147
155
  # Also useful when the size of the capped collection needs to be changed
148
156
  def purge_all
149
157
  collection.drop
150
- @collection = nil
158
+ reopen
151
159
  create_indexes
152
160
  end
153
161
 
154
- # Flush all pending logs to disk.
155
- # Waits for all sent documents to be written to disk
156
- def flush
157
- db.get_last_error
158
- end
159
-
160
162
  # Log the message to MongoDB
161
163
  def log(log)
162
164
  return false unless should_log?(log)
163
165
 
164
166
  # Insert log entry into Mongo
165
- collection.insert(formatter.call(log, self), w: @write_concern)
167
+ collection.insert_one(formatter.call(log, self))
166
168
  true
167
169
  end
168
170
 
@@ -51,10 +51,10 @@ class SemanticLogger::Appender::Sentry < SemanticLogger::Subscriber
51
51
  context.delete(:exception)
52
52
  Raven.capture_exception(log.exception, context)
53
53
  else
54
- message = {
54
+ message = {
55
55
  error_class: context.delete(:name),
56
56
  error_message: context.delete(:message),
57
- extra: context
57
+ extra: context
58
58
  }
59
59
  message[:backtrace] = log.backtrace if log.backtrace
60
60
  Raven.capture_message(message[:error_message], message)
@@ -65,10 +65,10 @@ class SemanticLogger::Appender::SplunkHttp < SemanticLogger::Appender::Http
65
65
  # Proc: Only include log messages where the supplied Proc returns true
66
66
  # The Proc must return true or false.
67
67
  def initialize(options, &block)
68
- options = options.dup
69
- @source_type = options.delete(:source_type)
70
- @index = options.delete(:index)
71
- token = options.delete(:token)
68
+ options = options.dup
69
+ @source_type = options.delete(:source_type)
70
+ @index = options.delete(:index)
71
+ token = options.delete(:token)
72
72
  raise(ArgumentError, 'Missing mandatory parameter :token') unless token
73
73
 
74
74
  # Splunk supports HTTP Compression, enable by default
@@ -66,7 +66,7 @@ module SemanticLogger
66
66
  # Only used with the TCP protocol.
67
67
  # Specify custom parameters to pass into Net::TCPClient.new
68
68
  # For a list of options see the net_tcp_client documentation:
69
- # https://www.omniref.com/ruby/gems/net_tcp_client/1.0.0/symbols/Net::TCPClient/initialize
69
+ # https://github.com/rocketjob/net_tcp_client/blob/master/lib/net/tcp_client/tcp_client.rb
70
70
  #
71
71
  # level: [:trace | :debug | :info | :warn | :error | :fatal]
72
72
  # Override the log level for this appender.
@@ -196,7 +196,7 @@ module SemanticLogger
196
196
  ::Syslog.open(application, @options, @facility)
197
197
  when :tcp
198
198
  # Use the local logger for @remote_syslog so errors with the remote logger can be recorded locally.
199
- @tcp_client_options[:logger] = SemanticLogger::Logger.logger
199
+ @tcp_client_options[:logger] = SemanticLogger::Processor.logger.clone
200
200
  @remote_syslog = Net::TCPClient.new(@tcp_client_options)
201
201
  when :udp
202
202
  @remote_syslog = UDPSocket.new
@@ -182,12 +182,13 @@ module SemanticLogger
182
182
  # connect_retry_count: 5
183
183
  # )
184
184
  def initialize(options = {}, &block)
185
- @options = options.dup
186
- @separator = @options.delete(:separator) || "\n"
185
+ @tcp_client = nil
186
+ @options = options.dup
187
+ @separator = @options.delete(:separator) || "\n"
187
188
 
188
189
  # Use the internal logger so that errors with remote logging are only written locally.
189
- Net::TCPClient.logger = SemanticLogger::Logger.logger.dup
190
- Net::TCPClient.logger.name = 'Net::TCPClient'
190
+ Net::TCPClient.logger = SemanticLogger::Processor.logger.clone
191
+ Net::TCPClient.logger.name = 'Net::TCPClient'
191
192
 
192
193
  options = extract_subscriber_options!(@options)
193
194
  super(options, &block)
@@ -205,7 +206,10 @@ module SemanticLogger
205
206
  def log(log)
206
207
  return false unless should_log?(log)
207
208
 
208
- @tcp_client.retry_on_connection_failure { @tcp_client.write("#{formatter.call(log, self)}#{separator}") }
209
+ message = formatter.call(log, self)
210
+ @tcp_client.retry_on_connection_failure do
211
+ @tcp_client.write("#{message}#{separator}")
212
+ end
209
213
  true
210
214
  end
211
215
 
@@ -58,6 +58,7 @@ module SemanticLogger
58
58
  # server: 'server:3300'
59
59
  # )
60
60
  def initialize(options = {}, &block)
61
+ @socket = nil
61
62
  options = options.dup
62
63
  @server = options.delete(:server)
63
64
  @udp_flags = options.delete(:udp_flags) || 0
@@ -4,8 +4,6 @@
4
4
  #
5
5
  # Implements common behavior such as log level, default text formatter etc
6
6
  #
7
- # Note: Do not create instances of this class directly
8
- #
9
7
  module SemanticLogger
10
8
  class Base
11
9
  # Class name to be logged
@@ -116,8 +114,33 @@ module SemanticLogger
116
114
  end
117
115
  end
118
116
 
117
+ # Backward compatibility
119
118
  alias_method :benchmark, :measure
120
119
 
120
+ # Log a thread backtrace
121
+ def backtrace(thread: Thread.current, level: :warn, message: 'Backtrace:', payload: nil, metric: nil, metric_amount: 1)
122
+ log = Log.new(name, level)
123
+ backtrace =
124
+ if thread == Thread.current
125
+ self.class.cleanse_backtrace
126
+ else
127
+ log.thread_name = thread.name
128
+ log.tags = thread[:semantic_logger_tags].clone
129
+ log.named_tags = thread[:semantic_logger_named_tags].clone
130
+ thread.backtrace
131
+ end
132
+ # TODO: Keep backtrace instead of transforming into a text message at this point
133
+ # Maybe log_backtrace: true
134
+ if backtrace
135
+ message += "\n"
136
+ message << backtrace.join("\n")
137
+ end
138
+
139
+ if log.assign(message: message, backtrace: backtrace, payload: payload, metric: metric, metric_amount: metric_amount) && should_log?(log)
140
+ self.log(log)
141
+ end
142
+ end
143
+
121
144
  # :nodoc:
122
145
  def tagged(*tags, &block)
123
146
  SemanticLogger.tagged(*tags, &block)
@@ -151,33 +174,16 @@ module SemanticLogger
151
174
  SemanticLogger.fast_tag(tag, &block)
152
175
  end
153
176
 
154
- # Thread specific context information to be logged with every log entry
155
- #
156
- # Add a payload to all log calls on This Thread within the supplied block
157
- #
158
- # logger.with_payload(tracking_number: 12345) do
159
- # logger.debug('Hello World')
160
- # end
161
- #
162
- # If a log call already includes a pyload, this payload will be merged with
163
- # the supplied payload, with the supplied payload taking precedence
164
- #
165
- # logger.with_payload(tracking_number: 12345) do
166
- # logger.debug('Hello World', result: 'blah')
167
- # end
177
+ # :nodoc:
168
178
  def with_payload(payload)
169
- current_payload = self.payload
170
- Thread.current[:semantic_logger_payload] = current_payload ? current_payload.merge(payload) : payload
171
- yield
172
- ensure
173
- Thread.current[:semantic_logger_payload] = current_payload
179
+ warn '#with_payload is deprecated, use SemanticLogger.named_tagged'
180
+ SemanticLogger.named_tagged(payload)
174
181
  end
175
182
 
176
- # Returns [Hash] payload to be added to every log entry in the current scope
177
- # on this thread.
178
- # Returns nil if no payload is currently set
183
+ # :nodoc:
179
184
  def payload
180
- Thread.current[:semantic_logger_payload]
185
+ warn '#payload is deprecated, use SemanticLogger.named_tags'
186
+ SemanticLogger.named_tags
181
187
  end
182
188
 
183
189
  protected
@@ -206,9 +212,8 @@ module SemanticLogger
206
212
  # regular expression. All other messages will be ignored
207
213
  # Proc: Only include log messages where the supplied Proc returns true
208
214
  # The Proc must return true or false
209
- def initialize(klass, level=nil, filter=nil)
210
- # Support filtering all messages to this logger using a Regular Expression
211
- # or Proc
215
+ def initialize(klass, level = nil, filter = nil)
216
+ # Support filtering all messages to this logger using a Regular Expression or Proc
212
217
  raise ':filter must be a Regexp or Proc' unless filter.nil? || filter.is_a?(Regexp) || filter.is_a?(Proc)
213
218
 
214
219
  @filter = filter.is_a?(Regexp) ? filter.freeze : filter
@@ -247,78 +252,31 @@ module SemanticLogger
247
252
  end
248
253
 
249
254
  # Log message at the specified level
250
- def log_internal(level, index, message=nil, payload=nil, exception=nil)
251
- # Exception being logged?
252
- # Under JRuby a java exception is not a Ruby Exception
253
- # Java::JavaLang::ClassCastException.new.is_a?(Exception) => false
254
- if exception.nil? && payload.nil? && message.respond_to?(:backtrace) && message.respond_to?(:message)
255
- exception = message
256
- message = nil
257
- elsif exception.nil? && payload && payload.respond_to?(:backtrace) && payload.respond_to?(:message)
258
- exception = payload
259
- payload = nil
260
- end
261
-
262
- # Add result of block as message or payload if not nil
263
- if block_given? && (result = yield)
264
- if result.is_a?(String)
265
- message = message.nil? ? result : "#{message} -- #{result}"
266
- elsif message.nil? && result.is_a?(Hash)
267
- message = result
268
- elsif payload && payload.respond_to?(:merge)
269
- payload.merge(result)
255
+ def log_internal(level, index, message = nil, payload = nil, exception = nil, &block)
256
+ log = Log.new(name, level, index)
257
+ should_log =
258
+ if payload.nil? && exception.nil? && message.is_a?(Hash)
259
+ log.assign(message)
270
260
  else
271
- payload = result
261
+ log.assign_positional(message, payload, exception, &block)
272
262
  end
273
- end
274
263
 
275
- # Add scoped payload
276
- if self.payload
277
- payload = payload.nil? ? self.payload : self.payload.merge(payload)
278
- end
279
-
280
- # Add caller stack trace
281
- backtrace = extract_backtrace if index >= SemanticLogger.backtrace_level_index
282
-
283
- log = Log.new(level, Thread.current.name, name, message, payload, Time.now, nil, tags, index, exception, nil, backtrace)
284
-
285
- # Logging Hash only?
286
- # logger.info(name: 'value')
287
- if payload.nil? && exception.nil? && message.is_a?(Hash)
288
- payload = message.dup
289
- min_duration = payload.delete(:min_duration) || 0.0
290
- log.exception = payload.delete(:exception)
291
- log.message = payload.delete(:message)
292
- log.metric = payload.delete(:metric)
293
- log.metric_amount = payload.delete(:metric_amount) || 1
294
- if duration = payload.delete(:duration)
295
- return false if duration <= min_duration
296
- log.duration = duration
297
- end
298
- log.payload = payload if payload.size > 0
299
- end
300
-
301
- self.log(log) if include_message?(log)
302
- end
303
-
304
- SELF_PATTERN = File.join('lib', 'semantic_logger')
305
-
306
- # Extract the callers backtrace leaving out Semantic Logger
307
- def extract_backtrace
308
- stack = caller
309
- while (first = stack.first) && first.include?(SELF_PATTERN)
310
- stack.shift
311
- end
312
- stack
264
+ self.log(log) if should_log && include_message?(log)
313
265
  end
314
266
 
315
267
  # Measure the supplied block and log the message
316
268
  def measure_internal(level, index, message, params)
317
- start = Time.now
318
269
  exception = nil
270
+ result = nil
271
+ # Single parameter is a hash
272
+ if params.empty? && message.is_a?(Hash)
273
+ params = message
274
+ message = nil
275
+ end
276
+ start = Time.now
319
277
  begin
320
278
  if block_given?
321
- result =
279
+ result =
322
280
  if silence_level = params[:silence]
323
281
  # In case someone accidentally sets `silence: true` instead of `silence: :error`
324
282
  silence_level = :error if silence_level == true
@@ -326,65 +284,40 @@ module SemanticLogger
326
284
  else
327
285
  yield(params)
328
286
  end
329
- exception = params[:exception]
330
- result
331
287
  end
332
288
  rescue Exception => exc
333
289
  exception = exc
334
290
  ensure
335
- end_time = Time.now
336
- # Extract options after block completes so that block can modify any of the options
337
- log_exception = params[:log_exception] || :partial
338
- on_exception_level = params[:on_exception_level]
339
- min_duration = params[:min_duration] || 0.0
340
- payload = params[:payload]
341
- metric = params[:metric]
342
- duration =
291
+ # Must use ensure block otherwise a `return` in the yield above will skip the log entry
292
+ log = Log.new(name, level, index)
293
+ exception ||= params[:exception]
294
+ message = params[:message] if params[:message]
295
+ duration =
343
296
  if block_given?
344
- 1000.0 * (end_time - start)
297
+ 1000.0 * (Time.now - start)
345
298
  else
346
299
  params[:duration] || raise('Mandatory block missing when :duration option is not supplied')
347
300
  end
348
301
 
349
- # Add scoped payload
350
- if self.payload
351
- payload = payload.nil? ? self.payload : self.payload.merge(payload)
352
- end
353
- if exception
354
- logged_exception = exception
355
- backtrace = nil
356
- case log_exception
357
- when :full
358
- # On exception change the log level
359
- if on_exception_level
360
- level = on_exception_level
361
- index = SemanticLogger.level_to_index(level)
362
- end
363
- when :partial
364
- # On exception change the log level
365
- if on_exception_level
366
- level = on_exception_level
367
- index = SemanticLogger.level_to_index(level)
368
- end
369
- message = "#{message} -- Exception: #{exception.class}: #{exception.message}"
370
- logged_exception = nil
371
- backtrace = exception.backtrace
372
- else
373
- # Log the message with its duration but leave out the exception that was raised
374
- logged_exception = nil
375
- backtrace = exception.backtrace
376
- end
377
- log = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, logged_exception, metric, backtrace)
378
- self.log(log) if include_message?(log)
379
- raise exception
380
- elsif duration >= min_duration
381
- # Only log if the block took longer than 'min_duration' to complete
382
- # Add caller stack trace
383
- backtrace = extract_backtrace if index >= SemanticLogger.backtrace_level_index
384
-
385
- log = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric, backtrace)
386
- self.log(log) if include_message?(log)
387
- end
302
+ # Extract options after block completes so that block can modify any of the options
303
+ payload = params[:payload]
304
+
305
+ should_log = log.assign(
306
+ message: message,
307
+ payload: payload,
308
+ min_duration: params[:min_duration] || 0.0,
309
+ exception: exception,
310
+ metric: params[:metric],
311
+ metric_amount: 1,
312
+ duration: duration,
313
+ backtrace: nil,
314
+ log_exception: params[:log_exception] || :partial,
315
+ on_exception_level: params[:on_exception_level]
316
+ )
317
+
318
+ self.log(log) if should_log && include_message?(log)
319
+ raise exception if exception
320
+ result
388
321
  end
389
322
  end
390
323