semantic_logger 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/semantic_logger.rb +22 -2
  4. data/lib/semantic_logger/appender/bugsnag.rb +2 -2
  5. data/lib/semantic_logger/appender/elasticsearch.rb +9 -1
  6. data/lib/semantic_logger/appender/file.rb +1 -1
  7. data/lib/semantic_logger/appender/graylog.rb +18 -21
  8. data/lib/semantic_logger/appender/honeybadger.rb +27 -12
  9. data/lib/semantic_logger/appender/http.rb +19 -11
  10. data/lib/semantic_logger/appender/mongodb.rb +23 -30
  11. data/lib/semantic_logger/appender/new_relic.rb +2 -2
  12. data/lib/semantic_logger/appender/splunk.rb +32 -21
  13. data/lib/semantic_logger/appender/splunk_http.rb +1 -3
  14. data/lib/semantic_logger/appender/syslog.rb +25 -10
  15. data/lib/semantic_logger/appender/tcp.rb +231 -0
  16. data/lib/semantic_logger/appender/udp.rb +106 -0
  17. data/lib/semantic_logger/appender/wrapper.rb +1 -1
  18. data/lib/semantic_logger/base.rb +9 -3
  19. data/lib/semantic_logger/formatters/base.rb +36 -0
  20. data/lib/semantic_logger/formatters/color.rb +13 -10
  21. data/lib/semantic_logger/formatters/default.rb +8 -5
  22. data/lib/semantic_logger/formatters/json.rb +10 -3
  23. data/lib/semantic_logger/formatters/raw.rb +13 -0
  24. data/lib/semantic_logger/formatters/syslog.rb +119 -0
  25. data/lib/semantic_logger/log.rb +7 -5
  26. data/lib/semantic_logger/loggable.rb +5 -0
  27. data/lib/semantic_logger/logger.rb +45 -10
  28. data/lib/semantic_logger/metrics/new_relic.rb +1 -1
  29. data/lib/semantic_logger/metrics/statsd.rb +5 -1
  30. data/lib/semantic_logger/metrics/udp.rb +80 -0
  31. data/lib/semantic_logger/semantic_logger.rb +23 -27
  32. data/lib/semantic_logger/subscriber.rb +127 -0
  33. data/lib/semantic_logger/version.rb +1 -1
  34. data/test/appender/elasticsearch_test.rb +6 -4
  35. data/test/appender/file_test.rb +12 -12
  36. data/test/appender/honeybadger_test.rb +7 -1
  37. data/test/appender/http_test.rb +4 -2
  38. data/test/appender/mongodb_test.rb +1 -2
  39. data/test/appender/splunk_http_test.rb +8 -6
  40. data/test/appender/splunk_test.rb +48 -45
  41. data/test/appender/syslog_test.rb +3 -3
  42. data/test/appender/tcp_test.rb +68 -0
  43. data/test/appender/udp_test.rb +61 -0
  44. data/test/appender/wrapper_test.rb +5 -5
  45. data/test/concerns/compatibility_test.rb +6 -6
  46. data/test/debug_as_trace_logger_test.rb +2 -2
  47. data/test/loggable_test.rb +2 -2
  48. data/test/logger_test.rb +48 -45
  49. metadata +13 -3
  50. data/lib/semantic_logger/appender/base.rb +0 -101
@@ -11,7 +11,7 @@ end
11
11
  #
12
12
  # Example:
13
13
  # SemanticLogger.add_appender(appender: :new_relic)
14
- class SemanticLogger::Appender::NewRelic < SemanticLogger::Appender::Base
14
+ class SemanticLogger::Appender::NewRelic < SemanticLogger::Subscriber
15
15
  # Create Appender
16
16
  #
17
17
  # Parameters
@@ -39,7 +39,7 @@ class SemanticLogger::Appender::NewRelic < SemanticLogger::Appender::Base
39
39
 
40
40
  # Returns [Hash] of parameters to send to New Relic.
41
41
  def call(log, logger)
42
- h = log.to_h
42
+ h = log.to_h(host, application)
43
43
  h.delete(:time)
44
44
  h.delete(:exception)
45
45
  {metric: log.metric, custom_params: h}
@@ -18,8 +18,8 @@ end
18
18
  # scheme: :https,
19
19
  # index: 'main'
20
20
  # )
21
- class SemanticLogger::Appender::Splunk < SemanticLogger::Appender::Base
22
- attr_reader :config, :index, :service, :service_index
21
+ class SemanticLogger::Appender::Splunk < SemanticLogger::Subscriber
22
+ attr_reader :config, :index, :service, :service_index, :source_type
23
23
 
24
24
  # Write to Splunk.
25
25
  #
@@ -37,7 +37,7 @@ class SemanticLogger::Appender::Splunk < SemanticLogger::Appender::Base
37
37
  # Not required if username and password are supplied.
38
38
  #
39
39
  # :host [String]
40
- # Splunk host name.
40
+ # Splunk server host name.
41
41
  # Default: 'localhost'
42
42
  #
43
43
  # :port [Integer]
@@ -61,6 +61,17 @@ class SemanticLogger::Appender::Splunk < SemanticLogger::Appender::Base
61
61
  # :ssl_client_key [OpenSSL::PKey::RSA | OpenSSL::PKey::DSA]
62
62
  # Client key.
63
63
  #
64
+ # source_type: [String]
65
+ # Optional: Source type to display in Splunk
66
+ #
67
+ # application: [String]
68
+ # The :source forwarded to Splunk
69
+ # Default: SemanticLogger.application
70
+ #
71
+ # host: [String]
72
+ # Name of this host to appear in log messages.
73
+ # Default: SemanticLogger.host
74
+ #
64
75
  # level: [:trace | :debug | :info | :warn | :error | :fatal]
65
76
  # Override the log level for this appender.
66
77
  # Default: SemanticLogger.default_level
@@ -75,21 +86,16 @@ class SemanticLogger::Appender::Splunk < SemanticLogger::Appender::Base
75
86
  # regular expression. All other messages will be ignored.
76
87
  # Proc: Only include log messages where the supplied Proc returns true
77
88
  # The Proc must return true or false.
78
- def initialize(options, _deprecated_level = nil, &block)
89
+ def initialize(options = {}, _deprecated_level = nil, &block)
79
90
  @config = options.dup
80
91
  @config[:level] = _deprecated_level if _deprecated_level
81
92
  @index = @config.delete(:index) || 'main'
93
+ @source_type = options.delete(:source_type)
82
94
 
83
- options = {
84
- level: @config.delete(:level) || :error,
85
- formatter: @config.delete(:formatter),
86
- filter: @config.delete(:filter)
87
- }
88
-
89
- reopen
90
-
95
+ options = extract_subscriber_options!(@config)
91
96
  # Pass on the level and custom formatter if supplied
92
97
  super(options, &block)
98
+ reopen
93
99
  end
94
100
 
95
101
  # After forking an active process call #reopen to re-open
@@ -105,20 +111,25 @@ class SemanticLogger::Appender::Splunk < SemanticLogger::Appender::Base
105
111
  # Log the message to Splunk
106
112
  def log(log)
107
113
  return false unless should_log?(log)
108
-
109
- service_index.submit(log.message, formatter.call(log, self))
114
+ event = formatter.call(log, self)
115
+ service_index.submit(event.delete(:message), event)
110
116
  true
111
117
  end
112
118
 
113
- # Returns [String] JSON to send to Splunk
119
+ # Returns [Hash] To send to Splunk
114
120
  # For splunk format requirements see:
115
121
  # http://dev.splunk.com/view/event-collector/SP-CAAAE6P
116
- def call(log, _logger)
117
- h = log.to_h
118
- h.delete(:message)
119
- h.delete(:application)
120
- h.delete(:host)
122
+ def call(log, logger)
123
+ h = log.to_h(nil, nil)
121
124
  h.delete(:time)
122
- h
125
+ message = {
126
+ source: logger.application,
127
+ host: logger.host,
128
+ time: log.time.utc.to_f,
129
+ message: h.delete(:message),
130
+ event: h
131
+ }
132
+ message[:source_type] = source_type if source_type
133
+ message
123
134
  end
124
135
  end
@@ -83,9 +83,7 @@ class SemanticLogger::Appender::SplunkHttp < SemanticLogger::Appender::Http
83
83
  # For splunk format requirements see:
84
84
  # http://dev.splunk.com/view/event-collector/SP-CAAAE6P
85
85
  def call(log, logger)
86
- h = log.to_h
87
- h.delete(:application)
88
- h.delete(:host)
86
+ h = log.to_h(nil, nil)
89
87
  h.delete(:time)
90
88
  message = {
91
89
  source: logger.application,
@@ -20,9 +20,7 @@ require 'socket'
20
20
  # )
21
21
  module SemanticLogger
22
22
  module Appender
23
- class Syslog < SemanticLogger::Appender::Base
24
-
25
- attr_reader :remote_syslog, :url, :server, :port, :protocol, :facility, :host, :application
23
+ class Syslog < SemanticLogger::Subscriber
26
24
 
27
25
  # Default mapping of ruby log levels to syslog log levels
28
26
  #
@@ -42,6 +40,7 @@ module SemanticLogger
42
40
  debug: ::Syslog::LOG_INFO,
43
41
  trace: ::Syslog::LOG_DEBUG
44
42
  }
43
+ attr_reader :remote_syslog, :url, :server, :port, :protocol, :facility
45
44
 
46
45
  # Create a Syslog appender instance.
47
46
  #
@@ -149,7 +148,6 @@ module SemanticLogger
149
148
  # Default: :syslog
150
149
  def initialize(options = {}, &block)
151
150
  options = options.dup
152
- @application = options.delete(:application) || options.delete(:ident) || 'ruby'
153
151
  @options = options.delete(:options) || (::Syslog::LOG_PID | ::Syslog::LOG_CONS)
154
152
  @facility = options.delete(:facility) || ::Syslog::LOG_USER
155
153
  level_map = options.delete(:level_map)
@@ -159,7 +157,6 @@ module SemanticLogger
159
157
  @protocol = (uri.scheme || :syslog).to_sym
160
158
  @port = uri.port || 514
161
159
  @server = 'localhost' if @protocol == :syslog
162
- @host = options.delete(:host) || options.delete(:local_hostname) || SemanticLogger.host
163
160
  @tcp_client_options = options.delete(:tcp_client)
164
161
 
165
162
  raise "Unknown protocol #{@protocol}!" unless [:syslog, :tcp, :udp].include?(@protocol)
@@ -187,9 +184,8 @@ module SemanticLogger
187
184
  end
188
185
  end
189
186
 
190
- reopen
191
-
192
187
  super(options, &block)
188
+ reopen
193
189
  end
194
190
 
195
191
  # After forking an active process call #reopen to re-open
@@ -197,7 +193,7 @@ module SemanticLogger
197
193
  def reopen
198
194
  case @protocol
199
195
  when :syslog
200
- ::Syslog.open(@application, @options, @facility)
196
+ ::Syslog.open(application, @options, @facility)
201
197
  when :tcp
202
198
  # Use the local logger for @remote_syslog so errors with the remote logger can be recorded locally.
203
199
  @tcp_client_options[:logger] = SemanticLogger::Logger.logger
@@ -264,13 +260,32 @@ module SemanticLogger
264
260
  message
265
261
  end
266
262
 
263
+ private
264
+
265
+ # Extract Syslog formatter options
266
+ def format_options(options, protocol, &block)
267
+ opts = options.delete(:options)
268
+ facility = options.delete(:facility)
269
+ level_map = options.delete(:level_map)
270
+ if formatter = options.delete(:formatter)
271
+ extract_formatter(formatter)
272
+ else
273
+ case protocol
274
+ when :syslog
275
+ extract_formatter(syslog: {options: opts, facility: facility, level_map: level_map})
276
+ when :tcp, :udp
277
+ extract_formatter(syslog: {options: opts, facility: facility, level_map: level_map})
278
+ end
279
+ end
280
+ end
281
+
267
282
  # Format the syslog packet so it can be sent over TCP or UDP
268
283
  def syslog_packet_formatter(log)
269
284
  packet = SyslogProtocol::Packet.new
270
- packet.hostname = @host
285
+ packet.hostname = host
271
286
  packet.facility = @facility
272
287
  packet.severity = @level_map[log.level]
273
- packet.tag = @application
288
+ packet.tag = application.gsub(' ', '')
274
289
  packet.content = formatter.call(log, self)
275
290
  packet.time = log.time
276
291
  packet.to_s
@@ -0,0 +1,231 @@
1
+ begin
2
+ require 'net/tcp_client'
3
+ rescue LoadError
4
+ raise 'Gem net_tcp_client is required for logging over TCP. Please add the gem "net_tcp_client" to your Gemfile.'
5
+ end
6
+
7
+ raise 'Net::TCPClient v2.0 or greater is required to log over TCP' unless Net::TCPClient::VERSION.to_f >= 2.0
8
+
9
+ module SemanticLogger
10
+ module Appender
11
+ # TCP log appender.
12
+ #
13
+ # Log to a server over a TCP Socket.
14
+ # By default messages are in JSON format.
15
+ #
16
+ # Features:
17
+ # * JSON Formatted messages.
18
+ # * SSL encryption.
19
+ # * Transparently reconnect when a connection is lost.
20
+ #
21
+ # Example:
22
+ # SemanticLogger.add_appender(
23
+ # appender: :tcp,
24
+ # server: 'server:3300',
25
+ # )
26
+ #
27
+ # Example, with connection retry options:
28
+ # SemanticLogger.add_appender(
29
+ # appender: :tcp,
30
+ # server: 'server:3300',
31
+ # connect_retry_interval: 0.1,
32
+ # connect_retry_count: 5
33
+ # )
34
+ #
35
+ # Example, with SSL enabled:
36
+ # SemanticLogger.add_appender(
37
+ # appender: :tcp,
38
+ # server: 'server:3300',
39
+ # ssl: true
40
+ # )
41
+ #
42
+ class Tcp < SemanticLogger::Subscriber
43
+ attr_accessor :separator
44
+ attr_reader :tcp_client
45
+
46
+ # Create TCP log appender.
47
+ #
48
+ # Net::TCPClient Parameters:
49
+ # :server [String]
50
+ # URL of the server to connect to with port number
51
+ # 'localhost:2000'
52
+ # '192.168.1.10:80'
53
+ #
54
+ # :servers [Array of String]
55
+ # Array of URL's of servers to connect to with port numbers
56
+ # ['server1:2000', 'server2:2000']
57
+ #
58
+ # The second server will only be attempted once the first server
59
+ # cannot be connected to or has timed out on connect
60
+ # A read failure or timeout will not result in switching to the second
61
+ # server, only a connection failure or during an automatic reconnect
62
+ #
63
+ # :connect_timeout [Float]
64
+ # Time in seconds to timeout when trying to connect to the server
65
+ # A value of -1 will cause the connect wait time to be infinite
66
+ # Default: Half of the :read_timeout ( 30 seconds )
67
+ #
68
+ # :read_timeout [Float]
69
+ # Time in seconds to timeout on read
70
+ # Can be overridden by supplying a timeout in the read call
71
+ # Default: 60
72
+ #
73
+ # :write_timeout [Float]
74
+ # Time in seconds to timeout on write
75
+ # Can be overridden by supplying a timeout in the write call
76
+ # Default: 60
77
+ #
78
+ # :log_level [Symbol]
79
+ # Optional: Set the logging level for the TCPClient
80
+ # Any valid SemanticLogger log level:
81
+ # :trace, :debug, :info, :warn, :error, :fatal
82
+ # Default: SemanticLogger.default_level
83
+ #
84
+ # :buffered [Boolean]
85
+ # Whether to use Nagle's Buffering algorithm (http://en.wikipedia.org/wiki/Nagle's_algorithm)
86
+ # Recommend disabling for RPC style invocations where we don't want to wait for an
87
+ # ACK from the server before sending the last partial segment
88
+ # Buffering is recommended in a browser or file transfer style environment
89
+ # where multiple sends are expected during a single response
90
+ # Default: true
91
+ #
92
+ # :connect_retry_count [Fixnum]
93
+ # Number of times to retry connecting when a connection fails
94
+ # Default: 10
95
+ #
96
+ # :connect_retry_interval [Float]
97
+ # Number of seconds between connection retry attempts after the first failed attempt
98
+ # Default: 0.5
99
+ #
100
+ # :retry_count [Fixnum]
101
+ # Number of times to retry when calling #retry_on_connection_failure
102
+ # This is independent of :connect_retry_count which still applies with
103
+ # connection failures. This retry controls upto how many times to retry the
104
+ # supplied block should a connection failure occurr during the block
105
+ # Default: 3
106
+ #
107
+ # :on_connect [Proc]
108
+ # Directly after a connection is established and before it is made available
109
+ # for use this Block is invoked.
110
+ # Typical Use Cases:
111
+ # - Initialize per connection session sequence numbers
112
+ # - Pass any authentication information to the server
113
+ # - Perform a handshake with the server
114
+ #
115
+ # :policy [Symbol|Proc]
116
+ # Specify the policy to use when connecting to servers.
117
+ # :ordered
118
+ # Select a server in the order supplied in the array, with the first
119
+ # having the highest priority. The second server will only be connected
120
+ # to if the first server is unreachable
121
+ # :random
122
+ # Randomly select a server from the list every time a connection
123
+ # is established, including during automatic connection recovery.
124
+ # :ping_time
125
+ # FUTURE - Not implemented yet - Pull request anyone?
126
+ # The server with the lowest ping time will be tried first
127
+ # Proc:
128
+ # When a Proc is supplied, it will be called passing in the list
129
+ # of servers. The Proc must return one server name
130
+ # Example:
131
+ # :policy => Proc.new do |servers|
132
+ # servers.last
133
+ # end
134
+ # Default: :ordered
135
+ #
136
+ # :close_on_error [True|False]
137
+ # To prevent the connection from going into an inconsistent state
138
+ # automatically close the connection if an error occurs
139
+ # This includes a Read Timeout
140
+ # Default: true
141
+ #
142
+ # Appender Parameters:
143
+ # separator: [String]
144
+ # Separator between every message
145
+ # Default: "\n"
146
+ # Note: The separator should not be something that could be output in the formatted log message.
147
+ #
148
+ # Common Appender Parameters:
149
+ # application: [String]
150
+ # Name of this application to appear in log messages.
151
+ # Default: SemanticLogger.application
152
+ #
153
+ # host: [String]
154
+ # Name of this host to appear in log messages.
155
+ # Default: SemanticLogger.host
156
+ #
157
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
158
+ # Override the log level for this appender.
159
+ # Default: SemanticLogger.default_level
160
+ #
161
+ # formatter: [Object|Proc]
162
+ # An instance of a class that implements #call, or a Proc to be used to format
163
+ # the output from this appender
164
+ # Default: Use the built-in formatter (See: #call)
165
+ #
166
+ # filter: [Regexp|Proc]
167
+ # RegExp: Only include log messages where the class name matches the supplied.
168
+ # regular expression. All other messages will be ignored.
169
+ # Proc: Only include log messages where the supplied Proc returns true
170
+ # The Proc must return true or false.
171
+ # Example:
172
+ # SemanticLogger.add_appender(
173
+ # appender: :tcp,
174
+ # server: 'server:3300'
175
+ # )
176
+ #
177
+ # Example, with connection retry options:
178
+ # SemanticLogger.add_appender(
179
+ # appender: :tcp,
180
+ # server: 'server:3300',
181
+ # connect_retry_interval: 0.1,
182
+ # connect_retry_count: 5
183
+ # )
184
+ def initialize(options = {}, &block)
185
+ @options = options.dup
186
+ @separator = @options.delete(:separator) || "\n"
187
+
188
+ # 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'
191
+
192
+ options = extract_subscriber_options!(@options)
193
+ super(options, &block)
194
+ reopen
195
+ end
196
+
197
+ # After forking an active process call #reopen to re-open
198
+ # open the handles to resources
199
+ def reopen
200
+ close
201
+ @tcp_client = Net::TCPClient.new(@options)
202
+ end
203
+
204
+ # Write the log using the specified protocol and server.
205
+ def log(log)
206
+ return false unless should_log?(log)
207
+
208
+ @tcp_client.retry_on_connection_failure { @tcp_client.write("#{formatter.call(log, self)}#{separator}") }
209
+ true
210
+ end
211
+
212
+ # Flush is called by the semantic_logger during shutdown.
213
+ def flush
214
+ @tcp_client.flush if @tcp_client && @tcp_client.respond_to?(:flush)
215
+ end
216
+
217
+ # Close is called during shutdown, or with reopen
218
+ def close
219
+ @tcp_client.close if @tcp_client
220
+ end
221
+
222
+ private
223
+
224
+ # Returns [SemanticLogger::Formatters::Default] formatter default for this Appender
225
+ def default_formatter
226
+ SemanticLogger::Formatters::Json.new
227
+ end
228
+
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,106 @@
1
+ require 'socket'
2
+ module SemanticLogger
3
+ module Appender
4
+ # UDP log appender.
5
+ #
6
+ # Write log messages to UDP.
7
+ # By default messages are in JSON format.
8
+ #
9
+ # Example:
10
+ # SemanticLogger.add_appender(
11
+ # appender: :udp,
12
+ # server: 'server:3300',
13
+ # )
14
+ class Udp < SemanticLogger::Subscriber
15
+ attr_accessor :server, :udp_flags
16
+ attr_reader :socket
17
+
18
+ # Create UDP log appender.
19
+ #
20
+ # server: [String]
21
+ # URL of the server to write UDP messages to.
22
+ #
23
+ # udp_flags: [Integer]
24
+ # Should be a bitwise OR of Socket::MSG_* constants.
25
+ # Default: 0
26
+ #
27
+ # Common Appender Parameters:
28
+ # application: [String]
29
+ # Name of this application to appear in log messages.
30
+ # Default: SemanticLogger.application
31
+ #
32
+ # host: [String]
33
+ # Name of this host to appear in log messages.
34
+ # Default: SemanticLogger.host
35
+ #
36
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
37
+ # Override the log level for this appender.
38
+ # Default: SemanticLogger.default_level
39
+ #
40
+ # formatter: [Object|Proc]
41
+ # An instance of a class that implements #call, or a Proc to be used to format
42
+ # the output from this appender
43
+ # Default: Use the built-in formatter (See: #call)
44
+ #
45
+ # filter: [Regexp|Proc]
46
+ # RegExp: Only include log messages where the class name matches the supplied.
47
+ # regular expression. All other messages will be ignored.
48
+ # Proc: Only include log messages where the supplied Proc returns true
49
+ # The Proc must return true or false.
50
+ #
51
+ # Limitations:
52
+ # * UDP packet size is limited by the connected network and any routers etc
53
+ # that the message has to traverse. See https://en.wikipedia.org/wiki/Maximum_transmission_unit
54
+ #
55
+ # Example:
56
+ # SemanticLogger.add_appender(
57
+ # appender: :udp,
58
+ # server: 'server:3300'
59
+ # )
60
+ def initialize(options = {}, &block)
61
+ options = options.dup
62
+ @server = options.delete(:server)
63
+ @udp_flags = options.delete(:udp_flags) || 0
64
+ raise(ArgumentError, 'Missing mandatory argument: :server') unless @server
65
+
66
+ super(options, &block)
67
+ reopen
68
+ end
69
+
70
+ # After forking an active process call #reopen to re-open
71
+ # open the handles to resources
72
+ def reopen
73
+ close
74
+ @socket = UDPSocket.new
75
+ host, port = server.split(':')
76
+ @socket.connect(host, port.to_i)
77
+ end
78
+
79
+ # Write the log using the specified protocol and server.
80
+ def log(log)
81
+ return false unless should_log?(log)
82
+
83
+ @socket.send(formatter.call(log, self), udp_flags)
84
+ true
85
+ end
86
+
87
+ # Flush is called by the semantic_logger during shutdown.
88
+ def flush
89
+ @socket.flush if @socket
90
+ end
91
+
92
+ # Close is called during shutdown, or with reopen
93
+ def close
94
+ @socket.close if @socket
95
+ end
96
+
97
+ private
98
+
99
+ # Returns [SemanticLogger::Formatters::Default] formatter default for this Appender
100
+ def default_formatter
101
+ SemanticLogger::Formatters::Json.new
102
+ end
103
+
104
+ end
105
+ end
106
+ end