semantic_logger 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/semantic_logger.rb +29 -13
  4. data/lib/semantic_logger/ansi_colors.rb +27 -0
  5. data/lib/semantic_logger/appender/base.rb +54 -128
  6. data/lib/semantic_logger/appender/bugsnag.rb +29 -19
  7. data/lib/semantic_logger/appender/elasticsearch.rb +9 -9
  8. data/lib/semantic_logger/appender/file.rb +40 -18
  9. data/lib/semantic_logger/appender/graylog.rb +30 -26
  10. data/lib/semantic_logger/appender/http.rb +14 -19
  11. data/lib/semantic_logger/appender/mongodb.rb +20 -20
  12. data/lib/semantic_logger/appender/new_relic.rb +15 -15
  13. data/lib/semantic_logger/appender/splunk.rb +1 -1
  14. data/lib/semantic_logger/appender/splunk_http.rb +28 -25
  15. data/lib/semantic_logger/appender/syslog.rb +41 -42
  16. data/lib/semantic_logger/appender/wrapper.rb +19 -17
  17. data/lib/semantic_logger/base.rb +57 -32
  18. data/lib/semantic_logger/concerns/compatibility.rb +51 -0
  19. data/lib/semantic_logger/debug_as_trace_logger.rb +6 -2
  20. data/lib/semantic_logger/formatters/color.rb +66 -0
  21. data/lib/semantic_logger/formatters/default.rb +39 -0
  22. data/lib/semantic_logger/formatters/json.rb +16 -0
  23. data/lib/semantic_logger/jruby/garbage_collection_logger.rb +1 -1
  24. data/lib/semantic_logger/log.rb +13 -8
  25. data/lib/semantic_logger/loggable.rb +2 -2
  26. data/lib/semantic_logger/logger.rb +18 -13
  27. data/lib/semantic_logger/metrics/new_relic.rb +18 -0
  28. data/lib/semantic_logger/metrics/statsd.rb +48 -0
  29. data/lib/semantic_logger/semantic_logger.rb +122 -55
  30. data/lib/semantic_logger/version.rb +1 -1
  31. data/test/appender/bugsnag_test.rb +12 -3
  32. data/test/appender/mongodb_test.rb +6 -5
  33. data/test/appender/new_relic_test.rb +1 -1
  34. data/test/appender/splunk_http_test.rb +1 -0
  35. data/test/concerns/compatibility_test.rb +106 -0
  36. data/test/debug_as_trace_logger_test.rb +1 -1
  37. data/test/loggable_test.rb +1 -1
  38. data/test/logger_test.rb +183 -24
  39. metadata +12 -3
@@ -9,15 +9,20 @@ module SemanticLogger
9
9
  # Create a File Logger appender instance.
10
10
  #
11
11
  # Parameters
12
- # filename [String|IO]
12
+ # :file_name [String|IO]
13
13
  # Name of file to write to.
14
14
  # Or, an IO stream to which to write the log message to.
15
15
  #
16
- # level [:trace | :debug | :info | :warn | :error | :fatal]
16
+ # :level [:trace | :debug | :info | :warn | :error | :fatal]
17
17
  # Override the log level for this appender.
18
18
  # Default: SemanticLogger.default_level
19
19
  #
20
- # filter [Regexp|Proc]
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]
21
26
  # RegExp: Only include log messages where the class name matches the supplied
22
27
  # regular expression. All other messages will be ignored.
23
28
  # Proc: Only include log messages where the supplied Proc returns true
@@ -30,10 +35,10 @@ module SemanticLogger
30
35
  # SemanticLogger.default_level = :info
31
36
  #
32
37
  # # Log to screen
33
- # SemanticLogger.add_appender(STDOUT)
38
+ # SemanticLogger.add_appender(io: STDOUT, formatter: :color)
34
39
  #
35
40
  # # And log to a file at the same time
36
- # SemanticLogger::Logger.add_appender('application.log')
41
+ # SemanticLogger::Logger.add_appender(file_name: 'application.log', formatter: :color)
37
42
  #
38
43
  # logger = SemanticLogger['test']
39
44
  # logger.info 'Hello World'
@@ -46,36 +51,53 @@ module SemanticLogger
46
51
  # SemanticLogger.default_level = :trace
47
52
  #
48
53
  # # Log to screen but only display :info and above
49
- # SemanticLogger.add_appender(STDOUT, :info)
54
+ # SemanticLogger.add_appender(io: STDOUT, level: :info)
50
55
  #
51
56
  # # And log to a file at the same time, including all :trace level data
52
- # SemanticLogger.add_appender('application.log')
57
+ # SemanticLogger.add_appender(file_name: 'application.log')
53
58
  #
54
59
  # logger = SemanticLogger['test']
55
60
  # logger.info 'Hello World'
56
- def initialize(filename, level=nil, filter=nil, &block)
57
- raise 'filename cannot be null when initializing the SemanticLogging::Appender::File' unless filename
58
- @log =
59
- if filename.respond_to?(:write) and filename.respond_to?(:close)
60
- filename
61
+ def initialize(options={}, deprecated_level = nil, deprecated_filter = nil, &block)
62
+ # Old style arguments: (file_name, level=nil, filter=nil, &block)
63
+ options =
64
+ if options.is_a?(Hash)
65
+ options.dup
61
66
  else
62
- @filename = filename
63
- reopen
67
+ file_name = options
68
+ opts = {}
69
+ if file_name.respond_to?(:write) && file_name.respond_to?(:close)
70
+ opts[:io] = file_name
71
+ else
72
+ opts[:file_name] = file_name
73
+ end
74
+ opts[:level] = deprecated_level if deprecated_level
75
+ opts[:filter] = deprecated_filter if deprecated_filter
76
+ opts
64
77
  end
65
78
 
79
+ if io = options.delete(:io)
80
+ @log = io
81
+ else
82
+ @file_name = options.delete(:file_name)
83
+ raise 'SemanticLogging::Appender::File missing mandatory parameter :file_name or :io' unless @file_name
84
+ reopen
85
+ end
86
+
66
87
  # Set the log level and formatter if supplied
67
- super(level, filter, &block)
88
+ super(options, &block)
68
89
  end
69
90
 
70
91
  # After forking an active process call #reopen to re-open
71
92
  # open the file handles etc to resources
72
93
  #
73
- # Note: This method will only work if a String filename was supplied
94
+ # Note: This method will only work if :file_name was supplied
74
95
  # on the initializer.
96
+ # If :io was supplied, it will need to be re-opened manually.
75
97
  def reopen
76
- return unless @filename
98
+ return unless @file_name
77
99
 
78
- @log = open(@filename, (::File::WRONLY | ::File::APPEND | ::File::CREAT))
100
+ @log = open(@file_name, (::File::WRONLY | ::File::APPEND | ::File::CREAT))
79
101
  # Force all log entries to write immediately without buffering
80
102
  # Allows multiple processes to write to the same log file simultaneously
81
103
  @log.sync = true
@@ -8,15 +8,11 @@ end
8
8
  # Forward log entries to a Graylog server.
9
9
  #
10
10
  # Example:
11
- # appender = SemanticLogger::Appender::Graylog.new(
12
- # url: 'udp://localhost:12201'
11
+ # SemanticLogger.add_appender(
12
+ # appender: :graylog,
13
+ # url: 'udp://localhost:12201'
13
14
  # )
14
15
  #
15
- # # Optional: Add filter to exclude health_check, or other log entries
16
- # appender.filter = Proc.new { |log| log.message !~ /(health_check|Not logged in)/ }
17
- #
18
- # SemanticLogger.add_appender(appender)
19
- #
20
16
  # Notes:
21
17
  # * trace is not supported by Graylog, so trace level logging will appear as debug in Graylog.
22
18
  #
@@ -62,17 +58,20 @@ class SemanticLogger::Appender::Graylog < SemanticLogger::Appender::Base
62
58
  # Override the log level for this appender.
63
59
  # Default: SemanticLogger.default_level
64
60
  #
61
+ # formatter: [Object|Proc]
62
+ # An instance of a class that implements #call, or a Proc to be used to format
63
+ # the output from this appender
64
+ # Default: Use the built-in formatter (See: #call)
65
+ #
65
66
  # filter: [Regexp|Proc]
66
67
  # RegExp: Only include log messages where the class name matches the supplied.
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.
70
71
  def initialize(options = {}, &block)
71
- @options = options.dup
72
- level = @options.delete(:level)
73
- filter = @options.delete(:filter)
74
- @url = options.delete(:url) || 'udp://localhost:12201'
75
- @max_size = @options.delete(:max_size) || 'WAN'
72
+ @gelf_options = options.dup
73
+ @url = @gelf_options.delete(:url) || 'udp://localhost:12201'
74
+ @max_size = @gelf_options.delete(:max_size) || 'WAN'
76
75
 
77
76
  uri = URI.parse(@url)
78
77
  @server = uri.host
@@ -81,29 +80,34 @@ class SemanticLogger::Appender::Graylog < SemanticLogger::Appender::Base
81
80
 
82
81
  raise(ArgumentError, "Invalid protocol value: #{protocol}. Must be :udp or :tcp") unless [:udp, :tcp].include?(protocol)
83
82
 
84
- @options[:protocol] = protocol == :tcp ? GELF::Protocol::TCP : GELF::Protocol::UDP
85
- @options[:facility] = @options.delete(:application) || SemanticLogger.application
83
+ @gelf_options[:protocol] = protocol == :tcp ? GELF::Protocol::TCP : GELF::Protocol::UDP
84
+ @gelf_options[:facility] = @gelf_options.delete(:application) || SemanticLogger.application
85
+
86
+ options = {
87
+ level: @gelf_options.delete(:level),
88
+ filter: @gelf_options.delete(:filter),
89
+ formatter: @gelf_options.delete(:formatter)
90
+ }
86
91
  reopen
87
- super(level, filter, &block)
92
+
93
+ super(options, &block)
88
94
  end
89
95
 
90
96
  # Re-open after process fork
91
97
  def reopen
92
- @notifier = GELF::Notifier.new(@server, @port, @max_size, @options)
98
+ @notifier = GELF::Notifier.new(@server, @port, @max_size, @gelf_options)
93
99
  @notifier.collect_file_and_line = false
94
100
  end
95
101
 
96
102
  # Returns [Hash] of parameters to send
97
- def default_formatter
98
- Proc.new do |log, logger|
99
- h = log.to_h
100
- h.delete(:time)
101
- h[:timestamp] = log.time.utc.to_f
102
- h[:level] = logger.map_level(log)
103
- h[:level_str] = log.level.to_s
104
- h[:short_message] = h.delete(:message) if log.message
105
- h
106
- end
103
+ def call(log, logger)
104
+ h = log.to_h
105
+ h.delete(:time)
106
+ h[:timestamp] = log.time.utc.to_f
107
+ h[:level] = logger.map_level(log)
108
+ h[:level_str] = log.level.to_s
109
+ h[:short_message] = h.delete(:message) if log.message
110
+ h
107
111
  end
108
112
 
109
113
  # Forward log messages
@@ -11,14 +11,10 @@ require 'json'
11
11
  # * SSL encryption (https).
12
12
  #
13
13
  # Example:
14
- # appender = SemanticLogger::Appender::Http.new(
15
- # url: 'http://localhost:8088/path'
14
+ # SemanticLogger.add_appender(
15
+ # appender: :http,
16
+ # url: 'http://localhost:8088/path'
16
17
  # )
17
- #
18
- # # Optional: Exclude health_check log entries, etc.
19
- # appender.filter = Proc.new { |log| log.message !~ /(health_check|Not logged in)/}
20
- #
21
- # SemanticLogger.add_appender(appender)
22
18
  class SemanticLogger::Appender::Http < SemanticLogger::Appender::Base
23
19
  attr_accessor :username, :application, :host, :compress, :header
24
20
  attr_reader :http, :url, :server, :port, :path, :ssl_options
@@ -61,15 +57,18 @@ class SemanticLogger::Appender::Http < SemanticLogger::Appender::Base
61
57
  # Override the log level for this appender.
62
58
  # Default: SemanticLogger.default_level
63
59
  #
60
+ # formatter: [Object|Proc]
61
+ # An instance of a class that implements #call, or a Proc to be used to format
62
+ # the output from this appender
63
+ # Default: Use the built-in formatter (See: #call)
64
+ #
64
65
  # filter: [Regexp|Proc]
65
66
  # RegExp: Only include log messages where the class name matches the supplied.
66
67
  # regular expression. All other messages will be ignored.
67
68
  # Proc: Only include log messages where the supplied Proc returns true
68
69
  # The Proc must return true or false.
69
70
  def initialize(options, &block)
70
- @options = options.dup
71
- level = @options.delete(:level)
72
- filter = @options.delete(:filter)
71
+ options = options.dup
73
72
  @url = options.delete(:url)
74
73
  @ssl_options = options.delete(:ssl)
75
74
  @username = options.delete(:username)
@@ -77,7 +76,9 @@ class SemanticLogger::Appender::Http < SemanticLogger::Appender::Base
77
76
  @application = options.delete(:application) || 'Semantic Logger'
78
77
  @host = options.delete(:host) || SemanticLogger.host
79
78
  @compress = options.delete(:compress) || false
80
- raise(ArgumentError, "Unknown options: #{options.inspect}") if options.size > 0
79
+ unless options.has_key?(:formatter)
80
+ options[:formatter] = block || (respond_to?(:call) ? self : SemanticLogger::Formatters::Json.new)
81
+ end
81
82
 
82
83
  raise(ArgumentError, 'Missing mandatory parameter :url') unless @url
83
84
 
@@ -96,12 +97,12 @@ class SemanticLogger::Appender::Http < SemanticLogger::Appender::Base
96
97
  @port = uri.port
97
98
  @username = uri.user if !@username && uri.user
98
99
  @password = uri.password if !@password && uri.password
99
- @path = uri.request_uri
100
+ @path = uri.path
100
101
 
101
102
  reopen
102
103
 
103
104
  # Pass on the level and custom formatter if supplied
104
- super(level, filter, &block)
105
+ super(options)
105
106
  end
106
107
 
107
108
  # Re-open after process fork
@@ -114,15 +115,9 @@ class SemanticLogger::Appender::Http < SemanticLogger::Appender::Base
114
115
  def log(log)
115
116
  return false if (level_index > (log.level_index || 0)) ||
116
117
  !include_message?(log) # Filtered out?
117
-
118
118
  post(formatter.call(log, self))
119
119
  end
120
120
 
121
- # Use the JSON formatter
122
- def default_formatter
123
- self.class.json_formatter
124
- end
125
-
126
121
  private
127
122
 
128
123
  def compress_data(data)
@@ -42,7 +42,7 @@ module SemanticLogger
42
42
  # db: database,
43
43
  # collection_size: 1024**3 # 1.gigabyte
44
44
  # )
45
- # SemanticLogger.add_appender(appender)
45
+ # SemanticLogger.add_appender(appender: appender)
46
46
  #
47
47
  # logger = SemanticLogger['Example']
48
48
  #
@@ -91,25 +91,27 @@ module SemanticLogger
91
91
  # Override the log level for this appender.
92
92
  # Default: SemanticLogger.default_level
93
93
  #
94
+ # formatter: [Object|Proc]
95
+ # An instance of a class that implements #call, or a Proc to be used to format
96
+ # the output from this appender
97
+ # Default: Use the built-in formatter (See: #call)
98
+ #
94
99
  # filter: [Regexp|Proc]
95
100
  # RegExp: Only include log messages where the class name matches the supplied.
96
101
  # regular expression. All other messages will be ignored.
97
102
  # Proc: Only include log messages where the supplied Proc returns true
98
103
  # The Proc must return true or false.
99
104
  def initialize(options = {}, &block)
100
- options = options.dup
101
- level = options.delete(:level)
102
- filter = options.delete(:filter)
103
- @db = options.delete(:db) || raise('Missing mandatory parameter :db')
104
- @collection_name = options.delete(:collection_name) || 'semantic_logger'
105
- @host = options.delete(:host) || options.delete(:host_name) || SemanticLogger.host
106
- @write_concern = options.delete(:write_concern) || 0
107
- @application = options.delete(:application) || SemanticLogger.application
105
+ options = options.dup
106
+ @db = options.delete(:db) || raise('Missing mandatory parameter :db')
107
+ @collection_name = options.delete(:collection_name) || 'semantic_logger'
108
+ @host = options.delete(:host) || options.delete(:host_name) || SemanticLogger.host
109
+ @write_concern = options.delete(:write_concern) || 0
110
+ @application = options.delete(:application) || SemanticLogger.application
108
111
 
109
112
  # Create a collection that will hold the lesser of 1GB space or 10K documents
110
- @collection_size = options.delete(:collection_size) || 1024**3
111
- @collection_max = options.delete(:collection_max)
112
- raise(ArgumentError, "Unknown options: #{options.inspect}") if options.size > 0
113
+ @collection_size = options.delete(:collection_size) || 1024**3
114
+ @collection_max = options.delete(:collection_max)
113
115
 
114
116
  reopen
115
117
 
@@ -117,7 +119,7 @@ module SemanticLogger
117
119
  create_indexes
118
120
 
119
121
  # Set the log level and formatter
120
- super(level, filter, &block)
122
+ super(options, &block)
121
123
  end
122
124
 
123
125
  # After forking an active process call #reopen to re-open
@@ -160,13 +162,11 @@ module SemanticLogger
160
162
 
161
163
  # Default log formatter
162
164
  # Replace this formatter by supplying a Block to the initializer
163
- def default_formatter
164
- Proc.new do |log|
165
- h = log.to_h
166
- h[:host] = host
167
- h[:application] = application
168
- h
169
- end
165
+ def call(log, logger)
166
+ h = log.to_h
167
+ h[:host] = host
168
+ h[:application] = application
169
+ h
170
170
  end
171
171
 
172
172
  # Log the message to MongoDB
@@ -10,8 +10,7 @@ end
10
10
  # "Applications" > "Application Name" > "Events" > "Errors" in New Relic.
11
11
  #
12
12
  # Example:
13
- # SemanticLogger.add_appender(SemanticLogger::Appender::NewRelic.new)
14
- #
13
+ # SemanticLogger.add_appender(appender: :new_relic)
15
14
  class SemanticLogger::Appender::NewRelic < SemanticLogger::Appender::Base
16
15
  # Create Appender
17
16
  #
@@ -20,6 +19,11 @@ class SemanticLogger::Appender::NewRelic < SemanticLogger::Appender::Base
20
19
  # Override the log level for this appender.
21
20
  # Default: :error
22
21
  #
22
+ # formatter: [Object|Proc]
23
+ # An instance of a class that implements #call, or a Proc to be used to format
24
+ # the output from this appender
25
+ # Default: Use the built-in formatter (See: #call)
26
+ #
23
27
  # filter: [Regexp|Proc]
24
28
  # RegExp: Only include log messages where the class name matches the supplied.
25
29
  # regular expression. All other messages will be ignored.
@@ -27,22 +31,18 @@ class SemanticLogger::Appender::NewRelic < SemanticLogger::Appender::Base
27
31
  # The Proc must return true or false.
28
32
  def initialize(options = {}, &block)
29
33
  # Backward compatibility
30
- options = {level: options} unless options.is_a?(Hash)
31
- @options = options.dup
32
- level = @options.delete(:level) || :error
33
- filter = @options.delete(:filter)
34
-
35
- super(level, filter, &block)
34
+ options = {level: options} unless options.is_a?(Hash)
35
+ options = options.dup
36
+ options[:level] = :error unless options.has_key?(:level)
37
+ super(options)
36
38
  end
37
39
 
38
40
  # Returns [Hash] of parameters to send to New Relic.
39
- def default_formatter
40
- Proc.new do |log, logger|
41
- h = log.to_h
42
- h.delete(:time)
43
- h.delete(:exception)
44
- {metric: log.metric, custom_params: h}
45
- end
41
+ def call(log, logger)
42
+ h = log.to_h
43
+ h.delete(:time)
44
+ h.delete(:exception)
45
+ {metric: log.metric, custom_params: h}
46
46
  end
47
47
 
48
48
  # Send an error notification to New Relic
@@ -18,7 +18,7 @@ class SemanticLogger::Appender::Splunk < SemanticLogger::Appender::Base
18
18
  reopen
19
19
 
20
20
  # Pass on the level and custom formatter if supplied
21
- super(level, &block)
21
+ super(level: level, &block)
22
22
  end
23
23
 
24
24
  # After forking an active process call #reopen to re-open
@@ -7,11 +7,11 @@ require 'json'
7
7
  # http://dev.splunk.com/view/event-collector/SP-CAAAE7F
8
8
  #
9
9
  # Example
10
- # appender = SemanticLogger::Appender::SplunkHttp.new(
11
- # url: 'http://localhost:8080',
12
- # token: '70CA900C-3D7E-42A4-9C79-7975D1C422A8'
10
+ # SemanticLogger.add_appender(
11
+ # appender: :splunk_http,
12
+ # url: 'http://localhost:8080',
13
+ # token: '70CA900C-3D7E-42A4-9C79-7975D1C422A8'
13
14
  # )
14
- # SemanticLogger.add_appender(appender)
15
15
  class SemanticLogger::Appender::SplunkHttp < SemanticLogger::Appender::Http
16
16
  # Create Splunk appender over persistent HTTP(S)
17
17
  #
@@ -54,16 +54,21 @@ class SemanticLogger::Appender::SplunkHttp < SemanticLogger::Appender::Http
54
54
  # Override the log level for this appender.
55
55
  # Default: SemanticLogger.default_level
56
56
  #
57
+ # formatter: [Object|Proc]
58
+ # An instance of a class that implements #call, or a Proc to be used to format
59
+ # the output from this appender
60
+ # Default: Use the built-in formatter (See: #call)
61
+ #
57
62
  # filter: [Regexp|Proc]
58
63
  # RegExp: Only include log messages where the class name matches the supplied.
59
64
  # regular expression. All other messages will be ignored.
60
65
  # Proc: Only include log messages where the supplied Proc returns true
61
66
  # The Proc must return true or false.
62
67
  def initialize(options, &block)
63
- options = options.dup
64
- @source_type = options.delete(:source_type)
65
- @index = options.delete(:index)
66
- 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)
67
72
  raise(ArgumentError, 'Missing mandatory parameter :token') unless token
68
73
 
69
74
  # Splunk supports HTTP Compression, enable by default
@@ -77,24 +82,22 @@ class SemanticLogger::Appender::SplunkHttp < SemanticLogger::Appender::Http
77
82
  # Returns [String] JSON to send to Splunk
78
83
  # For splunk format requirements see:
79
84
  # http://dev.splunk.com/view/event-collector/SP-CAAAE6P
80
- def default_formatter
81
- Proc.new do |log, logger|
82
- h = log.to_h
83
- h.delete(:application)
84
- h.delete(:host)
85
- h.delete(:time)
86
- message = {
87
- source: logger.application,
88
- host: logger.host,
89
- time: log.time.utc.to_f,
90
- event: h
91
- }
92
- message[:source_type] = @source_type if @source_type
93
- message[:index] = @index if @index
85
+ def call(log, logger)
86
+ h = log.to_h
87
+ h.delete(:application)
88
+ h.delete(:host)
89
+ h.delete(:time)
90
+ message = {
91
+ source: logger.application,
92
+ host: logger.host,
93
+ time: log.time.utc.to_f,
94
+ event: h
95
+ }
96
+ message[:source_type] = @source_type if @source_type
97
+ message[:index] = @index if @index
94
98
 
95
- # Render to JSON
96
- message.to_json
97
- end
99
+ # Render to JSON
100
+ message.to_json
98
101
  end
99
102
 
100
103
  end