semantic_logger 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +55 -8
  3. data/lib/semantic_logger.rb +1 -2
  4. data/lib/semantic_logger/ansi_colors.rb +1 -2
  5. data/lib/semantic_logger/appender.rb +17 -15
  6. data/lib/semantic_logger/appender/bugsnag.rb +5 -4
  7. data/lib/semantic_logger/appender/elasticsearch.rb +102 -16
  8. data/lib/semantic_logger/appender/elasticsearch_http.rb +76 -0
  9. data/lib/semantic_logger/appender/file.rb +9 -25
  10. data/lib/semantic_logger/appender/graylog.rb +43 -38
  11. data/lib/semantic_logger/appender/honeybadger.rb +3 -5
  12. data/lib/semantic_logger/appender/http.rb +12 -15
  13. data/lib/semantic_logger/appender/kafka.rb +183 -0
  14. data/lib/semantic_logger/appender/mongodb.rb +3 -3
  15. data/lib/semantic_logger/appender/new_relic.rb +3 -7
  16. data/lib/semantic_logger/appender/sentry.rb +2 -5
  17. data/lib/semantic_logger/appender/splunk.rb +7 -10
  18. data/lib/semantic_logger/appender/splunk_http.rb +16 -16
  19. data/lib/semantic_logger/appender/syslog.rb +43 -122
  20. data/lib/semantic_logger/appender/tcp.rb +28 -9
  21. data/lib/semantic_logger/appender/udp.rb +4 -7
  22. data/lib/semantic_logger/appender/wrapper.rb +3 -7
  23. data/lib/semantic_logger/base.rb +47 -7
  24. data/lib/semantic_logger/formatters/base.rb +29 -10
  25. data/lib/semantic_logger/formatters/color.rb +75 -45
  26. data/lib/semantic_logger/formatters/default.rb +53 -28
  27. data/lib/semantic_logger/formatters/json.rb +7 -8
  28. data/lib/semantic_logger/formatters/raw.rb +97 -1
  29. data/lib/semantic_logger/formatters/syslog.rb +46 -80
  30. data/lib/semantic_logger/formatters/syslog_cee.rb +57 -0
  31. data/lib/semantic_logger/log.rb +17 -67
  32. data/lib/semantic_logger/logger.rb +17 -27
  33. data/lib/semantic_logger/processor.rb +70 -46
  34. data/lib/semantic_logger/semantic_logger.rb +130 -69
  35. data/lib/semantic_logger/subscriber.rb +18 -32
  36. data/lib/semantic_logger/version.rb +1 -1
  37. data/test/appender/elasticsearch_http_test.rb +75 -0
  38. data/test/appender/elasticsearch_test.rb +34 -27
  39. data/test/appender/file_test.rb +2 -2
  40. data/test/appender/honeybadger_test.rb +1 -1
  41. data/test/appender/kafka_test.rb +36 -0
  42. data/test/appender/new_relic_test.rb +1 -1
  43. data/test/appender/sentry_test.rb +1 -1
  44. data/test/appender/syslog_test.rb +2 -2
  45. data/test/appender/wrapper_test.rb +1 -1
  46. data/test/formatters/color_test.rb +154 -0
  47. data/test/formatters/default_test.rb +176 -0
  48. data/test/loggable_test.rb +1 -1
  49. data/test/logger_test.rb +47 -4
  50. data/test/measure_test.rb +2 -2
  51. data/test/semantic_logger_test.rb +34 -6
  52. data/test/test_helper.rb +8 -0
  53. metadata +14 -3
@@ -2,9 +2,105 @@ require 'json'
2
2
  module SemanticLogger
3
3
  module Formatters
4
4
  class Raw < Base
5
+
6
+ # Fields are added by populating this hash.
7
+ attr_accessor :hash, :log, :logger
8
+
9
+ # Host name
10
+ def host
11
+ hash[:host] = logger.host if log_host && logger.host
12
+ end
13
+
14
+ # Application name
15
+ def application
16
+ hash[:application] = logger.application if log_application && logger.application
17
+ end
18
+
19
+ # Date & time
20
+ def time
21
+ hash[:time] = log.time
22
+ end
23
+
24
+ # Log level
25
+ def level
26
+ hash[:level] = log.level
27
+ hash[:level_index] = log.level_index
28
+ end
29
+
30
+ # Process info
31
+ def process_info
32
+ hash[:pid] = $$
33
+ hash[:thread] = log.thread_name
34
+
35
+ file, line = log.file_name_and_line
36
+ if file
37
+ hash[:file] = file
38
+ hash[:line] = line.to_i
39
+ end
40
+ end
41
+
42
+ # Tags
43
+ def tags
44
+ hash[:tags] = log.tags if log.tags && !log.tags.empty?
45
+ end
46
+
47
+ # Named Tags
48
+ def named_tags
49
+ hash[:named_tags] = log.named_tags if log.named_tags && !log.named_tags.empty?
50
+ end
51
+
52
+ # Duration
53
+ def duration
54
+ return unless log.duration
55
+
56
+ hash[:duration_ms] = log.duration
57
+ hash[:duration] = log.duration_human
58
+ end
59
+
60
+ # Class / app name
61
+ def name
62
+ hash[:name] = log.name
63
+ end
64
+
65
+ # Log message
66
+ def message
67
+ hash[:message] = log.cleansed_message if log.message
68
+ end
69
+
70
+ # Payload
71
+ def payload
72
+ hash[:payload] = log.payload if log.payload && log.payload.respond_to?(:empty?) && !log.payload.empty?
73
+ end
74
+
75
+ # Exception
76
+ def exception
77
+ return unless log.exception
78
+ root = hash
79
+ log.each_exception do |exception, i|
80
+ name = i == 0 ? :exception : :cause
81
+ root[name] = {
82
+ name: exception.class.name,
83
+ message: exception.message,
84
+ stack_trace: exception.backtrace
85
+ }
86
+ root = root[name]
87
+ end
88
+ end
89
+
90
+ # Metric
91
+ def metric
92
+ hash[:metric] = log.metric if log.metric
93
+ hash[:metric_amount] = log.metric_amount if log.metric_amount
94
+ end
95
+
5
96
  # Returns log messages in Hash format
6
97
  def call(log, logger)
7
- log.to_h(log_host ? logger.host : nil, log_application ? logger.application : nil)
98
+ self.hash = {}
99
+ self.log = log
100
+ self.logger = logger
101
+
102
+ host; application; time; level; process_info; duration; tags; named_tags; name; message; payload; exception; metric
103
+ hash
8
104
  end
9
105
 
10
106
  end
@@ -7,112 +7,78 @@ end
7
7
  module SemanticLogger
8
8
  module Formatters
9
9
  class Syslog < Default
10
- attr_accessor :level_map, :options, :facility
10
+ attr_accessor :level_map, :facility
11
11
 
12
- # Default mapping of ruby log levels to syslog log levels
12
+ # Default level map for every log level
13
13
  #
14
+ # :fatal => ::Syslog::LOG_CRIT - "A critical condition has occurred"
15
+ # :error => ::Syslog::LOG_ERR - "An error occurred"
16
+ # :warning =>::Syslog::LOG_WARNING - "Warning of a possible problem"
17
+ # :info => ::Syslog::LOG_NOTICE - "A normal but significant condition occurred"
18
+ # :debug => ::Syslog::LOG_INFO - "Informational message"
19
+ # :trace => ::Syslog::LOG_DEBUG - "Debugging information"
20
+ #
21
+ # The following levels are not used by default.
14
22
  # ::Syslog::LOG_EMERG - "System is unusable"
15
23
  # ::Syslog::LOG_ALERT - "Action needs to be taken immediately"
16
- # ::Syslog::LOG_CRIT - "A critical condition has occurred"
17
- # ::Syslog::LOG_ERR - "An error occurred"
18
- # ::Syslog::LOG_WARNING - "Warning of a possible problem"
19
- # ::Syslog::LOG_NOTICE - "A normal but significant condition occurred"
20
- # ::Syslog::LOG_INFO - "Informational message"
21
- # ::Syslog::LOG_DEBUG - "Debugging information"
22
- DEFAULT_LEVEL_MAP = {
23
- fatal: ::Syslog::LOG_CRIT,
24
- error: ::Syslog::LOG_ERR,
25
- warn: ::Syslog::LOG_WARNING,
26
- info: ::Syslog::LOG_NOTICE,
27
- debug: ::Syslog::LOG_INFO,
28
- trace: ::Syslog::LOG_DEBUG
29
- }.freeze
24
+ class LevelMap
25
+ attr_accessor :trace, :debug, :info, :warn, :error, :fatal
26
+
27
+ def initialize(trace: ::Syslog::LOG_DEBUG, debug: ::Syslog::LOG_INFO, info: ::Syslog::LOG_NOTICE, warn: ::Syslog::LOG_WARNING, error: ::Syslog::LOG_ERR, fatal: ::Syslog::LOG_CRIT)
28
+ @trace = trace
29
+ @debug = debug
30
+ @info = info
31
+ @warn = warn
32
+ @error = error
33
+ @fatal = fatal
34
+ end
35
+
36
+ def [](level)
37
+ public_send(level)
38
+ end
39
+ end
30
40
 
31
41
  # Create a Syslog Log Formatter
32
42
  #
33
43
  # Parameters:
34
- # options: [Integer]
35
- # Default: ::Syslog::LOG_PID | ::Syslog::LOG_CONS
36
- # Any of the following (options can be logically OR'd together)
37
- # ::Syslog::LOG_CONS
38
- # ::Syslog::LOG_NDELAY
39
- # ::Syslog::LOG_NOWAIT
40
- # ::Syslog::LOG_ODELAY
41
- # ::Syslog::LOG_PERROR
42
- # ::Syslog::LOG_PID
43
- #
44
44
  # facility: [Integer]
45
45
  # Default: ::Syslog::LOG_USER
46
- # Type of program (can be logically OR'd together)
47
- # ::Syslog::LOG_AUTH
48
- # ::Syslog::LOG_AUTHPRIV
49
- # ::Syslog::LOG_CONSOLE
50
- # ::Syslog::LOG_CRON
51
- # ::Syslog::LOG_DAEMON
52
- # ::Syslog::LOG_FTP
53
- # ::Syslog::LOG_KERN
54
- # ::Syslog::LOG_LRP
55
- # ::Syslog::LOG_MAIL
56
- # ::Syslog::LOG_NEWS
57
- # ::Syslog::LOG_NTP
58
- # ::Syslog::LOG_SECURITY
59
- # ::Syslog::LOG_SYSLOG
60
- # ::Syslog::LOG_USER
61
- # ::Syslog::LOG_UUCP
62
- # ::Syslog::LOG_LOCAL0
63
- # ::Syslog::LOG_LOCAL1
64
- # ::Syslog::LOG_LOCAL2
65
- # ::Syslog::LOG_LOCAL3
66
- # ::Syslog::LOG_LOCAL4
67
- # ::Syslog::LOG_LOCAL5
68
- # ::Syslog::LOG_LOCAL6
69
- # ::Syslog::LOG_LOCAL7
70
46
  #
71
- # level_map: [Hash]
47
+ # level_map: [Hash | SemanticLogger::Formatters::Syslog::LevelMap]
72
48
  # Supply a custom map of SemanticLogger levels to syslog levels.
73
- # For example, passing in { warn: ::Syslog::LOG_NOTICE }
74
- # would result in a log mapping that matches the default level map,
75
- # except for :warn, which ends up with a LOG_NOTICE level instead of a
76
- # LOG_WARNING one.
77
- # Without overriding any parameters, the level map will be
78
- # LEVEL_MAP = {
79
- # fatal: ::Syslog::LOG_CRIT,
80
- # error: ::Syslog::LOG_ERR,
81
- # warn: ::Syslog::LOG_WARNING,
82
- # info: ::Syslog::LOG_NOTICE,
83
- # debug: ::Syslog::LOG_INFO,
84
- # trace: ::Syslog::LOG_DEBUG
85
- # }
86
- def initialize(options = {})
87
- options = options.dup
88
- @options = options.delete(:options) || (::Syslog::LOG_PID | ::Syslog::LOG_CONS)
89
- @facility = options.delete(:facility) || ::Syslog::LOG_USER
90
- @level_map = DEFAULT_LEVEL_MAP.dup
91
- if level_map = options.delete(:level_map)
92
- @level_map.update(level_map)
93
- end
94
- # Time is already part of Syslog packet
95
- options[:time_format] = nil unless options.has_key?(:time_format)
96
- super(options)
49
+ #
50
+ # Example:
51
+ # # Change the warn level to LOG_NOTICE level instead of a the default of LOG_WARNING.
52
+ # SemanticLogger.add_appender(appender: :syslog, level_map: {warn: ::Syslog::LOG_NOTICE})
53
+ def initialize(facility: ::Syslog::LOG_USER, level_map: LevelMap.new)
54
+ @facility = facility
55
+ @level_map = level_map.is_a?(LevelMap) ? level_map : LevelMap.new(level_map)
56
+ super()
57
+ end
58
+
59
+ # Time is part of the syslog packet and is not included in the formatted message.
60
+ def time
61
+ nil
97
62
  end
98
63
 
99
64
  def call(log, logger)
100
65
  message = super(log, logger)
101
- create_syslog_packet(log, message)
66
+ create_syslog_packet(message)
102
67
  end
103
68
 
69
+ private
70
+
104
71
  # Create Syslog Packet
105
- def create_syslog_packet(log, message)
72
+ def create_syslog_packet(message)
106
73
  packet = SyslogProtocol::Packet.new
107
- packet.hostname = host
74
+ packet.hostname = logger.host
108
75
  packet.facility = facility
109
- packet.tag = application.gsub(' ', '')
76
+ packet.tag = logger.application.gsub(' ', '')
110
77
  packet.content = message
111
78
  packet.time = log.time
112
79
  packet.severity = level_map[log.level]
113
80
  packet.to_s
114
81
  end
115
-
116
82
  end
117
83
  end
118
84
  end
@@ -0,0 +1,57 @@
1
+ begin
2
+ require 'syslog_protocol'
3
+ rescue LoadError
4
+ raise 'Gem syslog_protocol is required for remote logging using the Syslog protol. Please add the gem "syslog_protocol" to your Gemfile.'
5
+ end
6
+
7
+ module SemanticLogger
8
+ module Formatters
9
+ class SyslogCee < Raw
10
+ attr_accessor :level_map, :facility
11
+
12
+ # CEE JSON Syslog format
13
+ # Untested prototype code. Based on documentation only.
14
+ # If this works for you, please let us know by opening an issue.
15
+ #
16
+ # Parameters:
17
+ # facility: [Integer]
18
+ # Default: ::Syslog::LOG_USER
19
+ #
20
+ # level_map: [Hash | SemanticLogger::Formatters::Syslog::LevelMap]
21
+ # Supply a custom map of SemanticLogger levels to syslog levels.
22
+ #
23
+ # Example:
24
+ # # Log via udp to a remote syslog server on host: `server1` and port `8514`, using the CEE format.
25
+ # SemanticLogger.add_appender(appender: :syslog, formatter: syslog_cee, url: 'udp://server1:8514')
26
+ def initialize(facility: ::Syslog::LOG_USER, level_map: SemanticLogger::Formatters::Syslog::LevelMap.new)
27
+ @facility = facility
28
+ @level_map = level_map.is_a?(SemanticLogger::Formatters::Syslog::LevelMap) ? level_map : SemanticLogger::Formatters::Syslog::LevelMap.new(level_map)
29
+ super()
30
+ end
31
+
32
+ # Time is part of the syslog packet and is not included in the formatted message.
33
+ def time
34
+ end
35
+
36
+ def call(log, logger)
37
+ hash = super(log, logger)
38
+ create_syslog_packet("@cee: #{hash.to_json}")
39
+ end
40
+
41
+ private
42
+
43
+ # Create Syslog Packet
44
+ def create_syslog_packet(message)
45
+ packet = SyslogProtocol::Packet.new
46
+ packet.hostname = logger.host
47
+ packet.facility = facility
48
+ packet.tag = logger.application.gsub(' ', '')
49
+ packet.content = message
50
+ packet.time = log.time
51
+ packet.severity = level_map[log.level]
52
+ packet.to_s
53
+ end
54
+ end
55
+ end
56
+ end
57
+
@@ -43,8 +43,11 @@ module SemanticLogger
43
43
  # metric_amount [Numeric]
44
44
  # Used for numeric or counter metrics.
45
45
  # For example, the number of inquiries or, the amount purchased etc.
46
+ #
47
+ # context [Hash]
48
+ # Named contexts that were captured when the log entry was created.
46
49
  class Log
47
- attr_accessor :level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric, :backtrace, :metric_amount, :named_tags
50
+ attr_accessor :level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric, :backtrace, :metric_amount, :named_tags, :context
48
51
 
49
52
  def initialize(name, level, index = nil)
50
53
  @level = level
@@ -180,7 +183,7 @@ module SemanticLogger
180
183
  # Returns [String] duration of the log entry as a string
181
184
  # Returns nil if their is no duration
182
185
  # Java time precision does not include microseconds
183
- if defined? JRuby
186
+ if Formatters::Base::PRECISION == 3
184
187
  def duration_to_s
185
188
  "#{duration.to_i}ms" if duration
186
189
  end
@@ -256,75 +259,22 @@ module SemanticLogger
256
259
  !(payload.nil? || (payload.respond_to?(:empty?) && payload.empty?))
257
260
  end
258
261
 
259
- if defined? JRuby
260
- # Return the Time as a formatted string
261
- # JRuby only supports time in ms
262
- # DEPRECATED
263
- def formatted_time
264
- "#{time.strftime('%Y-%m-%d %H:%M:%S')}.#{'%03d' % (time.usec/1000)}"
265
- end
266
- else
267
- # Return the Time as a formatted string
268
- # Ruby MRI supports micro seconds
269
- # DEPRECATED
270
- def formatted_time
271
- "#{time.strftime('%Y-%m-%d %H:%M:%S')}.#{'%06d' % (time.usec)}"
272
- end
262
+ # DEPRECATED
263
+ def formatted_time
264
+ time.strftime(Formatters::Base::TIME_FORMAT)
273
265
  end
274
266
 
275
- # Returns [Hash] representation of this log entry
276
- def to_h(host = SemanticLogger.host, application = SemanticLogger.application)
277
- # Header
278
- h = {
279
- name: name,
280
- pid: $$,
281
- thread: thread_name,
282
- time: time,
283
- level: level,
284
- level_index: level_index,
285
- }
286
- h[:host] = host if host
287
- h[:application] = application if application
288
- file, line = file_name_and_line
289
- if file
290
- h[:file] = file
291
- h[:line] = line.to_i
292
- end
293
-
294
- # Tags
295
- h[:tags] = tags if tags && !tags.empty?
296
- h[:named_tags] = named_tags if named_tags && !named_tags.empty?
267
+ DeprecatedLogger = Struct.new(:host, :application)
297
268
 
298
- # Duration
299
- if duration
300
- h[:duration_ms] = duration
301
- h[:duration] = duration_human
302
- end
303
-
304
- # Log message
305
- h[:message] = cleansed_message if message
306
-
307
- # Payload
308
- h[:payload] = payload if payload && payload.respond_to?(:empty?) && !payload.empty?
309
-
310
- # Exceptions
311
- if exception
312
- root = h
313
- each_exception do |exception, i|
314
- name = i == 0 ? :exception : :cause
315
- root[name] = {
316
- name: exception.class.name,
317
- message: exception.message,
318
- stack_trace: exception.backtrace
319
- }
320
- root = root[name]
321
- end
322
- end
269
+ # DEPRECATED: Use SemanticLogger::Formatters::Raw
270
+ def to_h(host = SemanticLogger.host, application = SemanticLogger.application)
271
+ logger = DeprecatedLogger.new(host, application)
272
+ SemanticLogger::Formatters::Raw.new.call(self, logger)
273
+ end
323
274
 
324
- # Metric
325
- h[:metric] = metric if metric
326
- h[:metric_amount] = metric_amount if metric_amount
327
- h
275
+ # Lazy initializes the context hash and assigns a key value pair.
276
+ def set_context(key, value)
277
+ (self.context ||= {})[key] = value
328
278
  end
329
279
 
330
280
  private
@@ -13,7 +13,7 @@ module SemanticLogger
13
13
  # logger = SemanticLogger::Logger.new('MyClass')
14
14
  #
15
15
  # Parameters:
16
- # application
16
+ # klass
17
17
  # A class, module or a string with the application/class name
18
18
  # to be used in the logger
19
19
  #
@@ -26,47 +26,37 @@ module SemanticLogger
26
26
  # regular expression. All other messages will be ignored
27
27
  # Proc: Only include log messages where the supplied Proc returns true
28
28
  # The Proc must return true or false
29
- def initialize(klass, level=nil, filter=nil)
30
- super
29
+ def initialize(klass, level = nil, filter = nil)
30
+ super(klass, level, filter)
31
31
  end
32
32
 
33
- # Returns [Integer] the number of log entries that have not been written
34
- # to the appenders
35
- #
36
- # When this number grows it is because the logging appender thread is not
37
- # able to write to the appenders fast enough. Either reduce the amount of
38
- # logging, increase the log level, reduce the number of appenders, or
39
- # look into speeding up the appenders themselves
33
+ # Place log request on the queue for the Appender thread to write to each
34
+ # appender in the order that they were registered
35
+ def log(log, message = nil, progname = nil, &block)
36
+ # Compatibility with ::Logger
37
+ return add(log, message, progname, &block) unless log.is_a?(SemanticLogger::Log)
38
+ Processor << log
39
+ end
40
+
41
+ # DEPRECATED
40
42
  def self.queue_size
41
43
  Processor.queue_size
42
44
  end
43
45
 
44
- # Flush all queued log entries disk, database, etc.
45
- # All queued log messages are written and then each appender is flushed in turn
46
+ # DEPRECATED
46
47
  def self.flush
47
- Processor.submit_request(:flush)
48
+ Processor.flush
48
49
  end
49
50
 
50
- # Close all appenders and flush any outstanding messages
51
+ # DEPRECATED
51
52
  def self.close
52
- Processor.submit_request(:close)
53
+ Processor.close
53
54
  end
54
55
 
55
- # Allow the internal logger to be overridden from its default of STDERR
56
- # Can be replaced with another Ruby logger or Rails logger, but never to
57
- # SemanticLogger::Logger itself since it is for reporting problems
58
- # while trying to log to the various appenders
56
+ # DEPRECATED
59
57
  def self.logger=(logger)
60
58
  Processor.logger = logger
61
59
  end
62
60
 
63
- # Place log request on the queue for the Appender thread to write to each
64
- # appender in the order that they were registered
65
- def log(log, message = nil, progname = nil, &block)
66
- # Compatibility with ::Logger
67
- return add(log, message, progname, &block) unless log.is_a?(SemanticLogger::Log)
68
- Processor << log
69
- end
70
-
71
61
  end
72
62
  end