semantic_logger 4.0.0 → 4.1.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 +55 -8
- data/lib/semantic_logger.rb +1 -2
- data/lib/semantic_logger/ansi_colors.rb +1 -2
- data/lib/semantic_logger/appender.rb +17 -15
- data/lib/semantic_logger/appender/bugsnag.rb +5 -4
- data/lib/semantic_logger/appender/elasticsearch.rb +102 -16
- data/lib/semantic_logger/appender/elasticsearch_http.rb +76 -0
- data/lib/semantic_logger/appender/file.rb +9 -25
- data/lib/semantic_logger/appender/graylog.rb +43 -38
- data/lib/semantic_logger/appender/honeybadger.rb +3 -5
- data/lib/semantic_logger/appender/http.rb +12 -15
- data/lib/semantic_logger/appender/kafka.rb +183 -0
- data/lib/semantic_logger/appender/mongodb.rb +3 -3
- data/lib/semantic_logger/appender/new_relic.rb +3 -7
- data/lib/semantic_logger/appender/sentry.rb +2 -5
- data/lib/semantic_logger/appender/splunk.rb +7 -10
- data/lib/semantic_logger/appender/splunk_http.rb +16 -16
- data/lib/semantic_logger/appender/syslog.rb +43 -122
- data/lib/semantic_logger/appender/tcp.rb +28 -9
- data/lib/semantic_logger/appender/udp.rb +4 -7
- data/lib/semantic_logger/appender/wrapper.rb +3 -7
- data/lib/semantic_logger/base.rb +47 -7
- data/lib/semantic_logger/formatters/base.rb +29 -10
- data/lib/semantic_logger/formatters/color.rb +75 -45
- data/lib/semantic_logger/formatters/default.rb +53 -28
- data/lib/semantic_logger/formatters/json.rb +7 -8
- data/lib/semantic_logger/formatters/raw.rb +97 -1
- data/lib/semantic_logger/formatters/syslog.rb +46 -80
- data/lib/semantic_logger/formatters/syslog_cee.rb +57 -0
- data/lib/semantic_logger/log.rb +17 -67
- data/lib/semantic_logger/logger.rb +17 -27
- data/lib/semantic_logger/processor.rb +70 -46
- data/lib/semantic_logger/semantic_logger.rb +130 -69
- data/lib/semantic_logger/subscriber.rb +18 -32
- data/lib/semantic_logger/version.rb +1 -1
- data/test/appender/elasticsearch_http_test.rb +75 -0
- data/test/appender/elasticsearch_test.rb +34 -27
- data/test/appender/file_test.rb +2 -2
- data/test/appender/honeybadger_test.rb +1 -1
- data/test/appender/kafka_test.rb +36 -0
- data/test/appender/new_relic_test.rb +1 -1
- data/test/appender/sentry_test.rb +1 -1
- data/test/appender/syslog_test.rb +2 -2
- data/test/appender/wrapper_test.rb +1 -1
- data/test/formatters/color_test.rb +154 -0
- data/test/formatters/default_test.rb +176 -0
- data/test/loggable_test.rb +1 -1
- data/test/logger_test.rb +47 -4
- data/test/measure_test.rb +2 -2
- data/test/semantic_logger_test.rb +34 -6
- data/test/test_helper.rb +8 -0
- metadata +14 -3
@@ -57,14 +57,11 @@ module SemanticLogger
|
|
57
57
|
# appender: :udp,
|
58
58
|
# server: 'server:3300'
|
59
59
|
# )
|
60
|
-
def initialize(
|
61
|
-
@
|
62
|
-
|
63
|
-
@server = options.delete(:server)
|
64
|
-
@udp_flags = options.delete(:udp_flags) || 0
|
65
|
-
raise(ArgumentError, 'Missing mandatory argument: :server') unless @server
|
60
|
+
def initialize(server:, udp_flags: 0, level: nil, formatter: nil, filter: nil, application: nil, host: nil, &block)
|
61
|
+
@server = server
|
62
|
+
@udp_flags = udp_flags
|
66
63
|
|
67
|
-
super(
|
64
|
+
super(level: level, formatter: formatter, filter: filter, application: application, host: host, &block)
|
68
65
|
reopen
|
69
66
|
end
|
70
67
|
|
@@ -39,19 +39,15 @@ module SemanticLogger
|
|
39
39
|
# logger.info('Hello World', some: :payload)
|
40
40
|
#
|
41
41
|
# Install the `rails_semantic_logger` gem to replace the Rails logger with Semantic Logger.
|
42
|
-
def initialize(
|
43
|
-
|
44
|
-
options = {logger: options} unless options.is_a?(Hash)
|
45
|
-
options = options.dup
|
46
|
-
@logger = options.delete(:logger)
|
42
|
+
def initialize(logger:, level: nil, formatter: nil, filter: nil, application: nil, host: nil, &block)
|
43
|
+
@logger = logger
|
47
44
|
|
48
45
|
# Check if the custom appender responds to all the log levels. For example Ruby ::Logger
|
49
46
|
if does_not_implement = LEVELS[1..-1].find { |i| !@logger.respond_to?(i) }
|
50
47
|
raise(ArgumentError, "Supplied logger does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..-1].inspect}")
|
51
48
|
end
|
52
49
|
|
53
|
-
|
54
|
-
super(options, &block)
|
50
|
+
super(level: level, formatter: formatter, filter: filter, application: application, host: host, &block)
|
55
51
|
end
|
56
52
|
|
57
53
|
# Pass log calls to the underlying Rails, log4j or Ruby logger
|
data/lib/semantic_logger/base.rb
CHANGED
@@ -132,7 +132,7 @@ module SemanticLogger
|
|
132
132
|
else
|
133
133
|
log.thread_name = thread.name
|
134
134
|
log.tags = (thread[:semantic_logger_tags] || []).clone
|
135
|
-
log.named_tags = (thread[:semantic_logger_named_tags] ||
|
135
|
+
log.named_tags = (thread[:semantic_logger_named_tags] || {}).clone
|
136
136
|
thread.backtrace
|
137
137
|
end
|
138
138
|
# TODO: Keep backtrace instead of transforming into a text message at this point
|
@@ -147,9 +147,42 @@ module SemanticLogger
|
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
150
|
-
#
|
150
|
+
# Add the tags or named tags to the list of tags to log for this thread whilst the supplied block is active.
|
151
|
+
#
|
152
|
+
# Returns result of block.
|
153
|
+
#
|
154
|
+
# Tagged example:
|
155
|
+
# SemanticLogger.tagged(12345, 'jack') do
|
156
|
+
# logger.debug('Hello World')
|
157
|
+
# end
|
158
|
+
#
|
159
|
+
# Named Tags (Hash) example:
|
160
|
+
# SemanticLogger.tagged(tracking_number: 12345) do
|
161
|
+
# logger.debug('Hello World')
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# Notes:
|
165
|
+
# - Named tags are the recommended approach since the tag consists of a name value pair this is more useful
|
166
|
+
# than just a string value in the logs, or centralized logging system.
|
167
|
+
# - This method is slow when using multiple text tags since it needs to flatten the tags and
|
168
|
+
# remove empty elements to support Rails 4.
|
169
|
+
# - It is recommended to keep tags as a list without any empty values, or contain any child arrays.
|
170
|
+
# However, this api will convert:
|
171
|
+
# `logger.tagged([['first', nil], nil, ['more'], 'other'])`
|
172
|
+
# to:
|
173
|
+
# `logger.tagged('first', 'more', 'other')`
|
174
|
+
# - For better performance with clean tags, see `SemanticLogger.tagged`.
|
151
175
|
def tagged(*tags, &block)
|
152
|
-
|
176
|
+
# Allow named tags to be passed into the logger
|
177
|
+
if tags.size == 1
|
178
|
+
tag = tags[0]
|
179
|
+
return yield if tag.nil? || tag == ''
|
180
|
+
return tag.is_a?(Hash) ? SemanticLogger.named_tagged(tag, &block) : SemanticLogger.fast_tag(tag.to_s, &block)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Need to flatten and reject empties to support calls from Rails 4
|
184
|
+
new_tags = tags.flatten.collect(&:to_s).reject(&:empty?)
|
185
|
+
SemanticLogger.tagged(*new_tags, &block)
|
153
186
|
end
|
154
187
|
|
155
188
|
# :nodoc:
|
@@ -160,9 +193,16 @@ module SemanticLogger
|
|
160
193
|
SemanticLogger.tags
|
161
194
|
end
|
162
195
|
|
163
|
-
#
|
196
|
+
# Returns the list of tags pushed after flattening them out and removing blanks
|
197
|
+
#
|
198
|
+
# Note:
|
199
|
+
# - This method is slow since it needs to flatten the tags and remove empty elements
|
200
|
+
# to support Rails 4.
|
201
|
+
# - For better performance with clean tags, use `SemanticLogger.push_tags`
|
164
202
|
def push_tags(*tags)
|
165
|
-
|
203
|
+
# Need to flatten and reject empties to support calls from Rails 4
|
204
|
+
new_tags = tags.flatten.collect(&:to_s).reject(&:empty?)
|
205
|
+
SemanticLogger.push_tags(*new_tags)
|
166
206
|
end
|
167
207
|
|
168
208
|
# :nodoc:
|
@@ -175,7 +215,7 @@ module SemanticLogger
|
|
175
215
|
SemanticLogger.silence(new_level, &block)
|
176
216
|
end
|
177
217
|
|
178
|
-
#
|
218
|
+
# Deprecated. Use `SemanticLogger.tagged`
|
179
219
|
def fast_tag(tag, &block)
|
180
220
|
SemanticLogger.fast_tag(tag, &block)
|
181
221
|
end
|
@@ -286,7 +326,7 @@ module SemanticLogger
|
|
286
326
|
if silence_level = params[:silence]
|
287
327
|
# In case someone accidentally sets `silence: true` instead of `silence: :error`
|
288
328
|
silence_level = :error if silence_level == true
|
289
|
-
silence(silence_level) {
|
329
|
+
silence(silence_level) {yield(params)}
|
290
330
|
else
|
291
331
|
yield(params)
|
292
332
|
end
|
@@ -1,7 +1,23 @@
|
|
1
|
+
require 'time'
|
1
2
|
module SemanticLogger
|
2
3
|
module Formatters
|
3
4
|
class Base
|
4
|
-
attr_accessor :time_format, :
|
5
|
+
attr_accessor :time_format, :log_host, :log_application
|
6
|
+
|
7
|
+
# Time precision varies by Ruby interpreter
|
8
|
+
# JRuby 9.1.8.0 supports microseconds
|
9
|
+
PRECISION =
|
10
|
+
if defined?(JRuby)
|
11
|
+
if (JRUBY_VERSION.to_f >= 9.1)
|
12
|
+
maint = JRUBY_VERSION.match(/\A\d\.\d\.(\d)\./)[1].to_i
|
13
|
+
(maint >= 8) || (JRUBY_VERSION.to_f > 9.1) ? 6 : 3
|
14
|
+
else
|
15
|
+
3
|
16
|
+
end
|
17
|
+
else
|
18
|
+
6
|
19
|
+
end
|
20
|
+
TIME_FORMAT = "%Y-%m-%d %H:%M:%S.%#{PRECISION}N"
|
5
21
|
|
6
22
|
# Parameters
|
7
23
|
# time_format: [String|Symbol|nil]
|
@@ -9,21 +25,24 @@ module SemanticLogger
|
|
9
25
|
# :iso_8601 Outputs an ISO8601 Formatted timestamp
|
10
26
|
# nil: Returns Empty string for time ( no time is output ).
|
11
27
|
# Default: '%Y-%m-%d %H:%M:%S.%6N'
|
12
|
-
def initialize(
|
13
|
-
|
14
|
-
@
|
15
|
-
|
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
|
28
|
+
def initialize(time_format: TIME_FORMAT, log_host: true, log_application: true)
|
29
|
+
@time_format = time_format
|
30
|
+
@log_host = log_host
|
31
|
+
@log_application = log_application
|
20
32
|
end
|
21
33
|
|
34
|
+
# Date & time
|
35
|
+
def time
|
36
|
+
format_time(log.time) if time_format
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
22
41
|
# Return the Time as a formatted string
|
23
42
|
def format_time(time)
|
24
43
|
case time_format
|
25
44
|
when :iso_8601
|
26
|
-
time.utc.iso8601(
|
45
|
+
time.utc.iso8601(PRECISION)
|
27
46
|
when nil
|
28
47
|
''
|
29
48
|
else
|
@@ -6,68 +6,98 @@ end
|
|
6
6
|
|
7
7
|
module SemanticLogger
|
8
8
|
module Formatters
|
9
|
-
class Color <
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
9
|
+
class Color < Default
|
10
|
+
attr_accessor :color_map, :color
|
11
|
+
|
12
|
+
# Supply a custom color map for every log level
|
13
|
+
class ColorMap
|
14
|
+
attr_accessor :trace, :debug, :info, :warn, :error, :fatal, :bold, :clear
|
15
|
+
|
16
|
+
def initialize(trace: AnsiColors::MAGENTA, debug: AnsiColors::GREEN, info: AnsiColors::CYAN, warn: AnsiColors::BOLD, error: AnsiColors::RED, fatal: AnsiColors::RED, bold: AnsiColors::BOLD, clear: AnsiColors::CLEAR)
|
17
|
+
@trace = trace
|
18
|
+
@debug = debug
|
19
|
+
@info = info
|
20
|
+
@warn = warn
|
21
|
+
@error = error
|
22
|
+
@fatal = fatal
|
23
|
+
@bold = bold
|
24
|
+
@clear = clear
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](level)
|
28
|
+
public_send(level)
|
29
|
+
end
|
21
30
|
end
|
22
31
|
|
23
32
|
# Adds color to the default log formatter
|
33
|
+
#
|
24
34
|
# Example:
|
35
|
+
# # Use a colorized output logger.
|
25
36
|
# SemanticLogger.add_appender(io: $stdout, formatter: :color)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
37
|
+
#
|
38
|
+
# Example:
|
39
|
+
# # Use a colorized output logger chenging the color for info to green.
|
40
|
+
# SemanticLogger.add_appender(io: $stdout, formatter: :color, color_map: {info: SemanticLogger::AnsiColors::YELLOW})
|
41
|
+
#
|
42
|
+
# Parameters:
|
43
|
+
# ap: [Hash]
|
44
|
+
# Any valid AwesomePrint option for rendering data.
|
45
|
+
# These options can also be changed be creating a `~/.aprc` file.
|
46
|
+
# See: https://github.com/michaeldv/awesome_print
|
47
|
+
#
|
48
|
+
# Note: The option :multiline is set to false if not supplied.
|
49
|
+
# Note: Has no effect if Awesome Print is not installed.
|
50
|
+
#
|
51
|
+
# color_map: [Hash | SemanticLogger::Formatters::Color::ColorMap]
|
52
|
+
# ColorMaps each of the log levels to a color
|
53
|
+
def initialize(ap: {multiline: false}, color_map: ColorMap.new, time_format: TIME_FORMAT, log_host: false, log_application: false)
|
54
|
+
@ai_options = ap
|
55
|
+
@color_map = color_map.is_a?(ColorMap) ? color_map : ColorMap.new(color_map)
|
56
|
+
super(time_format: time_format, log_host: log_host, log_application: log_application)
|
57
|
+
end
|
31
58
|
|
32
|
-
|
33
|
-
|
59
|
+
def level
|
60
|
+
"#{color}#{super}#{color_map.clear}"
|
61
|
+
end
|
34
62
|
|
35
|
-
|
36
|
-
|
63
|
+
def tags
|
64
|
+
"[#{color}#{log.tags.join("#{color_map.clear}] [#{color}")}#{color_map.clear}]" if log.tags && !log.tags.empty?
|
65
|
+
end
|
37
66
|
|
38
|
-
|
67
|
+
# Named Tags
|
68
|
+
def named_tags
|
39
69
|
if (named_tags = log.named_tags) && !named_tags.empty?
|
40
70
|
list = []
|
41
|
-
named_tags.each_pair { |name, value| list << "
|
42
|
-
|
71
|
+
named_tags.each_pair { |name, value| list << "#{color}#{name}: #{value}#{color_map.clear}" }
|
72
|
+
"{#{list.join(', ')}}"
|
43
73
|
end
|
74
|
+
end
|
44
75
|
|
45
|
-
|
46
|
-
|
76
|
+
def duration
|
77
|
+
"(#{color_map.bold}#{log.duration_human}#{color_map.clear})" if log.duration
|
78
|
+
end
|
47
79
|
|
48
|
-
|
49
|
-
|
80
|
+
def name
|
81
|
+
"#{color}#{super}#{color_map.clear}"
|
82
|
+
end
|
50
83
|
|
51
|
-
|
52
|
-
|
84
|
+
def payload
|
85
|
+
return unless log.has_payload?
|
53
86
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
if !defined?(AwesomePrint) || !payload.respond_to?(:ai)
|
59
|
-
payload.inspect
|
60
|
-
else
|
61
|
-
payload.ai(@ai_options) rescue payload.inspect
|
62
|
-
end
|
87
|
+
if !defined?(AwesomePrint) || !log.payload.respond_to?(:ai)
|
88
|
+
super
|
89
|
+
else
|
90
|
+
"-- #{log.payload.ai(@ai_options)}" rescue super
|
63
91
|
end
|
92
|
+
end
|
64
93
|
|
65
|
-
|
66
|
-
if log.exception
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
94
|
+
def exception
|
95
|
+
"-- Exception: #{color}#{log.exception.class}: #{log.exception.message}#{color_map.clear}\n#{log.backtrace_to_s}" if log.exception
|
96
|
+
end
|
97
|
+
|
98
|
+
def call(log, logger)
|
99
|
+
self.color = color_map[log.level]
|
100
|
+
super(log, logger)
|
71
101
|
end
|
72
102
|
|
73
103
|
end
|
@@ -1,46 +1,71 @@
|
|
1
1
|
module SemanticLogger
|
2
2
|
module Formatters
|
3
|
+
# Default non-colored text log output
|
3
4
|
class Default < Base
|
4
|
-
|
5
|
-
# Generates logs of the form:
|
6
|
-
# 2011-07-19 14:36:15.660235 D [1149:ScriptThreadProcess] Rails -- Hello World
|
7
|
-
def call(log, logger)
|
8
|
-
# Date & time
|
9
|
-
message = time_format.nil? ? '' : "#{format_time(log.time)} "
|
5
|
+
attr_accessor :log, :logger
|
10
6
|
|
11
|
-
|
12
|
-
|
7
|
+
# Formatting methods, must return nil, or a string
|
8
|
+
# Nil values are ignored
|
13
9
|
|
14
|
-
|
15
|
-
|
10
|
+
# Log level
|
11
|
+
def level
|
12
|
+
log.level_to_s
|
13
|
+
end
|
16
14
|
|
17
|
-
|
15
|
+
# Process info
|
16
|
+
def process_info
|
17
|
+
"[#{log.process_info}]"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Tags
|
21
|
+
def tags
|
22
|
+
"[#{log.tags.join('] [')}]" if log.tags && !log.tags.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
# Named Tags
|
26
|
+
def named_tags
|
18
27
|
if (named_tags = log.named_tags) && !named_tags.empty?
|
19
28
|
list = []
|
20
|
-
named_tags.each_pair { |name, value| list << "
|
21
|
-
|
29
|
+
named_tags.each_pair { |name, value| list << "#{name}: #{value}" }
|
30
|
+
"{#{list.join(', ')}}"
|
22
31
|
end
|
32
|
+
end
|
23
33
|
|
24
|
-
|
25
|
-
|
34
|
+
# Duration
|
35
|
+
def duration
|
36
|
+
"(#{log.duration_human})" if log.duration
|
37
|
+
end
|
26
38
|
|
27
|
-
|
28
|
-
|
39
|
+
# Class / app name
|
40
|
+
def name
|
41
|
+
log.name
|
42
|
+
end
|
29
43
|
|
30
|
-
|
31
|
-
|
44
|
+
# Log message
|
45
|
+
def message
|
46
|
+
"-- #{log.message}" if log.message
|
47
|
+
end
|
32
48
|
|
33
|
-
|
34
|
-
|
35
|
-
|
49
|
+
# Payload
|
50
|
+
def payload
|
51
|
+
if pl = log.payload_to_s
|
52
|
+
"-- #{pl}"
|
36
53
|
end
|
54
|
+
end
|
37
55
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
56
|
+
# Exception
|
57
|
+
def exception
|
58
|
+
"-- Exception: #{log.exception.class}: #{log.exception.message}\n#{log.backtrace_to_s}" if log.exception
|
59
|
+
end
|
60
|
+
|
61
|
+
# Default text log format
|
62
|
+
# Generates logs of the form:
|
63
|
+
# 2011-07-19 14:36:15.660235 D [1149:ScriptThreadProcess] Rails -- Hello World
|
64
|
+
def call(log, logger)
|
65
|
+
self.log = log
|
66
|
+
self.logger = logger
|
67
|
+
|
68
|
+
[time, level, process_info, tags, named_tags, duration, name, message, payload, exception].compact.join(' ')
|
44
69
|
end
|
45
70
|
|
46
71
|
end
|
@@ -3,18 +3,17 @@ module SemanticLogger
|
|
3
3
|
module Formatters
|
4
4
|
class Json < Raw
|
5
5
|
# Default JSON time format is ISO8601
|
6
|
-
def initialize(
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
def initialize(time_format: :iso_8601, log_host: true, log_application: true)
|
7
|
+
super(time_format: time_format, log_host: log_host, log_application: log_application)
|
8
|
+
end
|
9
|
+
|
10
|
+
def time
|
11
|
+
hash[:timestamp] = format_time(log.time)
|
10
12
|
end
|
11
13
|
|
12
14
|
# Returns log messages in JSON format
|
13
15
|
def call(log, logger)
|
14
|
-
|
15
|
-
h.delete(:time)
|
16
|
-
h[:timestamp] = format_time(log.time)
|
17
|
-
h.to_json
|
16
|
+
super(log, logger).to_json
|
18
17
|
end
|
19
18
|
|
20
19
|
end
|