semantic_logger 4.0.0 → 4.1.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 +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
|