twiglet 3.0.6 → 3.1.3
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/.github/workflows/{version-update.yml → dobby-actions.yml} +5 -4
- data/Gemfile +2 -6
- data/README.md +5 -1
- data/lib/twiglet/formatter.rb +5 -2
- data/lib/twiglet/logger.rb +14 -3
- data/lib/twiglet/message.rb +1 -12
- data/lib/twiglet/validation_schema.json +10 -0
- data/lib/twiglet/validator.rb +25 -0
- data/lib/twiglet/version.rb +1 -1
- data/test/formatter_test.rb +2 -1
- data/test/logger_test.rb +30 -2
- data/test/message_test.rb +0 -18
- data/test/validator_test.rb +38 -0
- data/twiglet.gemspec +6 -0
- metadata +77 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9cace8decdc47857c5ce0bec8ae85b92fe93f866521bc6872371db38999b25c3
|
4
|
+
data.tar.gz: dc9bf4d673968a4c718af21c117584f7770a3f2d50cc704ff5d39da1bec26626
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12c4455ae13471d0ebf0899966c9dbd32bf17841c468beaddb0a912a234033921a1694c2007d6a9d1732bdb69dbabc21f88cf98a195b9eea9a9be52d51cc07db
|
7
|
+
data.tar.gz: 91fab7466ae13a05de15fe66c2b8e4b78b28e2ee31220cd0f427d3928bfd59b15dbec2facc444f5c3a10a29bade3ee568e369fb921360fd6ddbb4c76f80bafd0
|
@@ -1,4 +1,4 @@
|
|
1
|
-
name: "
|
1
|
+
name: "dobby action"
|
2
2
|
on:
|
3
3
|
issue_comment:
|
4
4
|
types: [created]
|
@@ -8,8 +8,9 @@ jobs:
|
|
8
8
|
if: startsWith(github.event.comment.body, '/dobby')
|
9
9
|
|
10
10
|
steps:
|
11
|
-
- name:
|
12
|
-
uses: simplybusiness/dobby@
|
11
|
+
- name: bump version
|
12
|
+
uses: simplybusiness/dobby@v2.0.0
|
13
13
|
env:
|
14
|
-
|
14
|
+
DOBBY_APP_ID: ${{ secrets.DOBBY_APP_ID }}
|
15
|
+
DOBBY_PRIVATE_KEY: ${{ secrets.DOBBY_PRIVATE_KEY }}
|
15
16
|
VERSION_FILE_PATH: lib/twiglet/version.rb
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -110,7 +110,7 @@ This writes:
|
|
110
110
|
It may be that when making a series of logs that write information about a single event, you may want to avoid duplication by creating an event specific logger that includes the context:
|
111
111
|
|
112
112
|
```ruby
|
113
|
-
|
113
|
+
request_logger = logger.with({ event: { action: 'HTTP request'}, trace: { id: '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb' }})
|
114
114
|
```
|
115
115
|
|
116
116
|
This can be used like any other Logger instance:
|
@@ -140,6 +140,10 @@ To access the formatter:
|
|
140
140
|
logger.formatter
|
141
141
|
```
|
142
142
|
|
143
|
+
### HTTP Request Logging
|
144
|
+
Take a look at this sample [Rack application](examples/rack/example_rack_app.rb#L15) with an ECS compliant
|
145
|
+
[request logger](/examples/rack/request_logger.rb) as a template when configuring your own request logging middleware with Twiglet.
|
146
|
+
|
143
147
|
## Use of dotted keys (DEPRECATED)
|
144
148
|
|
145
149
|
Writing nested json objects could be confusing. This library has a built-in feature to convert dotted keys into nested objects, so if you log like this:
|
data/lib/twiglet/formatter.rb
CHANGED
@@ -7,18 +7,21 @@ module Twiglet
|
|
7
7
|
Hash.include HashExtensions
|
8
8
|
|
9
9
|
def initialize(service_name,
|
10
|
-
default_properties: {},
|
10
|
+
validator:, default_properties: {},
|
11
11
|
now: -> { Time.now.utc })
|
12
12
|
@service_name = service_name
|
13
13
|
@now = now
|
14
14
|
@default_properties = default_properties
|
15
|
+
@validator = validator
|
15
16
|
|
16
17
|
super()
|
17
18
|
end
|
18
19
|
|
19
20
|
def call(severity, _time, _progname, msg)
|
20
21
|
level = severity.downcase
|
21
|
-
|
22
|
+
message = Message.new(msg)
|
23
|
+
@validator.validate(message)
|
24
|
+
log(level: level, message: message)
|
22
25
|
end
|
23
26
|
|
24
27
|
private
|
data/lib/twiglet/logger.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
require 'logger'
|
4
4
|
require 'time'
|
5
|
-
require 'json'
|
6
5
|
require_relative 'formatter'
|
7
6
|
require_relative '../hash_extensions'
|
8
7
|
require_relative 'message'
|
8
|
+
require_relative 'validator'
|
9
9
|
|
10
10
|
module Twiglet
|
11
11
|
class Logger < ::Logger
|
@@ -26,8 +26,19 @@ module Twiglet
|
|
26
26
|
raise 'Service name is mandatory' \
|
27
27
|
unless service_name.is_a?(String) && !service_name.strip.empty?
|
28
28
|
|
29
|
-
|
30
|
-
|
29
|
+
@validator = Validator.from_file("#{__dir__}/validation_schema.json")
|
30
|
+
|
31
|
+
@formatter = Twiglet::Formatter.new(
|
32
|
+
service_name,
|
33
|
+
default_properties: default_properties,
|
34
|
+
now: now,
|
35
|
+
validator: @validator
|
36
|
+
)
|
37
|
+
super(output, formatter: @formatter, level: level)
|
38
|
+
end
|
39
|
+
|
40
|
+
def configure_validation_error_response(&block)
|
41
|
+
@validator.custom_error_handler = block
|
31
42
|
end
|
32
43
|
|
33
44
|
def error(message = nil, error = nil, &block)
|
data/lib/twiglet/message.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Twiglet
|
2
2
|
class Message < Hash
|
3
3
|
def initialize(msg)
|
4
|
+
super
|
4
5
|
case msg
|
5
6
|
when String
|
6
7
|
self[:message] = msg
|
@@ -9,18 +10,6 @@ module Twiglet
|
|
9
10
|
else
|
10
11
|
super(msg)
|
11
12
|
end
|
12
|
-
|
13
|
-
validate!
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def validate!
|
19
|
-
raise 'Message must be initialized with a String or a non-empty Hash' if empty?
|
20
|
-
|
21
|
-
raise 'Log object must have a \'message\' property' unless self[:message]
|
22
|
-
|
23
|
-
raise 'The \'message\' property of the log object must not be empty' if self[:message].strip.empty?
|
24
13
|
end
|
25
14
|
end
|
26
15
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json-schema'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Twiglet
|
7
|
+
class Validator
|
8
|
+
attr_accessor :custom_error_handler
|
9
|
+
|
10
|
+
def initialize(schema)
|
11
|
+
@schema = schema
|
12
|
+
@custom_error_handler = ->(e) { raise e }
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_file(file_path)
|
16
|
+
new(JSON.parse(File.read(file_path)))
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate(message)
|
20
|
+
JSON::Validator.validate!(@schema, message)
|
21
|
+
rescue JSON::Schema::ValidationError => e
|
22
|
+
custom_error_handler.call(e)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/twiglet/version.rb
CHANGED
data/test/formatter_test.rb
CHANGED
@@ -3,11 +3,12 @@
|
|
3
3
|
require 'minitest/autorun'
|
4
4
|
require 'json'
|
5
5
|
require_relative '../lib/twiglet/formatter'
|
6
|
+
require_relative '../lib/twiglet/validator'
|
6
7
|
|
7
8
|
describe Twiglet::Formatter do
|
8
9
|
before do
|
9
10
|
@now = -> { Time.utc(2020, 5, 11, 15, 1, 1) }
|
10
|
-
@formatter = Twiglet::Formatter.new('petshop', now: @now)
|
11
|
+
@formatter = Twiglet::Formatter.new('petshop', now: @now, validator: Twiglet::Validator.new({}))
|
11
12
|
end
|
12
13
|
|
13
14
|
it 'initializes an instance of a Ruby Logger Formatter' do
|
data/test/logger_test.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'minitest/autorun'
|
4
|
+
require 'minitest/mock'
|
4
5
|
require_relative '../lib/twiglet/logger'
|
5
6
|
|
6
7
|
LEVELS = [
|
@@ -42,11 +43,17 @@ describe Twiglet::Logger do
|
|
42
43
|
|
43
44
|
describe 'JSON logging' do
|
44
45
|
it 'should throw an error with an empty message' do
|
45
|
-
assert_raises
|
46
|
+
assert_raises JSON::Schema::ValidationError, "The property '#/message' was not of a minimum string length of 1" do
|
46
47
|
@logger.info({ message: '' })
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
51
|
+
it 'should throw an error if message is missing' do
|
52
|
+
assert_raises JSON::Schema::ValidationError, "The property '#/message' was not of a minimum string length of 1" do
|
53
|
+
@logger.info({ foo: 'bar' })
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
50
57
|
it 'should log mandatory attributes' do
|
51
58
|
@logger.error({ message: 'Out of pets exception' })
|
52
59
|
actual_log = read_json(@buffer)
|
@@ -239,7 +246,7 @@ describe Twiglet::Logger do
|
|
239
246
|
|
240
247
|
describe 'text logging' do
|
241
248
|
it 'should throw an error with an empty message' do
|
242
|
-
assert_raises
|
249
|
+
assert_raises JSON::Schema::ValidationError, "The property '#/message' was not of a minimum string length of 1" do
|
243
250
|
@logger.info('')
|
244
251
|
end
|
245
252
|
end
|
@@ -352,6 +359,27 @@ describe Twiglet::Logger do
|
|
352
359
|
end
|
353
360
|
end
|
354
361
|
|
362
|
+
describe 'configuring error response' do
|
363
|
+
it 'blows up by default' do
|
364
|
+
assert_raises JSON::Schema::ValidationError,
|
365
|
+
"The property '#/message' of type boolean did not match the following type: string" do
|
366
|
+
@logger.debug(message: true)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'silently swallows errors when configured to do so' do
|
371
|
+
mock = Minitest::Mock.new
|
372
|
+
|
373
|
+
@logger.configure_validation_error_response do |_e|
|
374
|
+
mock.notify_error("Logging schema validation error")
|
375
|
+
end
|
376
|
+
|
377
|
+
mock.expect(:notify_error, nil, ["Logging schema validation error"])
|
378
|
+
nonconformant_log = { message: true }
|
379
|
+
@logger.debug(nonconformant_log)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
355
383
|
private
|
356
384
|
|
357
385
|
def read_json(buffer)
|
data/test/message_test.rb
CHANGED
@@ -2,24 +2,6 @@ require 'minitest/autorun'
|
|
2
2
|
require_relative '../lib/twiglet/message'
|
3
3
|
|
4
4
|
describe Twiglet::Message do
|
5
|
-
it 'raises if message is empty' do
|
6
|
-
assert_raises RuntimeError do
|
7
|
-
Twiglet::Message.new(' ')
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'raises if message is not provided' do
|
12
|
-
assert_raises RuntimeError do
|
13
|
-
Twiglet::Message.new(foo: 'bar')
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'raises on unrecognized inputs' do
|
18
|
-
assert_raises RuntimeError do
|
19
|
-
Twiglet::Message.new(OpenStruct.new(message: 'hello'))
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
5
|
it 'returns a message hash from a string' do
|
24
6
|
assert_equal Twiglet::Message.new('hello, world'), { message: 'hello, world' }
|
25
7
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require_relative '../lib/twiglet/validator'
|
5
|
+
|
6
|
+
describe Twiglet::Validator do
|
7
|
+
let(:valid)
|
8
|
+
|
9
|
+
before do
|
10
|
+
schema = {
|
11
|
+
"type" => "object",
|
12
|
+
"required" => ["message"],
|
13
|
+
"properties" => {
|
14
|
+
"message" => {
|
15
|
+
"type" => "string"
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
@validator = Twiglet::Validator.new(schema)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'does not raise when validation passes' do
|
24
|
+
assert_equal(@validator.validate({ message: 'this is my message', foo: 'bar' }), true)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'raises when validation fails' do
|
28
|
+
assert_raises JSON::Schema::ValidationError do
|
29
|
+
@validator.validate({ message: true })
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'is a no-op when validator is configured to swallow errors' do
|
34
|
+
@validator.custom_error_handler = ->(e) { puts e }
|
35
|
+
|
36
|
+
assert_nil(@validator.validate({ message: true }))
|
37
|
+
end
|
38
|
+
end
|
data/twiglet.gemspec
CHANGED
@@ -21,4 +21,10 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.required_ruby_version = '>= 2.6'
|
22
22
|
|
23
23
|
gem.license = 'Copyright SimplyBusiness'
|
24
|
+
|
25
|
+
gem.add_runtime_dependency 'json-schema'
|
26
|
+
gem.add_development_dependency 'minitest'
|
27
|
+
gem.add_development_dependency 'rake'
|
28
|
+
gem.add_development_dependency 'simplecov', '0.17.1'
|
29
|
+
gem.add_development_dependency 'simplycop'
|
24
30
|
end
|
metadata
CHANGED
@@ -1,15 +1,85 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twiglet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simply Business
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-02-
|
12
|
-
dependencies:
|
11
|
+
date: 2021-02-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json-schema
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.17.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.17.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplycop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
13
83
|
description: Like a log, only smaller.
|
14
84
|
email:
|
15
85
|
- tech@simplybusiness.co.uk
|
@@ -19,10 +89,10 @@ extra_rdoc_files: []
|
|
19
89
|
files:
|
20
90
|
- ".github/CODEOWNERS"
|
21
91
|
- ".github/dependabot.yml"
|
92
|
+
- ".github/workflows/dobby-actions.yml"
|
22
93
|
- ".github/workflows/gem-publish.yml"
|
23
94
|
- ".github/workflows/ruby.yml"
|
24
95
|
- ".github/workflows/version-forget-me-not.yml"
|
25
|
-
- ".github/workflows/version-update.yml"
|
26
96
|
- ".gitignore"
|
27
97
|
- ".rubocop.yml"
|
28
98
|
- ".ruby-version"
|
@@ -42,12 +112,15 @@ files:
|
|
42
112
|
- lib/twiglet/formatter.rb
|
43
113
|
- lib/twiglet/logger.rb
|
44
114
|
- lib/twiglet/message.rb
|
115
|
+
- lib/twiglet/validation_schema.json
|
116
|
+
- lib/twiglet/validator.rb
|
45
117
|
- lib/twiglet/version.rb
|
46
118
|
- test/formatter_test.rb
|
47
119
|
- test/hash_extensions_test.rb
|
48
120
|
- test/logger_test.rb
|
49
121
|
- test/message_test.rb
|
50
122
|
- test/test_coverage.rb
|
123
|
+
- test/validator_test.rb
|
51
124
|
- twiglet.gemspec
|
52
125
|
homepage: https://github.com/simplybusiness/twiglet-ruby
|
53
126
|
licenses:
|