twiglet 2.1.1 → 2.2.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/lib/twiglet/formatter.rb +68 -0
- data/lib/twiglet/logger.rb +12 -69
- data/lib/twiglet/version.rb +1 -1
- data/test/formatter_test.rb +31 -0
- data/test/logger_test.rb +37 -4
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 356d8c7fec6c41823029cc36bef1cc49bacae6157342b251e9b57f49dcb8151f
|
4
|
+
data.tar.gz: bd58a67b48daa9bf6108dc78098f46ad0204f98ca600c29bacd713f6cb54ea2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4bc443482b93b1bba853fbe67605d9b0673830726cf1ef89e941860bc7f832d1efb5f82d5890197ae279b9edf97bf19edf8dc2d8bf61d56d082d6e641f94a245
|
7
|
+
data.tar.gz: 8d5e178fca01957147dca807aab4117958b18514d568440e72f53cfc8931aad62035a71633646776368f4409d9be48ab8598c66aad4347edd59f79a0bfc281ae
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require_relative '../hash_extensions'
|
3
|
+
|
4
|
+
module Twiglet
|
5
|
+
class Formatter < ::Logger::Formatter
|
6
|
+
Hash.include HashExtensions
|
7
|
+
|
8
|
+
def initialize(service_name,
|
9
|
+
default_properties: {},
|
10
|
+
now: -> { Time.now.utc })
|
11
|
+
@service_name = service_name
|
12
|
+
@now = now
|
13
|
+
@default_properties = default_properties
|
14
|
+
|
15
|
+
super()
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(severity, _time, _progname, msg)
|
19
|
+
level = severity.downcase
|
20
|
+
log(level: level, message: msg)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def log(level:, message:)
|
26
|
+
case message
|
27
|
+
when String
|
28
|
+
log_text(level, message: message)
|
29
|
+
when Hash
|
30
|
+
log_object(level, message: message)
|
31
|
+
else
|
32
|
+
raise('Message must be String or Hash')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def log_text(level, message:)
|
37
|
+
raise('The \'message\' property of log object must not be empty') if message.strip.empty?
|
38
|
+
|
39
|
+
message = { message: message }
|
40
|
+
log_message(level, message: message)
|
41
|
+
end
|
42
|
+
|
43
|
+
def log_object(level, message:)
|
44
|
+
message = message.transform_keys(&:to_sym)
|
45
|
+
message.key?(:message) || raise('Log object must have a \'message\' property')
|
46
|
+
message[:message].strip.empty? && raise('The \'message\' property of log object must not be empty')
|
47
|
+
|
48
|
+
log_message(level, message: message)
|
49
|
+
end
|
50
|
+
|
51
|
+
def log_message(level, message:)
|
52
|
+
base_message = {
|
53
|
+
"@timestamp": @now.call.iso8601(3),
|
54
|
+
service: {
|
55
|
+
name: @service_name
|
56
|
+
},
|
57
|
+
log: {
|
58
|
+
level: level
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
base_message
|
63
|
+
.deep_merge(@default_properties.to_nested)
|
64
|
+
.deep_merge(message.to_nested)
|
65
|
+
.to_json
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/twiglet/logger.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'logger'
|
3
4
|
require 'time'
|
4
5
|
require 'json'
|
6
|
+
require 'twiglet/formatter'
|
5
7
|
require_relative '../hash_extensions'
|
6
8
|
|
7
9
|
module Twiglet
|
8
|
-
class Logger
|
10
|
+
class Logger < ::Logger
|
9
11
|
Hash.include HashExtensions
|
10
12
|
|
11
13
|
def initialize(
|
@@ -19,26 +21,13 @@ module Twiglet
|
|
19
21
|
@output = output
|
20
22
|
|
21
23
|
raise 'Service name is mandatory' \
|
22
|
-
unless
|
24
|
+
unless service_name.is_a?(String) && !service_name.strip.empty?
|
23
25
|
|
24
|
-
|
26
|
+
formatter = Twiglet::Formatter.new(service_name, default_properties: default_properties, now: now)
|
27
|
+
super(output, formatter: formatter)
|
25
28
|
end
|
26
29
|
|
27
|
-
def
|
28
|
-
log(level: 'debug', message: message)
|
29
|
-
end
|
30
|
-
|
31
|
-
def info(message)
|
32
|
-
log(level: 'info', message: message)
|
33
|
-
end
|
34
|
-
|
35
|
-
def warning(message)
|
36
|
-
log(level: 'warning', message: message)
|
37
|
-
end
|
38
|
-
|
39
|
-
alias_method :warn, :warning
|
40
|
-
|
41
|
-
def error(message, error = nil)
|
30
|
+
def error(message = {}, error = nil, &block)
|
42
31
|
if error
|
43
32
|
error_fields = {
|
44
33
|
'error': {
|
@@ -46,18 +35,12 @@ module Twiglet
|
|
46
35
|
}
|
47
36
|
}
|
48
37
|
add_stack_trace(error_fields, error)
|
49
|
-
message
|
38
|
+
message.is_a?(Hash) ? message.merge!(error_fields) : error_fields.merge!(message: message)
|
50
39
|
end
|
51
40
|
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
def critical(message)
|
56
|
-
log(level: 'critical', message: message)
|
41
|
+
super(message, &block)
|
57
42
|
end
|
58
43
|
|
59
|
-
alias_method :fatal, :critical
|
60
|
-
|
61
44
|
def with(default_properties)
|
62
45
|
Logger.new(@service_name,
|
63
46
|
default_properties: default_properties,
|
@@ -65,50 +48,10 @@ module Twiglet
|
|
65
48
|
output: @output)
|
66
49
|
end
|
67
50
|
|
68
|
-
|
69
|
-
|
70
|
-
def log(level:, message:)
|
71
|
-
case message
|
72
|
-
when String
|
73
|
-
log_text(level, message: message)
|
74
|
-
when Hash
|
75
|
-
log_object(level, message: message)
|
76
|
-
else
|
77
|
-
raise('Message must be String or Hash')
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def log_text(level, message:)
|
82
|
-
raise('The \'message\' property of log object must not be empty') if message.strip.empty?
|
83
|
-
|
84
|
-
message = { message: message }
|
85
|
-
log_message(level, message: message)
|
86
|
-
end
|
87
|
-
|
88
|
-
def log_object(level, message:)
|
89
|
-
message = message.transform_keys(&:to_sym)
|
90
|
-
message.key?(:message) || raise('Log object must have a \'message\' property')
|
91
|
-
message[:message].strip.empty? && raise('The \'message\' property of log object must not be empty')
|
51
|
+
alias_method :warning, :warn
|
52
|
+
alias_method :critical, :fatal
|
92
53
|
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
def log_message(level, message:)
|
97
|
-
base_message = {
|
98
|
-
"@timestamp": @now.call.iso8601(3),
|
99
|
-
service: {
|
100
|
-
name: @service_name
|
101
|
-
},
|
102
|
-
log: {
|
103
|
-
level: level
|
104
|
-
}
|
105
|
-
}
|
106
|
-
|
107
|
-
@output.puts base_message
|
108
|
-
.deep_merge(@default_properties.to_nested)
|
109
|
-
.deep_merge(message.to_nested)
|
110
|
-
.to_json
|
111
|
-
end
|
54
|
+
private
|
112
55
|
|
113
56
|
def add_stack_trace(hash_to_add_to, error)
|
114
57
|
hash_to_add_to[:error][:stack_trace] = error.backtrace.join("\n") if error.backtrace
|
data/lib/twiglet/version.rb
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'json'
|
5
|
+
require_relative '../lib/twiglet/formatter'
|
6
|
+
|
7
|
+
describe Twiglet::Formatter do
|
8
|
+
before do
|
9
|
+
@now = -> { Time.utc(2020, 5, 11, 15, 1, 1) }
|
10
|
+
@formatter = Twiglet::Formatter.new('petshop', now: @now)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'initializes an instance of a Ruby Logger Formatter' do
|
14
|
+
assert @formatter.is_a?(::Logger::Formatter)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns a formatted log from a string message' do
|
18
|
+
msg = @formatter.call('warn', nil, nil, 'shop is running low on dog food')
|
19
|
+
expected_log = {
|
20
|
+
"@timestamp" => '2020-05-11T15:01:01.000Z',
|
21
|
+
"service" => {
|
22
|
+
"name" => 'petshop'
|
23
|
+
},
|
24
|
+
"log" => {
|
25
|
+
"level" => 'warn'
|
26
|
+
},
|
27
|
+
"message" => 'shop is running low on dog food'
|
28
|
+
}
|
29
|
+
assert_equal JSON.parse(msg), expected_log
|
30
|
+
end
|
31
|
+
end
|
data/test/logger_test.rb
CHANGED
@@ -15,10 +15,10 @@ describe Twiglet::Logger do
|
|
15
15
|
LEVELS = [
|
16
16
|
{ method: :debug, level: 'debug' },
|
17
17
|
{ method: :info, level: 'info' },
|
18
|
-
{ method: :warning, level: '
|
19
|
-
{ method: :warn, level: '
|
20
|
-
{ method: :critical, level: '
|
21
|
-
{ method: :fatal, level: '
|
18
|
+
{ method: :warning, level: 'warn' },
|
19
|
+
{ method: :warn, level: 'warn' },
|
20
|
+
{ method: :critical, level: 'fatal' },
|
21
|
+
{ method: :fatal, level: 'fatal' },
|
22
22
|
{ method: :error, level: 'error' }
|
23
23
|
].freeze
|
24
24
|
|
@@ -28,6 +28,13 @@ describe Twiglet::Logger do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
it 'conforms to the standard Ruby Logger API' do
|
32
|
+
[:debug, :debug?, :info, :info?, :warn, :warn?, :fatal, :fatal?, :error, :error?,
|
33
|
+
:level, :level=, :sev_threshold=].each do |call|
|
34
|
+
assert @logger.respond_to?(call), "Logger does not respond to #{call}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
31
38
|
describe 'JSON logging' do
|
32
39
|
it 'should throw an error with an empty message' do
|
33
40
|
assert_raises RuntimeError do
|
@@ -258,6 +265,32 @@ describe Twiglet::Logger do
|
|
258
265
|
end
|
259
266
|
end
|
260
267
|
|
268
|
+
describe 'logging with a block' do
|
269
|
+
LEVELS.each do |attrs|
|
270
|
+
it "should correctly log the block when calling #{attrs[:method]}" do
|
271
|
+
block = proc { 'a block log message' }
|
272
|
+
@logger.public_send(attrs[:method], &block)
|
273
|
+
actual_log = read_json(@buffer)
|
274
|
+
|
275
|
+
assert_equal attrs[:level], actual_log[:log][:level]
|
276
|
+
assert_equal 'a block log message', actual_log[:message]
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe 'logger level' do
|
282
|
+
[
|
283
|
+
{ expression: :info, level: 1 },
|
284
|
+
{ expression: 'warn', level: 2 },
|
285
|
+
{ expression: Logger::DEBUG, level: 0 }
|
286
|
+
].each do |args|
|
287
|
+
it "sets the severity threshold to level #{args[:level]}" do
|
288
|
+
@logger.level = args[:expression]
|
289
|
+
assert_equal args[:level], @logger.level
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
261
294
|
private
|
262
295
|
|
263
296
|
def read_json(buffer)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twiglet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simply Business
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Like a log, only smaller.
|
14
14
|
email:
|
@@ -31,8 +31,10 @@ files:
|
|
31
31
|
- Rakefile
|
32
32
|
- example_app.rb
|
33
33
|
- lib/hash_extensions.rb
|
34
|
+
- lib/twiglet/formatter.rb
|
34
35
|
- lib/twiglet/logger.rb
|
35
36
|
- lib/twiglet/version.rb
|
37
|
+
- test/formatter_test.rb
|
36
38
|
- test/hash_extensions_test.rb
|
37
39
|
- test/logger_test.rb
|
38
40
|
- twiglet.gemspec
|
@@ -55,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
57
|
- !ruby/object:Gem::Version
|
56
58
|
version: '0'
|
57
59
|
requirements: []
|
58
|
-
rubygems_version: 3.
|
60
|
+
rubygems_version: 3.0.8
|
59
61
|
signing_key:
|
60
62
|
specification_version: 4
|
61
63
|
summary: Twiglet
|