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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/semantic_logger.rb +22 -2
- data/lib/semantic_logger/appender/bugsnag.rb +2 -2
- data/lib/semantic_logger/appender/elasticsearch.rb +9 -1
- data/lib/semantic_logger/appender/file.rb +1 -1
- data/lib/semantic_logger/appender/graylog.rb +18 -21
- data/lib/semantic_logger/appender/honeybadger.rb +27 -12
- data/lib/semantic_logger/appender/http.rb +19 -11
- data/lib/semantic_logger/appender/mongodb.rb +23 -30
- data/lib/semantic_logger/appender/new_relic.rb +2 -2
- data/lib/semantic_logger/appender/splunk.rb +32 -21
- data/lib/semantic_logger/appender/splunk_http.rb +1 -3
- data/lib/semantic_logger/appender/syslog.rb +25 -10
- data/lib/semantic_logger/appender/tcp.rb +231 -0
- data/lib/semantic_logger/appender/udp.rb +106 -0
- data/lib/semantic_logger/appender/wrapper.rb +1 -1
- data/lib/semantic_logger/base.rb +9 -3
- data/lib/semantic_logger/formatters/base.rb +36 -0
- data/lib/semantic_logger/formatters/color.rb +13 -10
- data/lib/semantic_logger/formatters/default.rb +8 -5
- data/lib/semantic_logger/formatters/json.rb +10 -3
- data/lib/semantic_logger/formatters/raw.rb +13 -0
- data/lib/semantic_logger/formatters/syslog.rb +119 -0
- data/lib/semantic_logger/log.rb +7 -5
- data/lib/semantic_logger/loggable.rb +5 -0
- data/lib/semantic_logger/logger.rb +45 -10
- data/lib/semantic_logger/metrics/new_relic.rb +1 -1
- data/lib/semantic_logger/metrics/statsd.rb +5 -1
- data/lib/semantic_logger/metrics/udp.rb +80 -0
- data/lib/semantic_logger/semantic_logger.rb +23 -27
- data/lib/semantic_logger/subscriber.rb +127 -0
- data/lib/semantic_logger/version.rb +1 -1
- data/test/appender/elasticsearch_test.rb +6 -4
- data/test/appender/file_test.rb +12 -12
- data/test/appender/honeybadger_test.rb +7 -1
- data/test/appender/http_test.rb +4 -2
- data/test/appender/mongodb_test.rb +1 -2
- data/test/appender/splunk_http_test.rb +8 -6
- data/test/appender/splunk_test.rb +48 -45
- data/test/appender/syslog_test.rb +3 -3
- data/test/appender/tcp_test.rb +68 -0
- data/test/appender/udp_test.rb +61 -0
- data/test/appender/wrapper_test.rb +5 -5
- data/test/concerns/compatibility_test.rb +6 -6
- data/test/debug_as_trace_logger_test.rb +2 -2
- data/test/loggable_test.rb +2 -2
- data/test/logger_test.rb +48 -45
- metadata +13 -3
- data/lib/semantic_logger/appender/base.rb +0 -101
data/lib/semantic_logger/base.rb
CHANGED
@@ -211,9 +211,15 @@ module SemanticLogger
|
|
211
211
|
# or Proc
|
212
212
|
raise ':filter must be a Regexp or Proc' unless filter.nil? || filter.is_a?(Regexp) || filter.is_a?(Proc)
|
213
213
|
|
214
|
-
@filter
|
215
|
-
@name
|
216
|
-
|
214
|
+
@filter = filter.is_a?(Regexp) ? filter.freeze : filter
|
215
|
+
@name = klass.is_a?(String) ? klass : klass.name
|
216
|
+
if level.nil?
|
217
|
+
# Allow the global default level to determine this loggers log level
|
218
|
+
@level_index = nil
|
219
|
+
@level = nil
|
220
|
+
else
|
221
|
+
self.level = level
|
222
|
+
end
|
217
223
|
end
|
218
224
|
|
219
225
|
# Return the level index for fast comparisons
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SemanticLogger
|
2
|
+
module Formatters
|
3
|
+
class Base
|
4
|
+
attr_accessor :time_format, :precision, :log_host, :log_application
|
5
|
+
|
6
|
+
# Parameters
|
7
|
+
# time_format: [String|Symbol|nil]
|
8
|
+
# See Time#strftime for the format of this string
|
9
|
+
# :iso_8601 Outputs an ISO8601 Formatted timestamp
|
10
|
+
# nil: Returns Empty string for time ( no time is output ).
|
11
|
+
# Default: '%Y-%m-%d %H:%M:%S.%6N'
|
12
|
+
def initialize(options = {})
|
13
|
+
options = options.dup
|
14
|
+
@precision = defined?(JRuby) ? 3 : 6
|
15
|
+
default_format = "%Y-%m-%d %H:%M:%S.%#{precision}N"
|
16
|
+
@time_format = options.has_key?(:time_format) ? options.delete(:time_format) : default_format
|
17
|
+
@log_host = options.has_key?(:log_host) ? options.delete(:log_host) : true
|
18
|
+
@log_application = options.has_key?(:log_application) ? options.delete(:log_application) : true
|
19
|
+
raise(ArgumentError, "Unknown options: #{options.inspect}") if options.size > 0
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return the Time as a formatted string
|
23
|
+
def format_time(time)
|
24
|
+
case time_format
|
25
|
+
when :iso_8601
|
26
|
+
time.utc.iso8601(precision)
|
27
|
+
when nil
|
28
|
+
''
|
29
|
+
else
|
30
|
+
time.strftime(time_format)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -6,17 +6,18 @@ end
|
|
6
6
|
|
7
7
|
module SemanticLogger
|
8
8
|
module Formatters
|
9
|
-
class Color
|
9
|
+
class Color < Base
|
10
10
|
# Parameters:
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
11
|
+
# ap: Any valid AwesomePrint option for rendering data.
|
12
|
+
# These options can also be changed be creating a `~/.aprc` file.
|
13
|
+
# See: https://github.com/michaeldv/awesome_print
|
14
14
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
def initialize(options={})
|
18
|
-
|
19
|
-
@ai_options
|
15
|
+
# Note: The option :multiline is set to false if not supplied.
|
16
|
+
# Note: Has no effect if Awesome Print is not installed.
|
17
|
+
def initialize(options = {})
|
18
|
+
options = options.dup
|
19
|
+
@ai_options = options.delete(:ap) || {multiline: false}
|
20
|
+
super(options)
|
20
21
|
end
|
21
22
|
|
22
23
|
# Adds color to the default log formatter
|
@@ -26,8 +27,10 @@ module SemanticLogger
|
|
26
27
|
colors = SemanticLogger::AnsiColors
|
27
28
|
level_color = colors::LEVEL_MAP[log.level]
|
28
29
|
|
30
|
+
message = time_format.nil? ? '' : "#{format_time(log.time)} "
|
31
|
+
|
29
32
|
# Header with date, time, log level and process info
|
30
|
-
message
|
33
|
+
message << "#{level_color}#{log.level_to_s}#{colors::CLEAR} [#{log.process_info}]"
|
31
34
|
|
32
35
|
# Tags
|
33
36
|
message << ' ' << log.tags.collect { |tag| "[#{level_color}#{tag}#{colors::CLEAR}]" }.join(' ') if log.tags && (log.tags.size > 0)
|
@@ -1,12 +1,15 @@
|
|
1
1
|
module SemanticLogger
|
2
2
|
module Formatters
|
3
|
-
class Default
|
4
|
-
# Default log
|
3
|
+
class Default < Base
|
4
|
+
# Default text log format
|
5
5
|
# Generates logs of the form:
|
6
|
-
# 2011-07-19 14:36:15.
|
6
|
+
# 2011-07-19 14:36:15.660235 D [1149:ScriptThreadProcess] Rails -- Hello World
|
7
7
|
def call(log, logger)
|
8
|
-
#
|
9
|
-
message =
|
8
|
+
# Date & time
|
9
|
+
message = time_format.nil? ? '' : "#{format_time(log.time)} "
|
10
|
+
|
11
|
+
# Log level and process info
|
12
|
+
message << "#{log.level_to_s} [#{log.process_info}]"
|
10
13
|
|
11
14
|
# Tags
|
12
15
|
message << ' ' << log.tags.collect { |tag| "[#{tag}]" }.join(' ') if log.tags && (log.tags.size > 0)
|
@@ -1,12 +1,19 @@
|
|
1
1
|
require 'json'
|
2
2
|
module SemanticLogger
|
3
3
|
module Formatters
|
4
|
-
class Json
|
4
|
+
class Json < Raw
|
5
|
+
# Default JSON time format is ISO8601
|
6
|
+
def initialize(options = {})
|
7
|
+
options = options.dup
|
8
|
+
options[:time_format] = :iso_8601 unless options.has_key?(:time_format)
|
9
|
+
super(options)
|
10
|
+
end
|
11
|
+
|
5
12
|
# Returns log messages in JSON format
|
6
13
|
def call(log, logger)
|
7
|
-
h = log
|
14
|
+
h = super(log, logger)
|
8
15
|
h.delete(:time)
|
9
|
-
h[:timestamp] = log.time
|
16
|
+
h[:timestamp] = format_time(log.time)
|
10
17
|
h.to_json
|
11
18
|
end
|
12
19
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'json'
|
2
|
+
module SemanticLogger
|
3
|
+
module Formatters
|
4
|
+
class Raw < Base
|
5
|
+
# Returns log messages in Hash format
|
6
|
+
def call(log, logger)
|
7
|
+
log.to_h(log_host ? logger.host : nil, log_application ? logger.application : nil)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,119 @@
|
|
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 Syslog < Default
|
10
|
+
attr_accessor :level_map, :options, :facility
|
11
|
+
|
12
|
+
# Default mapping of ruby log levels to syslog log levels
|
13
|
+
#
|
14
|
+
# ::Syslog::LOG_EMERG - "System is unusable"
|
15
|
+
# ::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
|
30
|
+
|
31
|
+
# Create a Syslog Log Formatter
|
32
|
+
#
|
33
|
+
# 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
|
+
# facility: [Integer]
|
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
|
+
#
|
71
|
+
# level_map: [Hash]
|
72
|
+
# 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)
|
97
|
+
end
|
98
|
+
|
99
|
+
def call(log, logger)
|
100
|
+
message = super(log, logger)
|
101
|
+
create_syslog_packet(log, message)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Create Syslog Packet
|
105
|
+
def create_syslog_packet(log, message)
|
106
|
+
packet = SyslogProtocol::Packet.new
|
107
|
+
packet.hostname = host
|
108
|
+
packet.facility = facility
|
109
|
+
packet.tag = application.gsub(' ', '')
|
110
|
+
packet.content = message
|
111
|
+
packet.time = log.time
|
112
|
+
packet.severity = level_map[log.level]
|
113
|
+
packet.to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
data/lib/semantic_logger/log.rb
CHANGED
@@ -163,23 +163,23 @@ module SemanticLogger
|
|
163
163
|
if defined? JRuby
|
164
164
|
# Return the Time as a formatted string
|
165
165
|
# JRuby only supports time in ms
|
166
|
+
# DEPRECATED
|
166
167
|
def formatted_time
|
167
168
|
"#{time.strftime('%Y-%m-%d %H:%M:%S')}.#{'%03d' % (time.usec/1000)}"
|
168
169
|
end
|
169
170
|
else
|
170
171
|
# Return the Time as a formatted string
|
171
172
|
# Ruby MRI supports micro seconds
|
173
|
+
# DEPRECATED
|
172
174
|
def formatted_time
|
173
175
|
"#{time.strftime('%Y-%m-%d %H:%M:%S')}.#{'%06d' % (time.usec)}"
|
174
176
|
end
|
175
177
|
end
|
176
178
|
|
177
179
|
# Returns [Hash] representation of this log entry
|
178
|
-
def to_h
|
180
|
+
def to_h(host = SemanticLogger.host, application = SemanticLogger.application)
|
179
181
|
# Header
|
180
|
-
h
|
181
|
-
host: SemanticLogger.host,
|
182
|
-
application: SemanticLogger.application,
|
182
|
+
h = {
|
183
183
|
name: name,
|
184
184
|
pid: $$,
|
185
185
|
thread: thread_name,
|
@@ -187,7 +187,9 @@ module SemanticLogger
|
|
187
187
|
level: level,
|
188
188
|
level_index: level_index,
|
189
189
|
}
|
190
|
-
|
190
|
+
h[:host] = host if host
|
191
|
+
h[:application] = application if application
|
192
|
+
file, line = file_name_and_line
|
191
193
|
if file
|
192
194
|
h[:file] = file
|
193
195
|
h[:line] = line.to_i
|
@@ -34,6 +34,11 @@ module SemanticLogger
|
|
34
34
|
@semantic_logger ||= SemanticLogger[self]
|
35
35
|
end
|
36
36
|
|
37
|
+
# Replace instance class level logger
|
38
|
+
def self.logger=(logger)
|
39
|
+
@semantic_logger = logger
|
40
|
+
end
|
41
|
+
|
37
42
|
# Returns [SemanticLogger::Logger] instance level logger
|
38
43
|
def logger
|
39
44
|
@semantic_logger ||= self.class.logger
|
@@ -44,12 +44,28 @@ module SemanticLogger
|
|
44
44
|
# Flush all queued log entries disk, database, etc.
|
45
45
|
# All queued log messages are written and then each appender is flushed in turn
|
46
46
|
def self.flush
|
47
|
-
|
47
|
+
msg = "Flushing appenders with #{queue_size} log messages on the queue"
|
48
|
+
if queue_size > 1_000
|
49
|
+
logger.warn msg
|
50
|
+
elsif queue_size > 100
|
51
|
+
logger.info msg
|
52
|
+
elsif queue_size > 0
|
53
|
+
logger.trace msg
|
54
|
+
end
|
55
|
+
process_request(:flush)
|
56
|
+
end
|
48
57
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
58
|
+
# Close all appenders and flush any outstanding messages
|
59
|
+
def self.close
|
60
|
+
msg = "Closing appenders with #{queue_size} log messages on the queue"
|
61
|
+
if queue_size > 1_000
|
62
|
+
logger.warn msg
|
63
|
+
elsif queue_size > 100
|
64
|
+
logger.info msg
|
65
|
+
elsif queue_size > 0
|
66
|
+
logger.trace msg
|
67
|
+
end
|
68
|
+
process_request(:close)
|
53
69
|
end
|
54
70
|
|
55
71
|
@@lag_check_interval = 5000
|
@@ -73,10 +89,6 @@ module SemanticLogger
|
|
73
89
|
@@lag_threshold_s
|
74
90
|
end
|
75
91
|
|
76
|
-
def self.time_threshold_s=(time_threshold_s)
|
77
|
-
@@lag_threshold_s = time_threshold_s
|
78
|
-
end
|
79
|
-
|
80
92
|
# Allow the internal logger to be overridden from its default to STDERR
|
81
93
|
# Can be replaced with another Ruby logger or Rails logger, but never to
|
82
94
|
# SemanticLogger::Logger itself since it is for reporting problems
|
@@ -111,7 +123,7 @@ module SemanticLogger
|
|
111
123
|
appender = block || options.delete(:appender)
|
112
124
|
|
113
125
|
# Convert symbolized metrics appender to an actual object
|
114
|
-
appender =
|
126
|
+
appender = SemanticLogger.constantize_symbol(appender, 'SemanticLogger::Metrics').new(options) if appender.is_a?(Symbol)
|
115
127
|
|
116
128
|
raise('When supplying a metrics appender, it must support the #call method') unless appender.is_a?(Proc) || appender.respond_to?(:call)
|
117
129
|
(@@metric_subscribers ||= Concurrent::Array.new) << appender
|
@@ -202,6 +214,20 @@ module SemanticLogger
|
|
202
214
|
end
|
203
215
|
end
|
204
216
|
|
217
|
+
message[:reply_queue] << true if message[:reply_queue]
|
218
|
+
logger.trace 'Appender thread: All appenders flushed'
|
219
|
+
when :close
|
220
|
+
SemanticLogger.appenders.each do |appender|
|
221
|
+
begin
|
222
|
+
logger.trace "Appender thread: Closing appender: #{appender.name}"
|
223
|
+
appender.flush
|
224
|
+
appender.close
|
225
|
+
SemanticLogger.remove_appender(appender)
|
226
|
+
rescue Exception => exc
|
227
|
+
logger.error "Appender thread: Failed to close appender: #{appender.inspect}", exc
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
205
231
|
message[:reply_queue] << true if message[:reply_queue]
|
206
232
|
logger.trace 'Appender thread: All appenders flushed'
|
207
233
|
else
|
@@ -242,5 +268,14 @@ module SemanticLogger
|
|
242
268
|
end
|
243
269
|
end
|
244
270
|
|
271
|
+
# Close all appenders and flush any outstanding messages
|
272
|
+
def self.process_request(command)
|
273
|
+
return false unless appender_thread_active?
|
274
|
+
|
275
|
+
reply_queue = Queue.new
|
276
|
+
queue << {command: command, reply_queue: reply_queue}
|
277
|
+
reply_queue.pop
|
278
|
+
end
|
279
|
+
|
245
280
|
end
|
246
281
|
end
|
@@ -7,7 +7,7 @@ end
|
|
7
7
|
|
8
8
|
module SemanticLogger
|
9
9
|
module Metrics
|
10
|
-
class Statsd
|
10
|
+
class Statsd < Subscriber
|
11
11
|
# Create Statsd metrics subscriber
|
12
12
|
#
|
13
13
|
# Parameters:
|
@@ -18,6 +18,10 @@ module SemanticLogger
|
|
18
18
|
# Example, send all metrics to a particular namespace:
|
19
19
|
# udp://localhost:8125/namespace
|
20
20
|
# Default: udp://localhost:8125
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
# subscriber = SemanticLogger::Metrics::Statsd.new(url: 'udp://localhost:8125')
|
24
|
+
# SemanticLogger.on_metric(subscriber)
|
21
25
|
def initialize(options = {})
|
22
26
|
options = options.dup
|
23
27
|
@url = options.delete(:url) || 'udp://localhost:8125'
|