twiglet 3.0.1 → 3.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65c3f0028e0ac918736f63a2475fb45231586d51256a8778d4730ebd7792675c
4
- data.tar.gz: 98d3607d5e1aad68f00d0ced7ccce24f0dba0c8b3eede84620a4a37a102a9849
3
+ metadata.gz: f5cdde3c0d71c1d9749653946057baea5d50178d32c3345aaec2969da0a08b81
4
+ data.tar.gz: cbf3b20dadb27002fbfe6f9448d515f086b59c9b6d9321e01090860edf6aad3e
5
5
  SHA512:
6
- metadata.gz: '0418346dbb4f5d486062caa13b7d1859a96309d00061b3e818563c9c399601f73035e2c89c64888e168d2a040fbb96c18a47e150c505859a58c067cfe46fe76f'
7
- data.tar.gz: 9437cae3f2662746cd059a660f9e0884c5c0edd7e56de77771d81678c01f6e55006e048b50bd44f7a99d240b9a5d3527b70a15528d0f4e8711ca2e0954c73235
6
+ metadata.gz: 47fbf4941a54d8fc6acf5b52f6b0cc3bbf68a0ecc6cb0f7739c595c47b78dd23d1a1bf3cfb692c79d915248b1bc9de6b9c45f8f43c902f5bc22675a43256af85
7
+ data.tar.gz: f65764ec68e251d38ba49eafe44f1b04a62695bc94411f5c1ebb524c3fab498916b65f6905768fbb1c7bdad5dce7124f8e38fa219266cd0a300aadfae84c1162
@@ -0,0 +1,22 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "07:00"
8
+ open-pull-requests-limit: 99
9
+ labels:
10
+ - dependencies
11
+ ignore:
12
+ - dependency-name: simplecov
13
+ versions:
14
+ - ">= 0.18"
15
+ - package-ecosystem: github-actions
16
+ directory: "/"
17
+ schedule:
18
+ interval: daily
19
+ time: "07:00"
20
+ open-pull-requests-limit: 99
21
+ labels:
22
+ - dependencies
@@ -16,7 +16,7 @@ jobs:
16
16
 
17
17
  strategy:
18
18
  matrix:
19
- ruby-version: [2.6, 2.7]
19
+ ruby-version: [2.6, 2.7, 3.0]
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v2
@@ -10,7 +10,7 @@ jobs:
10
10
  runs-on: ubuntu-18.04
11
11
 
12
12
  steps:
13
- - uses: simplybusiness/version-forget-me-not@v1
13
+ - uses: simplybusiness/version-forget-me-not@v2
14
14
  env:
15
15
  ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16
16
  VERSION_FILE_PATH: "lib/twiglet/version.rb"
@@ -0,0 +1,15 @@
1
+ name: "version update action"
2
+ on:
3
+ issue_comment:
4
+ types: [created]
5
+ jobs:
6
+ pr_commented:
7
+ runs-on: ubuntu-20.04
8
+ if: startsWith(github.event.comment.body, '/dobby')
9
+
10
+ steps:
11
+ - name: 'bump version'
12
+ uses: simplybusiness/dobby@v1.0.0
13
+ env:
14
+ ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15
+ VERSION_FILE_PATH: lib/twiglet/version.rb
data/example_app.rb CHANGED
@@ -7,28 +7,32 @@ PORT = 8080
7
7
  logger = Twiglet::Logger.new('petshop')
8
8
 
9
9
  # Start our petshop
10
- logger.info({
11
- event: {
12
- action: 'startup'
13
- },
14
- message: "Ready to go, listening on port #{PORT}",
15
- server: {
16
- port: PORT
10
+ logger.info(
11
+ {
12
+ event: {
13
+ action: 'startup'
14
+ },
15
+ message: "Ready to go, listening on port #{PORT}",
16
+ server: {
17
+ port: PORT
18
+ }
17
19
  }
18
- })
20
+ )
19
21
 
20
22
  # Use text logging
21
23
  logger.info("Ready to go, listening on port #{PORT}")
22
24
  #
23
25
  # We get a request
24
- request_logger = logger.with({
25
- event: {
26
- action: 'HTTP request'
27
- },
28
- trace: {
29
- id: '126bb6fa-28a2-470f-b013-eefbf9182b2d'
26
+ request_logger = logger.with(
27
+ {
28
+ event: {
29
+ action: 'HTTP request'
30
+ },
31
+ trace: {
32
+ id: '126bb6fa-28a2-470f-b013-eefbf9182b2d'
33
+ }
30
34
  }
31
- })
35
+ )
32
36
 
33
37
  # Oh noes!
34
38
  db_err = StandardError.new('Connection timed-out')
@@ -36,17 +40,19 @@ db_err = StandardError.new('Connection timed-out')
36
40
  request_logger.error({ message: 'DB connection failed.' }, db_err) if db_err
37
41
 
38
42
  # We return an error to the requester
39
- request_logger.info({
40
- message: 'Internal Server Error',
41
- http: {
42
- request: {
43
- method: 'get'
44
- },
45
- response: {
46
- status_code: 500
43
+ request_logger.info(
44
+ {
45
+ message: 'Internal Server Error',
46
+ http: {
47
+ request: {
48
+ method: 'get'
49
+ },
50
+ response: {
51
+ status_code: 500
52
+ }
47
53
  }
48
54
  }
49
- })
55
+ )
50
56
 
51
57
  # Logging with an empty message is an anti-pattern and is therefore forbidden
52
58
  # Both of the following lines would throw an error
@@ -11,7 +11,7 @@ class RequestLogger
11
11
  [status, headers, body]
12
12
  rescue StandardError => e
13
13
  log_error(env, 500, e)
14
- [500, {}, body]
14
+ raise e
15
15
  end
16
16
 
17
17
  private
@@ -26,24 +26,41 @@ class RequestLogger
26
26
  @logger.error(fields, error)
27
27
  end
28
28
 
29
+ # https://www.elastic.co/guide/en/ecs/1.5/ecs-field-reference.html
29
30
  def get_fields(env, status)
30
31
  message = "#{env['REQUEST_METHOD']}: #{env['PATH_INFO']}"
31
32
 
32
33
  {
33
- http: {
34
- request: {
35
- method: env['REQUEST_METHOD'],
36
- server: env['SERVER_NAME'],
37
- https_enabled: env['HTTPS'],
38
- path: env['PATH_INFO'],
39
- query: env['QUERY_STRING'] # Don't log PII query params
40
- },
41
- response: {
42
- status: status,
43
- body: { bytes: env['CONTENT_LENGTH'] }
44
- }
34
+ http: http_fields(env, status),
35
+ url: url_fields(env),
36
+ client: {
37
+ ip: env['HTTP_TRUE_CLIENT_IP'] || env['REMOTE_ADDR']
38
+ },
39
+ user_agent: {
40
+ original: env['HTTP_USER_AGENT']
45
41
  },
46
42
  message: message
47
43
  }
48
44
  end
45
+
46
+ def http_fields(env, status)
47
+ {
48
+ request: {
49
+ method: env['REQUEST_METHOD'],
50
+ mime_type: env['HTTP_ACCEPT']
51
+ },
52
+ response: {
53
+ status: status
54
+ },
55
+ version: env['HTTP_VERSION']
56
+ }
57
+ end
58
+
59
+ def url_fields(env)
60
+ {
61
+ path: env['PATH_INFO'],
62
+ query: env['QUERY_STRING'],
63
+ domain: env['SERVER_NAME']
64
+ }
65
+ end
49
66
  end
@@ -1,4 +1,5 @@
1
1
  require 'minitest/autorun'
2
+ require_relative '../../lib/twiglet/logger'
2
3
  require_relative './request_logger'
3
4
  require 'rack'
4
5
 
@@ -14,27 +15,49 @@ describe RequestLogger do
14
15
  end
15
16
 
16
17
  it 'logs the request data' do
17
- request.get("/some/path?some_var=1")
18
+ request.get(
19
+ "/some/path?some_var=1", 'HTTP_ACCEPT' => 'application/json',
20
+ 'REMOTE_ADDR' => '0.0.0.0',
21
+ 'HTTP_VERSION' => 'HTTP/1.1',
22
+ 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh)'
23
+ )
18
24
  log = JSON.parse(output.string)
19
- http_body = {
20
- "request" => {
21
- "https_enabled" => "off",
22
- "method" => "GET",
25
+
26
+ expected_log = {
27
+ "log" => { "level" => "info" },
28
+ "http" => {
29
+ "request" => {
30
+ "method" => "GET",
31
+ "mime_type" => 'application/json'
32
+ },
33
+ "response" => {
34
+ "status" => 200
35
+ },
36
+ "version" => 'HTTP/1.1'
37
+ },
38
+ "url" => {
23
39
  "path" => "/some/path",
24
40
  "query" => "some_var=1",
25
- "server" => "example.org"
41
+ "domain" => "example.org"
42
+ },
43
+ "client" => {
44
+ 'ip' => '0.0.0.0'
26
45
  },
27
- "response" => {
28
- "status" => 200,
29
- "body" => { "bytes" => "0" }
30
- }
46
+ "user_agent" => {
47
+ "original" => 'Mozilla/5.0 (Macintosh)'
48
+ },
49
+ "message" => "GET: /some/path"
31
50
  }
32
- assert_equal http_body, log["http"]
33
- assert_equal "GET: /some/path", log["message"]
51
+
52
+ assert_equal(log['log'], expected_log['log'])
53
+ assert_equal(log['http'], expected_log['http'])
54
+ assert_equal(log['url'], expected_log['url'])
55
+ assert_equal(log['user_agent'], expected_log['user_agent'])
56
+ assert_equal(log['message'], expected_log['message'])
34
57
  end
35
58
 
36
59
  it 'does not log PII' do
37
- request.post("/user/info", input_data: {credit_card_no: '1234'})
60
+ request.post("/user/info", input_data: { credit_card_no: '1234' })
38
61
  log = output.string
39
62
  assert_includes log, "POST: /user/info"
40
63
  refute_includes log, 'credit_card_no'
@@ -42,10 +65,12 @@ describe RequestLogger do
42
65
  end
43
66
 
44
67
  it 'logs an error message when a request is bad' do
45
- bad_request.get("/some/path")
68
+ -> { bad_request.get("/some/path") }.must_raise StandardError
46
69
  log = JSON.parse(output.string)
47
- assert_equal 'error', log['log']['level']
48
- assert_equal 'some exception', log['error']['message']
70
+ assert_equal log['log']['level'], 'error'
71
+ assert_equal log['error']['message'], 'some exception'
72
+ assert_equal log['error']['type'], 'StandardError'
73
+ assert_includes log['error']['stack_trace'], 'request_logger_test.rb'
49
74
  end
50
75
  end
51
76
 
@@ -33,9 +33,9 @@ module Twiglet
33
33
  def error(message = nil, error = nil, &block)
34
34
  if error
35
35
  error_fields = {
36
- 'error': {
37
- 'type': error.class,
38
- 'message': error.message
36
+ error: {
37
+ type: error.class,
38
+ message: error.message
39
39
  }
40
40
  }
41
41
  add_stack_trace(error_fields, error)
@@ -46,11 +46,13 @@ module Twiglet
46
46
  end
47
47
 
48
48
  def with(default_properties)
49
- Logger.new(@service_name,
50
- default_properties: default_properties,
51
- now: @now,
52
- output: @output,
53
- level: @level)
49
+ Logger.new(
50
+ @service_name,
51
+ default_properties: default_properties,
52
+ now: @now,
53
+ output: @output,
54
+ level: @level
55
+ )
54
56
  end
55
57
 
56
58
  alias_method :warning, :warn
@@ -6,6 +6,8 @@ module Twiglet
6
6
  self[:message] = msg
7
7
  when Hash
8
8
  replace(msg.transform_keys!(&:to_sym))
9
+ else
10
+ super(msg)
9
11
  end
10
12
 
11
13
  validate!
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Twiglet
4
- VERSION = '3.0.1'
4
+ VERSION = '3.0.6'
5
5
  end
data/test/logger_test.rb CHANGED
@@ -18,9 +18,11 @@ describe Twiglet::Logger do
18
18
  before do
19
19
  @now = -> { Time.utc(2020, 5, 11, 15, 1, 1) }
20
20
  @buffer = StringIO.new
21
- @logger = Twiglet::Logger.new('petshop',
22
- now: @now,
23
- output: @buffer)
21
+ @logger = Twiglet::Logger.new(
22
+ 'petshop',
23
+ now: @now,
24
+ output: @buffer
25
+ )
24
26
  end
25
27
 
26
28
  it 'should throw an error with an empty service name' do
@@ -30,8 +32,10 @@ describe Twiglet::Logger do
30
32
  end
31
33
 
32
34
  it 'conforms to the standard Ruby Logger API' do
33
- [:debug, :debug?, :info, :info?, :warn, :warn?, :fatal, :fatal?, :error, :error?,
34
- :level, :level=, :sev_threshold=].each do |call|
35
+ [
36
+ :debug, :debug?, :info, :info?, :warn, :warn?, :fatal, :fatal?, :error, :error?,
37
+ :level, :level=, :sev_threshold=
38
+ ].each do |call|
35
39
  assert @logger.respond_to?(call), "Logger does not respond to #{call}"
36
40
  end
37
41
  end
@@ -39,12 +43,12 @@ describe Twiglet::Logger do
39
43
  describe 'JSON logging' do
40
44
  it 'should throw an error with an empty message' do
41
45
  assert_raises RuntimeError do
42
- @logger.info({message: ''})
46
+ @logger.info({ message: '' })
43
47
  end
44
48
  end
45
49
 
46
50
  it 'should log mandatory attributes' do
47
- @logger.error({message: 'Out of pets exception'})
51
+ @logger.error({ message: 'Out of pets exception' })
48
52
  actual_log = read_json(@buffer)
49
53
 
50
54
  expected_log = {
@@ -65,9 +69,13 @@ describe Twiglet::Logger do
65
69
  end
66
70
 
67
71
  it 'should log the provided message' do
68
- @logger.error({event:
69
- {action: 'exception'},
70
- message: 'Emergency! Emergency!'})
72
+ @logger.error(
73
+ {
74
+ event:
75
+ { action: 'exception' },
76
+ message: 'Emergency! Emergency!'
77
+ }
78
+ )
71
79
  log = read_json(@buffer)
72
80
 
73
81
  assert_equal 'exception', log[:event][:action]
@@ -82,17 +90,19 @@ describe Twiglet::Logger do
82
90
  service: {
83
91
  type: 'shop'
84
92
  },
85
- request: {method: 'get'},
86
- response: {status_code: 200}
93
+ request: { method: 'get' },
94
+ response: { status_code: 200 }
87
95
  }
88
96
 
89
97
  output = StringIO.new
90
- logger = Twiglet::Logger.new('petshop',
91
- now: @now,
92
- output: output,
93
- default_properties: extra_properties)
94
-
95
- logger.error({message: 'GET /cats'})
98
+ logger = Twiglet::Logger.new(
99
+ 'petshop',
100
+ now: @now,
101
+ output: output,
102
+ default_properties: extra_properties
103
+ )
104
+
105
+ logger.error({ message: 'GET /cats' })
96
106
  log = read_json output
97
107
 
98
108
  assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
@@ -104,17 +114,21 @@ describe Twiglet::Logger do
104
114
 
105
115
  it "should be able to add properties with '.with'" do
106
116
  # Let's add some context to this customer journey
107
- purchase_logger = @logger.with({
108
- trace: {id: '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb'},
109
- customer: {full_name: 'Freda Bloggs'},
110
- event: {action: 'pet purchase'}
111
- })
117
+ purchase_logger = @logger.with(
118
+ {
119
+ trace: { id: '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb' },
120
+ customer: { full_name: 'Freda Bloggs' },
121
+ event: { action: 'pet purchase' }
122
+ }
123
+ )
112
124
 
113
125
  # do stuff
114
- purchase_logger.info({
115
- message: 'customer bought a dog',
116
- pet: {name: 'Barker', species: 'dog', breed: 'Bitsa'}
117
- })
126
+ purchase_logger.info(
127
+ {
128
+ message: 'customer bought a dog',
129
+ pet: { name: 'Barker', species: 'dog', breed: 'Bitsa' }
130
+ }
131
+ )
118
132
 
119
133
  log = read_json @buffer
120
134
 
@@ -135,8 +149,8 @@ describe Twiglet::Logger do
135
149
  end
136
150
 
137
151
  it "should log multiple messages properly" do
138
- @logger.debug({message: 'hi'})
139
- @logger.info({message: 'there'})
152
+ @logger.debug({ message: 'hi' })
153
+ @logger.info({ message: 'there' })
140
154
 
141
155
  expected_output =
142
156
  '{"ecs":{"version":"1.5.0"},"@timestamp":"2020-05-11T15:01:01.000Z",'\
@@ -174,7 +188,7 @@ describe Twiglet::Logger do
174
188
 
175
189
  LEVELS.each do |attrs|
176
190
  it "should correctly log level when calling #{attrs[:method]}" do
177
- @logger.public_send(attrs[:method], {message: 'a log message'})
191
+ @logger.public_send(attrs[:method], { message: 'a log message' })
178
192
  actual_log = read_json(@buffer)
179
193
 
180
194
  assert_equal attrs[:level], actual_log[:log][:level]
@@ -188,7 +202,7 @@ describe Twiglet::Logger do
188
202
  begin
189
203
  1 / 0
190
204
  rescue StandardError => e
191
- @logger.error({message: 'Artificially raised exception'}, e)
205
+ @logger.error({ message: 'Artificially raised exception' }, e)
192
206
  end
193
207
 
194
208
  actual_log = read_json(@buffer)
@@ -201,7 +215,7 @@ describe Twiglet::Logger do
201
215
 
202
216
  it 'should log an error without backtrace' do
203
217
  e = StandardError.new('Connection timed-out')
204
- @logger.error({message: 'Artificially raised exception'}, e)
218
+ @logger.error({ message: 'Artificially raised exception' }, e)
205
219
 
206
220
  actual_log = read_json(@buffer)
207
221
 
@@ -284,13 +298,15 @@ describe Twiglet::Logger do
284
298
 
285
299
  describe 'dotted keys' do
286
300
  it 'should be able to convert dotted keys to nested objects' do
287
- @logger.debug({
288
- "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
289
- message: 'customer bought a dog',
290
- "pet.name": 'Barker',
291
- "pet.species": 'dog',
292
- "pet.breed": 'Bitsa'
293
- })
301
+ @logger.debug(
302
+ {
303
+ "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
304
+ message: 'customer bought a dog',
305
+ "pet.name": 'Barker',
306
+ "pet.species": 'dog',
307
+ "pet.breed": 'Bitsa'
308
+ }
309
+ )
294
310
  log = read_json(@buffer)
295
311
 
296
312
  assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
@@ -301,12 +317,14 @@ describe Twiglet::Logger do
301
317
  end
302
318
 
303
319
  it 'should be able to mix dotted keys and nested objects' do
304
- @logger.debug({
305
- "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
306
- message: 'customer bought a dog',
307
- pet: {name: 'Barker', breed: 'Bitsa'},
308
- "pet.species": 'dog'
309
- })
320
+ @logger.debug(
321
+ {
322
+ "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
323
+ message: 'customer bought a dog',
324
+ pet: { name: 'Barker', breed: 'Bitsa' },
325
+ "pet.species": 'dog'
326
+ }
327
+ )
310
328
  log = read_json(@buffer)
311
329
 
312
330
  assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
@@ -7,7 +7,11 @@ SimpleCov.start do
7
7
  if ENV['CI']
8
8
  formatter SimpleCov::Formatter::SimpleFormatter
9
9
  else
10
- formatter SimpleCov::Formatter::MultiFormatter.new([SimpleCov::Formatter::SimpleFormatter,
11
- SimpleCov::Formatter::HTMLFormatter])
10
+ formatter SimpleCov::Formatter::MultiFormatter.new(
11
+ [
12
+ SimpleCov::Formatter::SimpleFormatter,
13
+ SimpleCov::Formatter::HTMLFormatter
14
+ ]
15
+ )
12
16
  end
13
17
  end
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: 3.0.1
4
+ version: 3.0.6
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-12-22 00:00:00.000000000 Z
11
+ date: 2021-02-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Like a log, only smaller.
14
14
  email:
@@ -18,9 +18,11 @@ extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
20
  - ".github/CODEOWNERS"
21
+ - ".github/dependabot.yml"
21
22
  - ".github/workflows/gem-publish.yml"
22
23
  - ".github/workflows/ruby.yml"
23
24
  - ".github/workflows/version-forget-me-not.yml"
25
+ - ".github/workflows/version-update.yml"
24
26
  - ".gitignore"
25
27
  - ".rubocop.yml"
26
28
  - ".ruby-version"
@@ -66,7 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
66
68
  - !ruby/object:Gem::Version
67
69
  version: '0'
68
70
  requirements: []
69
- rubygems_version: 3.1.4
71
+ rubygems_version: 3.2.3
70
72
  signing_key:
71
73
  specification_version: 4
72
74
  summary: Twiglet