semantic_logger 3.4.1 → 4.0.0.beta1

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