twiglet 3.0.6 → 3.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|