semantic_logger 2.21.0 → 3.0.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.
@@ -1,12 +1,17 @@
1
- require 'splunk-sdk-ruby'
1
+ begin
2
+ require 'splunk-sdk-ruby'
3
+ rescue LoadError
4
+ raise 'Gem splunk-sdk-ruby is required for logging to Splunk. Please add the gem "splunk-sdk-ruby" to your Gemfile.'
5
+ end
2
6
 
3
- # Note: Not recommended to use the colorized formatter.
7
+ # Note: This appender is Deprecated. Use: SemanticLogger::Appender::SplunkHttp
4
8
  class SemanticLogger::Appender::Splunk < SemanticLogger::Appender::Base
5
9
  attr_reader :config, :index, :service, :service_index
6
10
 
7
- # Allow the level for this appender to be overwritten
8
- # Default: :error
11
+ # DEPRECATED, Please use SemanticLogger::Appender::SplunkHttp
9
12
  def initialize(options, level=:error, &block)
13
+ Kernel.warn('Splunk Appender is deprecated, please use SemanticLogger::Appender::SplunkHttp')
14
+
10
15
  # Parse input options for setting up splunk connection
11
16
  parse_options(options)
12
17
 
@@ -31,7 +36,7 @@ class SemanticLogger::Appender::Splunk < SemanticLogger::Appender::Base
31
36
  # Ensure minimum log level is met, and check filter
32
37
  return false if (level_index > (log.level_index || 0)) || !include_message?(log)
33
38
  # Submit the log message
34
- @service_index.submit(formatter.call(log))
39
+ @service_index.submit(formatter.call(log, self))
35
40
  true
36
41
  end
37
42
 
@@ -0,0 +1,99 @@
1
+ # Splunk log appender.
2
+ #
3
+ # Use the newer, faster and more complete JSON over HTTP interface for Splunk.
4
+ #
5
+ # To configure Splunk to receive log messages via this appender:
6
+ # http://dev.splunk.com/view/event-collector/SP-CAAAE7F
7
+ #
8
+ # Example
9
+ # appender = SemanticLogger::Appender::SplunkHttp.new(
10
+ # url: 'http://localhost:8080',
11
+ # token: '70CA900C-3D7E-42A4-9C79-7975D1C422A8'
12
+ # )
13
+ # SemanticLogger.add_appender(appender)
14
+ class SemanticLogger::Appender::SplunkHttp < SemanticLogger::Appender::Http
15
+ # Create Splunk appender over persistent HTTP(S)
16
+ #
17
+ # Parameters:
18
+ # token: [String]
19
+ # Token created in Splunk for this HTTP Appender
20
+ # Mandatory.
21
+ #
22
+ # source_type: [String]
23
+ # Optional: Source type to display in Splunk
24
+ #
25
+ # index: [String]
26
+ # Optional: Name of a valid index for this message in Splunk.
27
+ #
28
+ # url: [String]
29
+ # Valid URL to post to.
30
+ # Example: http://example.com
31
+ # To enable SSL include https in the URL.
32
+ # Example: https://example.com
33
+ # verify_mode will default: OpenSSL::SSL::VERIFY_PEER
34
+ #
35
+ # application: [String]
36
+ # Name of this application to appear in log messages.
37
+ # Default: SemanticLogger.application
38
+ #
39
+ # host: [String]
40
+ # Name of this host to appear in log messages.
41
+ # Default: SemanticLogger.host
42
+ #
43
+ # compress: [true|false]
44
+ # Whether to compress the JSON string with GZip.
45
+ # Default: true
46
+ #
47
+ # ssl: [Hash]
48
+ # Specific SSL options: For more details see NET::HTTP.start
49
+ # ca_file, ca_path, cert, cert_store, ciphers, key, open_timeout, read_timeout, ssl_timeout,
50
+ # ssl_version, use_ssl, verify_callback, verify_depth and verify_mode.
51
+ #
52
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
53
+ # Override the log level for this appender.
54
+ # Default: SemanticLogger.default_level
55
+ #
56
+ # filter: [Regexp|Proc]
57
+ # RegExp: Only include log messages where the class name matches the supplied.
58
+ # regular expression. All other messages will be ignored.
59
+ # Proc: Only include log messages where the supplied Proc returns true
60
+ # The Proc must return true or false.
61
+ def initialize(options, &block)
62
+ options = options.dup
63
+ @source_type = options.delete(:source_type)
64
+ @index = options.delete(:index)
65
+ token = options.delete(:token)
66
+ raise(ArgumentError, 'Missing mandatory parameter :token') unless token
67
+
68
+ # Splunk supports HTTP Compression, enable by default
69
+ options[:compress] = true unless options.key?(:compress)
70
+
71
+ super(options, &block)
72
+
73
+ @header['Authorization'] = "Splunk #{token}"
74
+ end
75
+
76
+ # Returns [String] JSON to send to Splunk
77
+ # For splunk format requirements see:
78
+ # http://dev.splunk.com/view/event-collector/SP-CAAAE6P
79
+ def default_formatter
80
+ Proc.new do |log, logger|
81
+ h = log.to_h
82
+ h.delete(:application)
83
+ h.delete(:host)
84
+ h.delete(:time)
85
+ message = {
86
+ source: logger.application,
87
+ host: logger.host,
88
+ time: log.time.utc.to_f,
89
+ event: h
90
+ }
91
+ message[:source_type] = @source_type if @source_type
92
+ message[:index] = @index if @index
93
+
94
+ # Render to JSON
95
+ message.to_json
96
+ end
97
+ end
98
+
99
+ end
@@ -1,41 +1,36 @@
1
- # syslog appender for SemanticLogger - Supports local and remote syslog (over TCP or UDP)
2
- #
3
- # Example 1
4
- # Log to the local syslog.
5
- #
6
- # require 'semantic_logger'
7
- # SemanticLogger.default_level = :trace
1
+ require 'syslog'
2
+ require 'uri'
3
+ require 'socket'
4
+
5
+ # Send log messages to local syslog, or remote syslog servers over TCP or UDP.
8
6
  #
9
- # syslog_appender = SemanticLogger::Appender::Syslog.new
10
- # SemanticLogger.add_appender(syslog_appender)
7
+ # Example: Log to a local Syslog daemon
8
+ # SemanticLogger.add_appender(SemanticLogger::Appender::Syslog.new)
11
9
  #
12
- # logger = SemanticLogger['SyslogAppenderExample']
13
- # logger.info "Info Hello! - This message should appear in the local syslog!"
10
+ # Example: Log to a remote Syslog server using TCP:
11
+ # appender = SemanticLogger::Appender::Syslog.new(
12
+ # url: 'tcp://myloghost:514'
13
+ # )
14
14
  #
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)/ }
15
17
  #
16
- # Example 2
17
- # Send to a remote syslog appender - myloghost - over TCP on port 514.
18
- # Tested with syslog-ng as part of an ELSA installation.
19
- # https://code.google.com/p/enterprise-log-search-and-archive/
18
+ # SemanticLogger.add_appender(appender)
20
19
  #
21
- # require 'semantic_logger'
22
- # # Only log warn and above messages to the remote syslog.
23
- # syslog_appender = SemanticLogger::Appender::Syslog.new(level: :warn, server: 'tcp://myloghost:514')
24
- # SemanticLogger.add_appender(syslog_appender)
20
+ # Example: Log to a remote Syslog server using UDP:
21
+ # appender = SemanticLogger::Appender::Syslog.new(
22
+ # url: 'udp://myloghost:514'
23
+ # )
25
24
  #
26
- # logger = SemanticLogger['SyslogAppenderExample']
27
- # logger.info "Info Hello! - The log level is too low and will not be logged."
28
- # logger.error "Error! Error! - This message should appear in the remote syslog!"
25
+ # # Optional: Add filter to exclude health_check, or other log entries
26
+ # appender.filter = Proc.new { |log| log.message !~ /(health_check|Not logged in)/ }
29
27
  #
30
- require 'syslog'
31
- require 'uri'
32
- require 'socket'
33
-
28
+ # SemanticLogger.add_appender(appender)
34
29
  module SemanticLogger
35
30
  module Appender
36
31
  class Syslog < SemanticLogger::Appender::Base
37
32
 
38
- attr_reader :remote_syslog, :server, :host, :port, :protocol, :facility, :local_hostname
33
+ attr_reader :remote_syslog, :url, :server, :port, :protocol, :facility, :host, :application
39
34
 
40
35
  # Default mapping of ruby log levels to syslog log levels
41
36
  #
@@ -56,14 +51,47 @@ module SemanticLogger
56
51
  trace: ::Syslog::LOG_DEBUG
57
52
  }
58
53
 
59
- # For more information on the Syslog constants used below see http://ruby-doc.org/stdlib-2.0.0/libdoc/syslog/rdoc/Syslog.html
54
+ # Create a Syslog appender instance.
55
+ #
60
56
  # Parameters
57
+ # url: [String]
58
+ # Default: 'syslog://localhost'
59
+ # For writing logs to a remote syslog server
60
+ # URL of server: protocol://host:port
61
+ # Uses port 514 by default for TCP and UDP.
62
+ # local syslog example: 'syslog://localhost'
63
+ # TCP example with default port: 'tcp://logger'
64
+ # TCP example with custom port: 'tcp://logger:8514'
65
+ # UDP example with default port: 'udp://logger'
66
+ # UDP example with custom port: 'udp://logger:8514'
67
+ # When using the :syslog protocol, logs will always be sent to the localhost syslog
61
68
  #
62
- # :ident [String]
63
- # Identity of the program
64
- # Default: 'ruby'
69
+ # host: [String]
70
+ # Host name to provide to the remote syslog.
71
+ # Default: SemanticLogger.host
65
72
  #
66
- # :options [Integer]
73
+ # tcp_client: [Hash]
74
+ # Default: {}
75
+ # Only used with the TCP protocol.
76
+ # Specify custom parameters to pass into Net::TCPClient.new
77
+ # For a list of options see the net_tcp_client documentation:
78
+ # https://www.omniref.com/ruby/gems/net_tcp_client/1.0.0/symbols/Net::TCPClient/initialize
79
+ #
80
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
81
+ # Override the log level for this appender.
82
+ # Default: SemanticLogger.default_level
83
+ #
84
+ # filter: [Regexp|Proc]
85
+ # RegExp: Only include log messages where the class name matches the supplied.
86
+ # regular expression. All other messages will be ignored.
87
+ # Proc: Only include log messages where the supplied Proc returns true
88
+ # The Proc must return true or false.
89
+ #
90
+ # application: [String]
91
+ # Identity of the program.
92
+ # Default: SemanticLogger.application
93
+ #
94
+ # options: [Integer]
67
95
  # Default: ::Syslog::LOG_PID | ::Syslog::LOG_CONS
68
96
  # Any of the following (options can be logically OR'd together)
69
97
  # ::Syslog::LOG_CONS
@@ -73,7 +101,7 @@ module SemanticLogger
73
101
  # ::Syslog::LOG_PERROR
74
102
  # ::Syslog::LOG_PID
75
103
  #
76
- # :facility [Integer]
104
+ # facility: [Integer]
77
105
  # Default: ::Syslog::LOG_USER
78
106
  # Type of program (can be logically OR'd together)
79
107
  # ::Syslog::LOG_AUTH
@@ -100,11 +128,7 @@ module SemanticLogger
100
128
  # ::Syslog::LOG_LOCAL6
101
129
  # ::Syslog::LOG_LOCAL7
102
130
  #
103
- # :level [Symbol]
104
- # Default: SemanticLogger's log level.
105
- # The minimum level at which this appender will write logs. Any log messages below this level will be ignored.
106
- #
107
- # :level_map [Hash]
131
+ # level_map: [Hash]
108
132
  # Supply a custom map of SemanticLogger levels to syslog levels.
109
133
  # For example, passing in { warn: ::Syslog::LOG_NOTICE }
110
134
  # would result in a log mapping that matches the default level map,
@@ -119,51 +143,28 @@ module SemanticLogger
119
143
  # debug: ::Syslog::LOG_INFO,
120
144
  # trace: ::Syslog::LOG_DEBUG
121
145
  # }
122
- #
123
- # :local_hostname [String]
124
- # Default: Socket.gethostname || `hostname`.strip
125
- # Hostname to provide to the remote syslog.
126
- #
127
- # :server [String]
128
- # Default: 'syslog://localhost'
129
- # For writing logs to a remote syslog server
130
- # URI of server: protocol://host:port
131
- # Uses port 514 by default for TCP and UDP.
132
- # local syslog example: 'syslog://localhost'
133
- # TCP example with default port: 'tcp://logger'
134
- # TCP example with custom port: 'tcp://logger:8514'
135
- # UDP example with default port: 'udp://logger'
136
- # UDP example with custom port: 'udp://logger:8514'
137
- # When using the :syslog protocol, logs will always be sent to the localhost syslog
138
- #
139
- # :tcp_client [Hash]
140
- # Default: {}
141
- # Only used with the TCP protocol.
142
- # Specify custom parameters to pass into Net::TCPClient.new
143
- # For a list of options see the net_tcp_client documentation:
144
- # https://www.omniref.com/ruby/gems/net_tcp_client/1.0.0/symbols/Net::TCPClient/initialize
145
- def initialize(params = {}, &block)
146
- params = params.dup
147
- @ident = params.delete(:ident) || 'ruby'
148
- @options = params.delete(:options) || (::Syslog::LOG_PID | ::Syslog::LOG_CONS)
149
- @facility = params.delete(:facility) || ::Syslog::LOG_USER
150
- filter = params.delete(:filter)
151
- level = params.delete(:level)
152
- level_map = params.delete(:level_map)
153
- @level_map = DEFAULT_LEVEL_MAP.dup
154
- @level_map.update(level_map) if level_map
155
- @server = params.delete(:server) || 'syslog://localhost'
156
- uri = URI(@server)
157
- @host = uri.host || 'localhost'
158
- @protocol = (uri.scheme || :syslog).to_sym
146
+ def initialize(options = {}, &block)
147
+ options = options.dup
148
+ level = options.delete(:level)
149
+ filter = options.delete(:filter)
150
+ @application = options.delete(:application) || options.delete(:ident) || 'ruby'
151
+ @options = options.delete(:options) || (::Syslog::LOG_PID | ::Syslog::LOG_CONS)
152
+ @facility = options.delete(:facility) || ::Syslog::LOG_USER
153
+ level_map = options.delete(:level_map)
154
+ @url = options.delete(:url) || options.delete(:server) || 'syslog://localhost'
155
+ uri = URI(@url)
156
+ @server = uri.host || 'localhost'
157
+ @protocol = (uri.scheme || :syslog).to_sym
158
+ @port = uri.port || 514
159
+ @server = 'localhost' if @protocol == :syslog
160
+ @host = options.delete(:host) || options.delete(:local_hostname) || SemanticLogger.host
161
+ @tcp_client_options = options.delete(:tcp_client)
162
+
159
163
  raise "Unknown protocol #{@protocol}!" unless [:syslog, :tcp, :udp].include?(@protocol)
160
- @host = 'localhost' if @protocol == :syslog
161
- @port = URI(@server).port || 514
162
- @local_hostname = params.delete(:local_hostname) || Socket.gethostname || `hostname`.strip
163
- @tcp_client_options = params.delete(:tcp_client)
164
+ raise(ArgumentError, "Unknown options: #{options.inspect}") if options.size > 0
164
165
 
165
- # Warn about any unknown configuration options.
166
- params.each_pair { |key, val| SemanticLogger::Logger.logger.warn "Ignoring unknown configuration option: #{key.inspect} => #{val.inspect}" }
166
+ @level_map = DEFAULT_LEVEL_MAP.dup
167
+ @level_map.update(level_map) if level_map
167
168
 
168
169
  # The syslog_protocol gem is required when logging over TCP or UDP.
169
170
  if [:tcp, :udp].include?(@protocol)
@@ -176,7 +177,7 @@ module SemanticLogger
176
177
  # The net_tcp_client gem is required when logging over TCP.
177
178
  if protocol == :tcp
178
179
  @tcp_client_options ||= {}
179
- @tcp_client_options[:server] = "#{@host}:#{@port}"
180
+ @tcp_client_options[:server] = "#{@server}:#{@port}"
180
181
  begin
181
182
  require 'net/tcp_client'
182
183
  rescue LoadError
@@ -195,7 +196,7 @@ module SemanticLogger
195
196
  def reopen
196
197
  case @protocol
197
198
  when :syslog
198
- ::Syslog.open(@ident, @options, @facility)
199
+ ::Syslog.open(@application, @options, @facility)
199
200
  when :tcp
200
201
  # Use the local logger for @remote_syslog so errors with the remote logger can be recorded locally.
201
202
  @tcp_client_options[:logger] = SemanticLogger::Logger.logger
@@ -207,7 +208,7 @@ module SemanticLogger
207
208
  end
208
209
  end
209
210
 
210
- # Write the log using the specified protocol and host.
211
+ # Write the log using the specified protocol and server.
211
212
  def log(log)
212
213
  # Ensure minimum log level is met, and check filter
213
214
  return false if (level_index > (log.level_index || 0)) || !include_message?(log)
@@ -215,12 +216,12 @@ module SemanticLogger
215
216
  case @protocol
216
217
  when :syslog
217
218
  # Since the Ruby Syslog API supports sprintf format strings, double up all existing '%'
218
- message = formatter.call(log).gsub '%', '%%'
219
+ message = formatter.call(log, self).gsub '%', '%%'
219
220
  ::Syslog.log @level_map[log.level], message
220
221
  when :tcp
221
222
  @remote_syslog.retry_on_connection_failure { @remote_syslog.write("#{syslog_packet_formatter(log)}\r\n") }
222
223
  when :udp
223
- @remote_syslog.send syslog_packet_formatter(log), 0, @host, @port
224
+ @remote_syslog.send syslog_packet_formatter(log), 0, @server, @port
224
225
  else
225
226
  raise "Unsupported protocol: #{protocol}"
226
227
  end
@@ -232,36 +233,48 @@ module SemanticLogger
232
233
  @remote_syslog.flush if @remote_syslog && @remote_syslog.respond_to?(:flush)
233
234
  end
234
235
 
235
- # Custom log formatter for syslog
236
+ # Custom log formatter for syslog.
237
+ # Only difference is the removal of the timestamp string since it is in the syslog packet.
236
238
  def default_formatter
237
239
  Proc.new do |log|
238
- tags = log.tags.collect { |tag| "[#{tag}]" }.join(" ") + " " if log.tags && (log.tags.size > 0)
240
+ # Header with date, time, log level and process info
241
+ entry = "#{log.level_to_s} [#{log.process_info}]"
239
242
 
240
- message = log.message.to_s
241
- message << ' -- ' << log.payload.inspect if log.payload
242
- log.each_exception do |exception, i|
243
- if i == 0
244
- message << ' -- '
245
- else
246
- message << "\nCause: "
247
- end
248
- message << "#{exception.class}: #{exception.message}\n#{(exception.backtrace || []).join("\n")}"
249
- end
243
+ # Tags
244
+ entry << ' ' << log.tags.collect { |tag| "[#{tag}]" }.join(' ') if log.tags && (log.tags.size > 0)
245
+
246
+ # Duration
247
+ entry << " (#{log.duration_human})" if log.duration
248
+
249
+ # Class / app name
250
+ entry << " #{log.name}"
250
251
 
251
- duration_str = log.duration ? "(#{'%.1f' % log.duration}ms) " : ''
252
+ # Log message
253
+ entry << " -- #{log.message}" if log.message
252
254
 
253
- "#{log.level.to_s[0..0].upcase} [#{$$}:#{log.thread_name}] #{tags}#{duration_str}#{log.name} -- #{message}"
255
+ # Payload
256
+ if payload = log.payload_to_s(false)
257
+ entry << ' -- ' << payload
258
+ end
259
+
260
+ # Exceptions
261
+ if log.exception
262
+ entry << " -- Exception: #{log.exception.class}: #{log.exception.message}\n"
263
+ entry << log.backtrace_to_s
264
+ end
265
+ entry
254
266
  end
255
267
  end
256
268
 
257
269
  # Format the syslog packet so it can be sent over TCP or UDP
258
270
  def syslog_packet_formatter(log)
259
271
  packet = SyslogProtocol::Packet.new
260
- packet.hostname = @local_hostname
272
+ packet.hostname = @host
261
273
  packet.facility = @facility
262
274
  packet.severity = @level_map[log.level]
263
- packet.tag = @ident
264
- packet.content = default_formatter.call(log)
275
+ packet.tag = @application
276
+ packet.content = default_formatter.call(log, self)
277
+ packet.time = log.time
265
278
  packet.to_s
266
279
  end
267
280
  end
@@ -1,19 +1,35 @@
1
- # Wrapper appender
1
+ # Send log messages to any standard Ruby logging class.
2
2
  #
3
- # Wraps the Rails log, log4r, or Ruby Logger with the SemanticLogger API's
3
+ # Forwards logging call to loggers such as Logger, log4r, etc.
4
4
  #
5
5
  module SemanticLogger
6
6
  module Appender
7
7
  class Wrapper < SemanticLogger::Appender::Base
8
8
  attr_reader :logger
9
9
 
10
- # Create a Logger or Rails Logger appender instance
10
+ # Forward all logging calls to the supplied logging instance.
11
+ #
12
+ # Parameters
13
+ # logger: [Object]
14
+ # Instance of an existing logger conforming to the Ruby Logger methods.
15
+ #
16
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
17
+ # Override the log level for this appender.
18
+ # Default: SemanticLogger.default_level
19
+ #
20
+ # filter: [Regexp|Proc]
21
+ # RegExp: Only include log messages where the class name matches the supplied.
22
+ # regular expression. All other messages will be ignored.
23
+ # Proc: Only include log messages where the supplied Proc returns true
24
+ # The Proc must return true or false.
11
25
  #
12
26
  # Ruby Logger
13
27
  # require 'logger'
14
28
  # require 'semantic_logger'
29
+ #
15
30
  # ruby_logger = Logger.new(STDOUT)
16
31
  # SemanticLogger.add_appender(ruby_logger)
32
+ #
17
33
  # logger = SemanticLogger['test']
18
34
  # logger.info('Hello World', some: :payload)
19
35
  #
@@ -25,26 +41,25 @@ module SemanticLogger
25
41
  # # Make ActiveRecord logging include its class name in every log entry
26
42
  # ActiveRecord::Base.logger = SemanticLogger['ActiveRecord']
27
43
  #
28
- # Note: Since the log level is controlled by setting the Ruby or Rails logger directly
29
- # the level is ignored for this appender
30
- def initialize(logger, filter=nil, &block)
31
- raise 'logger cannot be null when initiailizing the SemanticLogging::Appender::Wrapper' unless logger
44
+ # Install the `rails_semantic_logger` gem to replace the Rails logger with Semantic Logger.
45
+ def initialize(logger, level = nil, filter = nil, &block)
46
+ raise 'logger cannot be null when initializing the SemanticLogging::Appender::Wrapper' unless logger
32
47
  @logger = logger
33
48
 
34
49
  # Set the formatter to the supplied block
35
50
  @formatter = block || self.default_formatter
36
- super(nil, filter, &block)
51
+ super(level, filter, &block)
37
52
  end
38
53
 
39
54
  # Pass log calls to the underlying Rails, log4j or Ruby logger
40
55
  # trace entries are mapped to debug since :trace is not supported by the
41
56
  # Ruby or Rails Loggers
42
57
  def log(log)
43
- # Check filter
44
- return false unless include_message?(log)
58
+ # Ensure minimum log level is met, and check filter
59
+ return false if (level_index > (log.level_index || 0)) || !include_message?(log)
45
60
 
46
61
  # Underlying wrapper logger implements log level, so don't check here
47
- @logger.send(log.level == :trace ? :debug : log.level, @formatter.call(log))
62
+ @logger.send(log.level == :trace ? :debug : log.level, @formatter.call(log, self))
48
63
  true
49
64
  end
50
65