semantic_logger 3.2.1 → 3.3.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.
- 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'
|