semantic_logger 3.0.1 → 3.1.0

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