twiglet 2.0.0 → 2.1.0

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: 8f7e6901994e7715bab639be94853dbbcce697e16da26f2f8340404ebe4c0211
4
- data.tar.gz: 1758572f62c689a68642cfb8887dc2ea380a3602332cf2321b06d51e80a7066d
3
+ metadata.gz: 9a94a60f7f04b8a19fd27f483e9f8e29992a237fa03491ad417a385032b98401
4
+ data.tar.gz: 976a134b1c69aa19956b5979affa35fc0141cb5164a8fa67a0aa91da1f490b2a
5
5
  SHA512:
6
- metadata.gz: 808fb4ddbdaff2287331e022ec4a593469335a40476fa59cb0546facd126113c6b564f7538c3366984bb40ed178a131861b0a97e9ea571640caa68db0354a906
7
- data.tar.gz: 83913e66900495ee39ba12d8f38e16e83fbc3621c4caf7bdaa128c6b44568f82face4498f1b2081e798ff6265b79a7758dd97eff08bd6224e8b0b7d077e8cb81
6
+ metadata.gz: 93c304efe8eeccbe2f6ed826499a288eee8d07897c79e1c0aca1f16e3908ea136a1731db7318228e5fa819b3cdafe6b196926a0fd1f9d387f94f7a315b0e868b
7
+ data.tar.gz: 74b4d3ec698510d3a236a159bf92fe1b0d07d6caf60fce91e46d24d89680ea5c93ffaee042606c5560d72709148756912d011405c113f51213f959c449d9e0ba
@@ -8,7 +8,7 @@ AllCops:
8
8
  Documentation:
9
9
  Enabled: false
10
10
  Metrics/BlockLength:
11
- Max: 200
11
+ Max: 250
12
12
  Metrics/AbcSize:
13
13
  Max: 20
14
14
  Metrics/MethodLength:
data/README.md CHANGED
@@ -40,6 +40,17 @@ This will write to STDOUT a JSON string:
40
40
 
41
41
  Obviously the timestamp will be different.
42
42
 
43
+ Alternatively, if you just want to log some error message in text format
44
+ ```ruby
45
+ logger.error( "Emergency! There's an Emergency going on")
46
+ ```
47
+
48
+ This will write to STDOUT a JSON string:
49
+
50
+ ```json
51
+ {"service":{"name":"service name"},"@timestamp":"2020-05-14T10:54:59.164+01:00","log":{"level":"error"}, "message":"Emergency! There's an Emergency going on"}
52
+ ```
53
+
43
54
  Errors can be logged as well, and this will log the error message and backtrace in the relevant ECS compliant fields:
44
55
 
45
56
  ```ruby
@@ -68,6 +79,18 @@ This writes:
68
79
  {"service":{"name":"service name"},"@timestamp":"2020-05-14T10:56:49.527+01:00","log":{"level":"info"},"event":{"action":"HTTP request"},"message":"GET /pets success","trace":{"id":"1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb"},"http":{"request":{"method":"get"},"response":{"status_code":200}},"url":{"path":"/pets"}}
69
80
  ```
70
81
 
82
+ Similar to error you can use text logging here as:
83
+
84
+ ```
85
+ logger.info('GET /pets success')
86
+ ```
87
+ This writes:
88
+
89
+ ```json
90
+ {"service":{"name":"service name"},"@timestamp":"2020-05-14T10:56:49.527+01:00","log":{"level":"info"}}
91
+ ```
92
+
93
+
71
94
  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:
72
95
 
73
96
  ```ruby
@@ -17,6 +17,9 @@ logger.info({
17
17
  }
18
18
  })
19
19
 
20
+ # Use text logging
21
+ logger.info("Ready to go, listening on port #{PORT}")
22
+ #
20
23
  # We get a request
21
24
  request_logger = logger.with({
22
25
  event: {
@@ -55,6 +55,8 @@ module Twiglet
55
55
  log(level: 'critical', message: message)
56
56
  end
57
57
 
58
+ alias_method :fatal, :critical
59
+
58
60
  def with(default_properties)
59
61
  Logger.new(@service_name,
60
62
  default_properties: default_properties,
@@ -65,27 +67,46 @@ module Twiglet
65
67
  private
66
68
 
67
69
  def log(level:, message:)
68
- raise 'Message must be a Hash' unless message.is_a?(Hash)
70
+ case message
71
+ when String
72
+ log_text(level, message: message)
73
+ when Hash
74
+ log_object(level, message: message)
75
+ else
76
+ raise('Message must be String or Hash')
77
+ end
78
+ end
79
+
80
+ def log_text(level, message:)
81
+ raise('The \'message\' property of log object must not be empty') if message.strip.empty?
82
+
83
+ message = { message: message }
84
+ log_message(level, message: message)
85
+ end
69
86
 
87
+ def log_object(level, message:)
70
88
  message = message.transform_keys(&:to_sym)
71
89
  message.key?(:message) || raise('Log object must have a \'message\' property')
72
-
73
90
  message[:message].strip.empty? && raise('The \'message\' property of log object must not be empty')
74
91
 
92
+ log_message(level, message: message)
93
+ end
94
+
95
+ def log_message(level, message:)
75
96
  base_message = {
97
+ "@timestamp": @now.call.iso8601(3),
76
98
  service: {
77
99
  name: @service_name
78
100
  },
79
- "@timestamp": @now.call.iso8601(3),
80
101
  log: {
81
102
  level: level
82
103
  }
83
104
  }
84
105
 
85
106
  @output.puts base_message
86
- .deep_merge(@default_properties.to_nested)
87
- .deep_merge(message.to_nested)
88
- .to_json
107
+ .deep_merge(@default_properties.to_nested)
108
+ .deep_merge(message.to_nested)
109
+ .to_json
89
110
  end
90
111
  end
91
112
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Twiglet
4
- VERSION = '2.0.0'
4
+ VERSION = '2.1.0'
5
5
  end
@@ -12,191 +12,238 @@ describe Twiglet::Logger do
12
12
  output: @buffer)
13
13
  end
14
14
 
15
+ LEVELS = [
16
+ { method: :debug, level: 'debug' },
17
+ { method: :info, level: 'info' },
18
+ { method: :warning, level: 'warning' },
19
+ { method: :warn, level: 'warning' },
20
+ { method: :critical, level: 'critical' },
21
+ { method: :fatal, level: 'critical' },
22
+ { method: :error, level: 'error' }
23
+ ].freeze
24
+
15
25
  it 'should throw an error with an empty service name' do
16
26
  assert_raises RuntimeError do
17
27
  Twiglet::Logger.new(' ')
18
28
  end
19
29
  end
20
30
 
21
- it 'should throw an error with an empty message' do
22
- assert_raises RuntimeError do
23
- @logger.info('')
31
+ describe 'JSON logging' do
32
+ it 'should throw an error with an empty message' do
33
+ assert_raises RuntimeError do
34
+ @logger.info({message: ''})
35
+ end
24
36
  end
25
- end
26
37
 
27
- it 'should log mandatory attributes' do
28
- @logger.error({message: 'Out of pets exception'})
29
- actual_log = read_json(@buffer)
30
-
31
- expected_log = {
32
- message: 'Out of pets exception',
33
- "@timestamp": '2020-05-11T15:01:01.000Z',
34
- service: {
35
- name: 'petshop'
36
- },
37
- log: {
38
- level: 'error'
38
+ it 'should log mandatory attributes' do
39
+ @logger.error({message: 'Out of pets exception'})
40
+ actual_log = read_json(@buffer)
41
+
42
+ expected_log = {
43
+ message: 'Out of pets exception',
44
+ "@timestamp": '2020-05-11T15:01:01.000Z',
45
+ service: {
46
+ name: 'petshop'
47
+ },
48
+ log: {
49
+ level: 'error'
50
+ }
39
51
  }
40
- }
41
52
 
42
- assert_equal expected_log, actual_log
43
- end
53
+ assert_equal expected_log, actual_log
54
+ end
44
55
 
45
- it 'should log the provided message' do
46
- @logger.error({event:
47
- {action: 'exception'},
48
- message: 'Emergency! Emergency!'})
49
- log = read_json(@buffer)
56
+ it 'should log the provided message' do
57
+ @logger.error({event:
58
+ {action: 'exception'},
59
+ message: 'Emergency! Emergency!'})
60
+ log = read_json(@buffer)
50
61
 
51
- assert_equal 'exception', log[:event][:action]
52
- assert_equal 'Emergency! Emergency!', log[:message]
53
- end
62
+ assert_equal 'exception', log[:event][:action]
63
+ assert_equal 'Emergency! Emergency!', log[:message]
64
+ end
54
65
 
55
- it 'should log scoped properties defined at creation' do
56
- extra_properties = {
57
- trace: {
58
- id: '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb'
59
- },
60
- service: {
61
- type: 'shop'
62
- },
63
- request: {method: 'get'},
64
- response: {status_code: 200}
65
- }
66
-
67
- output = StringIO.new
68
- logger = Twiglet::Logger.new('petshop',
69
- now: @now,
70
- output: output,
71
- default_properties: extra_properties)
72
-
73
- logger.error({message: 'GET /cats'})
74
- log = read_json output
75
-
76
- assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
77
- assert_equal 'petshop', log[:service][:name]
78
- assert_equal 'shop', log[:service][:type]
79
- assert_equal 'get', log[:request][:method]
80
- assert_equal 200, log[:response][:status_code]
81
- end
66
+ it 'should log scoped properties defined at creation' do
67
+ extra_properties = {
68
+ trace: {
69
+ id: '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb'
70
+ },
71
+ service: {
72
+ type: 'shop'
73
+ },
74
+ request: {method: 'get'},
75
+ response: {status_code: 200}
76
+ }
82
77
 
83
- it "should be able to add properties with '.with'" do
84
- # Let's add some context to this customer journey
85
- purchase_logger = @logger.with({
86
- trace: {id: '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb'},
87
- customer: {full_name: 'Freda Bloggs'},
88
- event: {action: 'pet purchase'}
89
- })
90
-
91
- # do stuff
92
- purchase_logger.info({
93
- message: 'customer bought a dog',
94
- pet: {name: 'Barker', species: 'dog', breed: 'Bitsa'}
95
- })
96
-
97
- log = read_json @buffer
98
-
99
- assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
100
- assert_equal 'Freda Bloggs', log[:customer][:full_name]
101
- assert_equal 'pet purchase', log[:event][:action]
102
- assert_equal 'customer bought a dog', log[:message]
103
- assert_equal 'Barker', log[:pet][:name]
104
- end
78
+ output = StringIO.new
79
+ logger = Twiglet::Logger.new('petshop',
80
+ now: @now,
81
+ output: output,
82
+ default_properties: extra_properties)
105
83
 
106
- it "should log 'message' string property" do
107
- message = {}
108
- message['message'] = 'Guinea pigs arrived'
109
- @logger.debug(message)
110
- log = read_json(@buffer)
84
+ logger.error({message: 'GET /cats'})
85
+ log = read_json output
111
86
 
112
- assert_equal 'Guinea pigs arrived', log[:message]
113
- end
87
+ assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
88
+ assert_equal 'petshop', log[:service][:name]
89
+ assert_equal 'shop', log[:service][:type]
90
+ assert_equal 'get', log[:request][:method]
91
+ assert_equal 200, log[:response][:status_code]
92
+ end
114
93
 
115
- it 'should be able to convert dotted keys to nested objects' do
116
- @logger.debug({
117
- "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
118
- message: 'customer bought a dog',
119
- "pet.name": 'Barker',
120
- "pet.species": 'dog',
121
- "pet.breed": 'Bitsa'
122
- })
123
- log = read_json(@buffer)
124
-
125
- assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
126
- assert_equal 'customer bought a dog', log[:message]
127
- assert_equal 'Barker', log[:pet][:name]
128
- assert_equal 'dog', log[:pet][:species]
129
- assert_equal 'Bitsa', log[:pet][:breed]
130
- end
94
+ it "should be able to add properties with '.with'" do
95
+ # Let's add some context to this customer journey
96
+ purchase_logger = @logger.with({
97
+ trace: {id: '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb'},
98
+ customer: {full_name: 'Freda Bloggs'},
99
+ event: {action: 'pet purchase'}
100
+ })
101
+
102
+ # do stuff
103
+ purchase_logger.info({
104
+ message: 'customer bought a dog',
105
+ pet: {name: 'Barker', species: 'dog', breed: 'Bitsa'}
106
+ })
107
+
108
+ log = read_json @buffer
109
+
110
+ assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
111
+ assert_equal 'Freda Bloggs', log[:customer][:full_name]
112
+ assert_equal 'pet purchase', log[:event][:action]
113
+ assert_equal 'customer bought a dog', log[:message]
114
+ assert_equal 'Barker', log[:pet][:name]
115
+ end
131
116
 
132
- it 'should be able to mix dotted keys and nested objects' do
133
- @logger.debug({
134
- "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
135
- message: 'customer bought a dog',
136
- pet: {name: 'Barker', breed: 'Bitsa'},
137
- "pet.species": 'dog'
138
- })
139
- log = read_json(@buffer)
140
-
141
- assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
142
- assert_equal 'customer bought a dog', log[:message]
143
- assert_equal 'Barker', log[:pet][:name]
144
- assert_equal 'dog', log[:pet][:species]
145
- assert_equal 'Bitsa', log[:pet][:breed]
146
- end
117
+ it "should log 'message' string property" do
118
+ message = {}
119
+ message['message'] = 'Guinea pigs arrived'
120
+ @logger.debug(message)
121
+ log = read_json(@buffer)
147
122
 
148
- it 'should work with mixed string and symbol properties' do
149
- log = {
150
- "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb'
151
- }
152
- event = {}
153
- log['event'] = event
154
- log['message'] = 'customer bought a dog'
155
- pet = {}
156
- pet['name'] = 'Barker'
157
- pet['breed'] = 'Bitsa'
158
- pet[:species] = 'dog'
159
- log[:pet] = pet
160
-
161
- @logger.debug(log)
162
- actual_log = read_json(@buffer)
163
-
164
- assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', actual_log[:trace][:id]
165
- assert_equal 'customer bought a dog', actual_log[:message]
166
- assert_equal 'Barker', actual_log[:pet][:name]
167
- assert_equal 'dog', actual_log[:pet][:species]
168
- assert_equal 'Bitsa', actual_log[:pet][:breed]
169
- end
123
+ assert_equal 'Guinea pigs arrived', log[:message]
124
+ end
170
125
 
171
- it 'should log an error with backtrace' do
172
- begin
173
- 1 / 0
174
- rescue StandardError => e
175
- @logger.error({message: 'Artificially raised exception'}, e)
126
+ it 'should be able to convert dotted keys to nested objects' do
127
+ @logger.debug({
128
+ "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
129
+ message: 'customer bought a dog',
130
+ "pet.name": 'Barker',
131
+ "pet.species": 'dog',
132
+ "pet.breed": 'Bitsa'
133
+ })
134
+ log = read_json(@buffer)
135
+
136
+ assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
137
+ assert_equal 'customer bought a dog', log[:message]
138
+ assert_equal 'Barker', log[:pet][:name]
139
+ assert_equal 'dog', log[:pet][:species]
140
+ assert_equal 'Bitsa', log[:pet][:breed]
176
141
  end
177
142
 
178
- actual_log = read_json(@buffer)
143
+ it 'should be able to mix dotted keys and nested objects' do
144
+ @logger.debug({
145
+ "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
146
+ message: 'customer bought a dog',
147
+ pet: {name: 'Barker', breed: 'Bitsa'},
148
+ "pet.species": 'dog'
149
+ })
150
+ log = read_json(@buffer)
151
+
152
+ assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
153
+ assert_equal 'customer bought a dog', log[:message]
154
+ assert_equal 'Barker', log[:pet][:name]
155
+ assert_equal 'dog', log[:pet][:species]
156
+ assert_equal 'Bitsa', log[:pet][:breed]
157
+ end
179
158
 
180
- assert_equal 'Artificially raised exception', actual_log[:message]
181
- assert_equal 'divided by 0', actual_log[:error][:message]
182
- assert_match 'logger_test.rb', actual_log[:error][:stack_trace].lines.first
159
+ it 'should work with mixed string and symbol properties' do
160
+ log = {
161
+ "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb'
162
+ }
163
+ event = {}
164
+ log['event'] = event
165
+ log['message'] = 'customer bought a dog'
166
+ pet = {}
167
+ pet['name'] = 'Barker'
168
+ pet['breed'] = 'Bitsa'
169
+ pet[:species] = 'dog'
170
+ log[:pet] = pet
171
+
172
+ @logger.debug(log)
173
+ actual_log = read_json(@buffer)
174
+
175
+ assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', actual_log[:trace][:id]
176
+ assert_equal 'customer bought a dog', actual_log[:message]
177
+ assert_equal 'Barker', actual_log[:pet][:name]
178
+ assert_equal 'dog', actual_log[:pet][:species]
179
+ assert_equal 'Bitsa', actual_log[:pet][:breed]
180
+ end
181
+
182
+ it 'should log an error with backtrace' do
183
+ begin
184
+ 1 / 0
185
+ rescue StandardError => e
186
+ @logger.error({message: 'Artificially raised exception'}, e)
187
+ end
188
+
189
+ actual_log = read_json(@buffer)
190
+
191
+ assert_equal 'Artificially raised exception', actual_log[:message]
192
+ assert_equal 'divided by 0', actual_log[:error][:message]
193
+ assert_match 'logger_test.rb', actual_log[:error][:stack_trace].lines.first
194
+ end
195
+
196
+ LEVELS.each do |attrs|
197
+ it "should correctly log level when calling #{attrs[:method]}" do
198
+ @logger.public_send(attrs[:method], {message: 'a log message'})
199
+ actual_log = read_json(@buffer)
200
+
201
+ assert_equal attrs[:level], actual_log[:log][:level]
202
+ assert_equal 'a log message', actual_log[:message]
203
+ end
204
+ end
183
205
  end
184
206
 
185
- levels = [
186
- { method: :debug, level: 'debug' },
187
- { method: :info, level: 'info' },
188
- { method: :warning, level: 'warning' },
189
- { method: :warn, level: 'warning' },
190
- { method: :error, level: 'error' }
191
- ]
207
+ describe 'text logging' do
208
+ it 'should throw an error with an empty message' do
209
+ assert_raises RuntimeError do
210
+ @logger.info('')
211
+ end
212
+ end
192
213
 
193
- levels.each do |attrs|
194
- it "should correctly log level when calling #{attrs[:method]}" do
195
- @logger.public_send(attrs[:method], {message: 'a log message'})
214
+ it 'should log mandatory attributes' do
215
+ @logger.error('Out of pets exception')
196
216
  actual_log = read_json(@buffer)
197
217
 
198
- assert_equal attrs[:level], actual_log[:log][:level]
199
- assert_equal 'a log message', actual_log[:message]
218
+ expected_log = {
219
+ message: 'Out of pets exception',
220
+ "@timestamp": '2020-05-11T15:01:01.000Z',
221
+ service: {
222
+ name: 'petshop'
223
+ },
224
+ log: {
225
+ level: 'error'
226
+ }
227
+ }
228
+
229
+ assert_equal expected_log, actual_log
230
+ end
231
+
232
+ it 'should log the provided message' do
233
+ @logger.error('Emergency! Emergency!')
234
+ log = read_json(@buffer)
235
+
236
+ assert_equal 'Emergency! Emergency!', log[:message]
237
+ end
238
+
239
+ LEVELS.each do |attrs|
240
+ it "should correctly log level when calling #{attrs[:method]}" do
241
+ @logger.public_send(attrs[:method], 'a log message')
242
+ actual_log = read_json(@buffer)
243
+
244
+ assert_equal attrs[:level], actual_log[:log][:level]
245
+ assert_equal 'a log message', actual_log[:message]
246
+ end
200
247
  end
201
248
  end
202
249
 
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.0.0
4
+ version: 2.1.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-05 00:00:00.000000000 Z
11
+ date: 2020-06-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Like a log, only smaller.
14
14
  email:
@@ -55,7 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
55
55
  - !ruby/object:Gem::Version
56
56
  version: '0'
57
57
  requirements: []
58
- rubygems_version: 3.1.2
58
+ rubygems_version: 3.0.3
59
59
  signing_key:
60
60
  specification_version: 4
61
61
  summary: Twiglet