kiev 2.7.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +25 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +27 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE.md +7 -0
  9. data/README.md +461 -0
  10. data/Rakefile +18 -0
  11. data/bin/console +8 -0
  12. data/config.ru +9 -0
  13. data/gemfiles/que_0.12.2.gemfile +14 -0
  14. data/gemfiles/que_0.12.3.gemfile +15 -0
  15. data/gemfiles/rails_4.1.gemfile +13 -0
  16. data/gemfiles/rails_4.2.gemfile +13 -0
  17. data/gemfiles/sidekiq_4.2.gemfile +14 -0
  18. data/gemfiles/sinatra_1.4.gemfile +15 -0
  19. data/gemfiles/sinatra_2.0.gemfile +15 -0
  20. data/kiev.gemspec +28 -0
  21. data/lib/ext/rack/common_logger.rb +12 -0
  22. data/lib/kiev.rb +9 -0
  23. data/lib/kiev/base.rb +51 -0
  24. data/lib/kiev/base52.rb +20 -0
  25. data/lib/kiev/config.rb +164 -0
  26. data/lib/kiev/her_ext/client_request_id.rb +14 -0
  27. data/lib/kiev/httparty.rb +11 -0
  28. data/lib/kiev/json.rb +118 -0
  29. data/lib/kiev/logger.rb +122 -0
  30. data/lib/kiev/param_filter.rb +30 -0
  31. data/lib/kiev/que/job.rb +78 -0
  32. data/lib/kiev/rack.rb +20 -0
  33. data/lib/kiev/rack/request_id.rb +68 -0
  34. data/lib/kiev/rack/request_logger.rb +140 -0
  35. data/lib/kiev/rack/silence_action_dispatch_logger.rb +22 -0
  36. data/lib/kiev/rack/store_request_details.rb +21 -0
  37. data/lib/kiev/railtie.rb +55 -0
  38. data/lib/kiev/request_body_filter.rb +36 -0
  39. data/lib/kiev/request_body_filter/default.rb +11 -0
  40. data/lib/kiev/request_body_filter/form_data.rb +12 -0
  41. data/lib/kiev/request_body_filter/json.rb +14 -0
  42. data/lib/kiev/request_body_filter/xml.rb +18 -0
  43. data/lib/kiev/request_store.rb +32 -0
  44. data/lib/kiev/sidekiq.rb +41 -0
  45. data/lib/kiev/sidekiq/client_request_id.rb +12 -0
  46. data/lib/kiev/sidekiq/request_id.rb +39 -0
  47. data/lib/kiev/sidekiq/request_logger.rb +39 -0
  48. data/lib/kiev/sidekiq/request_store.rb +13 -0
  49. data/lib/kiev/sidekiq/store_request_details.rb +27 -0
  50. data/lib/kiev/subrequest_helper.rb +61 -0
  51. data/lib/kiev/util.rb +14 -0
  52. data/lib/kiev/version.rb +5 -0
  53. metadata +208 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4993218a36a9d91204d3af2a87327a3394494e3f
4
+ data.tar.gz: f446c0d1c31a6aacf5d12d47488cafb19756dabd
5
+ SHA512:
6
+ metadata.gz: 66768446011943507c8741451239becbbaa9397b1d4cf96214f3bc0bc31e524d407821d55566ea998953092ae5bcf869c8e3a74622ad06f9a0cca446f1fcf0d2
7
+ data.tar.gz: d11e2427f18cb51feb4ccddcb270b840638f37531819685e9913c6a1825a0cc8dc2d5fd6e44cfd2799a27b47caaa94178edabad127b135cbc6d5c40161c288c5
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /gemfiles/*.gemfile.lock
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ .rubocop-http*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,25 @@
1
+ inherit_from:
2
+ - https://raw.githubusercontent.com/blacklane/rubocop/master/rubocop.yml
3
+
4
+ Lint/HandleExceptions:
5
+ Exclude:
6
+ - test/rails_integration_test.rb
7
+ - test/sinatra_integration_test.rb
8
+ Lint/RescueException:
9
+ Exclude:
10
+ - lib/kiev/request_body_filter/json.rb
11
+ - lib/kiev/sidekiq/request_logger.rb
12
+ - lib/kiev/rack/request_logger.rb
13
+ - lib/kiev/json.rb
14
+ - test/sidekiq_test.rb
15
+ Style/GlobalVars:
16
+ Exclude:
17
+ - test/helper.rb
18
+ Style/GuardClause:
19
+ Exclude:
20
+ - lib/kiev/logger.rb
21
+ Style/NestedParenthesizedCalls:
22
+ Exclude:
23
+ - spec/lib/kiev/json_spec.rb
24
+ Style/BlockDelimiters:
25
+ EnforcedStyle: line_count_based
@@ -0,0 +1 @@
1
+ 2.3.3
@@ -0,0 +1,27 @@
1
+ sudo: false
2
+ branches:
3
+ only:
4
+ - master
5
+ cache:
6
+ - bundler
7
+ language: ruby
8
+ rvm:
9
+ - 2.3.3
10
+ - 2.2.3
11
+ addons:
12
+ postgresql: "9.4"
13
+ services:
14
+ - postgresql
15
+ - redis-server
16
+ before_script:
17
+ - psql -c 'create database que_test;' -U postgres
18
+ gemfile:
19
+ - gemfiles/que_0.12.2.gemfile
20
+ - gemfiles/que_0.12.3.gemfile
21
+ - gemfiles/rails_4.1.gemfile
22
+ - gemfiles/rails_4.2.gemfile
23
+ - gemfiles/sidekiq_4.2.gemfile
24
+ - gemfiles/sinatra_1.4.gemfile
25
+ - gemfiles/sinatra_2.0.gemfile
26
+ matrix:
27
+ fast_finish: true
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ eval_gemfile(File.join(File.dirname(__FILE__), "gemfiles/rails_4.1.gemfile"))
4
+
5
+ gem "wwtd"
@@ -0,0 +1,7 @@
1
+ Copyright 2017 Blacklane
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,461 @@
1
+ # Kiev [![Build Status](https://travis-ci.com/blacklane/kiev.svg?token=j5tcq3Fz9ERKZ2HhzC8T&branch=master)](https://travis-ci.com/blacklane/kiev)
2
+
3
+ Kiev is a comprehensive logging library aimed at covering a wide range of frameworks and tools from the Ruby ecosystem:
4
+
5
+ - Rails
6
+ - Sinatra
7
+ - Rack and other Rack-based frameworks
8
+ - Sidekiq
9
+ - Que
10
+ - Her and other Faraday-based libraries
11
+ - HTTParty
12
+
13
+ The main goal of Kiev is consistent logging across distributed systems, like **tracking HTTP requests across various Ruby micro-services**. Kiev will generate and propagate request IDs and make it easy for you to identify service calls and branching requests, **including background jobs triggered by these requests**.
14
+
15
+ Aside from web requests and background jobs, which are tracked out of the box, Kiev makes it easy to append additional information or introduce **custom events**.
16
+
17
+ Kiev produces structured logs in the **JSON format**, which are ready to be ingested by ElasticSearch or other similar JSON-driven data stores. It eliminates the need for Logstash in a typical ELK stack.
18
+
19
+ In **development mode**, Kiev can print human-readable logs - pretty much like the default Rails logger, but including all the additional information that you've provided via Kiev events.
20
+
21
+ ## Install
22
+
23
+ Add the gem to your `Gemfile`:
24
+
25
+ ```ruby
26
+ gem "kiev"
27
+ ```
28
+
29
+ Don't forget to `bundle install`.
30
+
31
+ ## Configure
32
+
33
+ ### Rails
34
+
35
+ Place your configuration under `config/initializers/kiev.rb`:
36
+
37
+ ```ruby
38
+ require "kiev"
39
+
40
+ Kiev.configure do |config|
41
+ config.app = :my_app
42
+ config.development_mode = Rails.env.development?
43
+ config.log_path = Rails.root.join("log", "structured.log") unless Rails.env.development? || $stdout.isatty
44
+ end
45
+ ```
46
+
47
+ The middleware stack is included automatically via a *Railtie*.
48
+
49
+ ### Sinatra
50
+
51
+ Somewhere in your code, ideally before the server configuration, add the following lines:
52
+
53
+ ```ruby
54
+ require "kiev"
55
+
56
+ Kiev.configure do |config|
57
+ config.app = :my_app
58
+ config.log_path = File.join("log", "structured.log")
59
+ end
60
+ ```
61
+
62
+ Within your `Sinatra::Base` implementation, include the `Kiev::Rack` module, in order to register the middleware stack:
63
+
64
+ ```ruby
65
+ require "kiev"
66
+ require "sinatra/base"
67
+
68
+ class MyController < Sinatra::Base
69
+ include Kiev::Rack
70
+
71
+ use SomeOtherMiddleware
72
+
73
+ get "/hello" do
74
+ "world"
75
+ end
76
+ end
77
+ ```
78
+
79
+ ### Rack
80
+
81
+ Somewhere in your code, ideally before the server configuration, add the following lines:
82
+
83
+ ```ruby
84
+ require "kiev"
85
+
86
+ Kiev.configure do |config|
87
+ config.app = :my_app
88
+ config.log_path = File.join("log", "structured.log")
89
+ end
90
+ ```
91
+
92
+ Within your `Rack::Builder` implementation, include the `Kiev::Rack` module, in order to register the middleware stack:
93
+
94
+ ```ruby
95
+ require "kiev"
96
+ require "rack"
97
+
98
+ app = Rack::Builder.new do
99
+ include Kiev::Rack
100
+
101
+ use SomeOtherMiddleware
102
+
103
+ run labmda { |env| [ 200, {}, [ "hello world" ] ] }
104
+ end
105
+
106
+ run(app)
107
+ ```
108
+
109
+ ### Sidekiq
110
+
111
+ Add the following lines to your initializer code:
112
+
113
+ ```ruby
114
+ Kiev::Sidekiq.enable
115
+ ```
116
+
117
+ ### Que
118
+
119
+ Add the following lines to your initializer code:
120
+
121
+ ```ruby
122
+ require "kiev/que/job"
123
+
124
+ class MyJob < Kiev::Que::Job
125
+ ...
126
+ end
127
+ ```
128
+
129
+ ### Her
130
+
131
+ Add the following lines to your initializer code:
132
+
133
+ ```ruby
134
+ Her::API.setup(url: "https://api.example.com") do |c|
135
+ c.use Kiev::HerExt::ClientRequestId
136
+ # other middleware
137
+ end
138
+ ```
139
+
140
+ ## Loading only the required parts
141
+
142
+ You can load only parts of the gem, if you don't want to use all features:
143
+
144
+ ```ruby
145
+ require "kiev/her_ext/client_request_id"
146
+ ```
147
+
148
+ ## Logging
149
+
150
+ ### Requests
151
+
152
+ For web requests the Kiev middleware will log the following information by default:
153
+
154
+ ```json
155
+ {
156
+ "application":"my_app",
157
+ "event":"request_finished",
158
+ "level":"INFO",
159
+ "timestamp":"2017-01-27T16:11:44.123Z",
160
+ "host":"localhost",
161
+ "verb":"GET",
162
+ "path":"/",
163
+ "params":"{\"hello\":\"world\",\"password\":\"[FILTERED]\"}",
164
+ "ip":"127.0.0.1",
165
+ "request_id":"UUID",
166
+ "request_depth":0,
167
+ "route":"RootController#index",
168
+ "user_agent":"curl/7.50.1",
169
+ "status":200,
170
+ "request_duration":62.3773,
171
+ "body":"See #log_response_body_condition",
172
+ "error_message": "...",
173
+ "error_class": "...",
174
+ "error_backtrace": "...",
175
+ "tree_path": "ACE",
176
+ "tree_leaf": true
177
+ }
178
+ ```
179
+
180
+ The `params` attribute will store both query parameters and request body fields (as long as they are parseable). Sensitive fields will be filtered out - see the `#filtered_params` option.
181
+
182
+ The `request_id` is the correlation ID and will be the same across all requests within a chain of requests. It's represented as a UUID (version 4).
183
+
184
+ The `request_depth` represents the position of the current request within a chain of requests. It starts with 0.
185
+
186
+ The `route` attribute will be set to either the Rails route (`RootController#index`) or Sinatra route (`/`) or the path, depending on the context.
187
+
188
+ The `request_duration` is measured in miliseconds.
189
+
190
+ The `body` attribute coresponds to the response body and will be logged depending on the `#log_response_body_condition` option.
191
+
192
+ The `tree_path` attribute can be used to follow the branching of requests within a chain of requests. It's a lexicographically sortable string.
193
+
194
+ The `tree_leaf` points out that this request is a leaf in the request chain tree structure.
195
+
196
+ ### Background jobs
197
+
198
+ For background jobs, Kiev will log the following information by default:
199
+
200
+ ```json
201
+ {
202
+ "application":"my_app",
203
+ "event":"job_finished",
204
+ "level":"INFO",
205
+ "timestamp":"2017-01-27T16:11:44.123Z",
206
+ "job_name":"name",
207
+ "params": "...",
208
+ "jid":123,
209
+ "request_id":"UUID",
210
+ "request_depth":0,
211
+ "request_duration":0.000623773,
212
+ "error_message": "...",
213
+ "error_class": "...",
214
+ "error_backtrace": "...",
215
+ "tree_path": "BDF",
216
+ "tree_leaf": true
217
+ }
218
+ ```
219
+
220
+ ### Appending data to the request log entry
221
+
222
+ You can also append **arbitrary data** to the request log by calling:
223
+
224
+ ```ruby
225
+ # Append structured data (will be merged)
226
+ Kiev.payload(first_name: "john", last_name: "smith")
227
+
228
+ # Same thing
229
+ Kiev[:first_name] = "john"
230
+ Kiev[:last_name] = "smith"
231
+ ```
232
+
233
+ ### Other events
234
+
235
+ Kiev allows you to log custom events as well.
236
+
237
+ The recommended way to do this is by using the `#event` method:
238
+
239
+ ```ruby
240
+ # Log event without any data
241
+ Kiev.event(:my_event)
242
+
243
+ # Log structured data (will be merged)
244
+ Kiev.event(:my_event, { some_array: [1, 2, 3] })
245
+
246
+ # Log other data types (will be available under the `message` key)
247
+ Kiev.event(:my_event, "hello world")
248
+ ```
249
+
250
+ However, `Kiev.logger` implements the Ruby `Logger` class, so all the other methods are available as well:
251
+
252
+ ```ruby
253
+ Kiev.logger.info("hello world")
254
+ Kiev.logger.debug({ first_name: "john", last_name: "smith" })
255
+ ```
256
+
257
+ Note that, even when logging custom events, Kiev **will try to append request information** to the entries: the HTTP `verb` and `path` for web request or `job_name` and `jid` for background jobs. The payload, however, will be logged only for the `request_finished` or `job_finished` events. If you want to add a payload to a custom event, use the second argument of the `event` method.
258
+
259
+ ## Advanced configuration
260
+
261
+ ### development_mode
262
+
263
+ Kiev offers human-readable logging for development purposes. You can enable it via the `development_mode` option:
264
+
265
+ ```ruby
266
+ Kiev.configure do |config|
267
+ config.development_mode = Rails.env.development?
268
+ end
269
+ ```
270
+
271
+ ### filtered_params
272
+
273
+ By default, Kiev filters out the values for the following parameters:
274
+
275
+ - client_secret
276
+ - token
277
+ - password,
278
+ - password_confirmation
279
+ - old_password
280
+ - credit_card_number
281
+ - credit_card_cvv
282
+
283
+ You can override this behaviour via the `filtered_params` option:
284
+
285
+ ```ruby
286
+ Kiev.configure do |config|
287
+ config.filtered_params = %w(email first_name last_name)
288
+ end
289
+ ```
290
+
291
+ ### ignored_params
292
+
293
+ By default, Kiev ignores the following parameters:
294
+
295
+ - controller
296
+ - action
297
+ - format
298
+ - authenticity_token
299
+ - utf8
300
+
301
+ You can override this behaviour via the `ignored_params` option:
302
+
303
+ ```ruby
304
+ Kiev.configure do |config|
305
+ config.ignored_params = %w(some_field some_other_field)
306
+ end
307
+ ```
308
+
309
+ ### log_request_condition
310
+
311
+ By default, Kiev doesn't log requests to `/ping` or `/health` or requests to assets.
312
+
313
+ You can override this behaviour via the `log_request_condition` option, which should be a `proc` returning a `boolean`:
314
+
315
+ ```ruby
316
+ Kiev.configure do |config|
317
+ config.log_request_condition = proc do |request, response|
318
+ !%r{(^(/ping|/health))|(\.(js|css|png|jpg|gif)$)}.match(request.path)
319
+ end
320
+ end
321
+ ```
322
+
323
+ ### log_request_error_condition
324
+
325
+ Kiev logs Ruby exceptions. By default, it won't log the exceptions produced by 404s.
326
+
327
+ You can override this behaviour via the `log_request_error_condition` option, which should be a `proc` returning a `boolean`:
328
+
329
+ ```ruby
330
+ Kiev.configure do |config|
331
+ config.log_request_error_condition = proc do |request, response|
332
+ response.status != 404
333
+ end
334
+ end
335
+ ```
336
+
337
+ ### log_response_body_condition
338
+
339
+ Kiev can log the response body. By default, it will only log the response body when the status code is in the 4xx range and the content type is JSON or XML.
340
+
341
+ You can override this behaviour via the `log_response_body_condition` option, which should be a `proc` returning a `boolean`:
342
+
343
+ ```ruby
344
+ Kiev.configure do |config|
345
+ config.log_response_body_condition = proc do |request, response|
346
+ response.status >= 400 && response.status < 500 && response.content_type =~ /(json|xml)/
347
+ end
348
+ end
349
+ ```
350
+
351
+ ### persistent_log_fields
352
+
353
+ If you need to log some data for every event in the session (e.g. the user ID), you can do this via the `persistent_log_fields` option.
354
+
355
+ ```ruby
356
+ Kiev.configure do |config|
357
+ config.persistent_log_fields = [:user_id]
358
+ end
359
+
360
+ # Somewhere in application
361
+ before do
362
+ Kiev[:user_id] = current_user.id
363
+ end
364
+
365
+ get "/" do
366
+ "hello world"
367
+ end
368
+ ```
369
+
370
+ ## nginx
371
+
372
+ If you want to log 499 and 50x errors in nginx, which will not be captured by Ruby application, consider adding this to your nginx configuration:
373
+
374
+ ```
375
+ log_format kiev '{"application":"app_name", "event":"request_finished",'
376
+ '"timestamp":"$time_iso8601", "request_id":"$http_x_request_id",'
377
+ '"user_agent":"$http_user_agent", "status":$status,'
378
+ '"request_duration_seconds":$request_time, "host":"$host",'
379
+ '"verb":"$request_method", "path":"$request_uri", "tree_path": "$http_x_tree_path"}';
380
+
381
+ log_format simple_log '$remote_addr - $remote_user [$time_local] '
382
+ '"$request" $status $bytes_sent '
383
+ '"$http_referer" "$http_user_agent"';
384
+
385
+ map $status $not_loggable {
386
+ ~(499) 0;
387
+ default 1;
388
+ }
389
+
390
+ map $status $loggable {
391
+ ~(499) 1;
392
+ default 0;
393
+ }
394
+
395
+ server {
396
+ access_log /var/log/nginx/access.kiev.log kiev if=$loggable;
397
+ access_log /var/log/nginx/access.log simple_log if=$not_loggable;
398
+
399
+ location = /50x.html {
400
+ access_log /var/log/nginx/access.kiev.log kiev;
401
+ }
402
+ }
403
+ ```
404
+
405
+ If you'd like to measure nginx queue latency, add the following to your nginx configuration:
406
+
407
+ ```
408
+ server {
409
+ ...
410
+ proxy_set_header X-Request-Start "${msec}";
411
+ ...
412
+ }
413
+ ```
414
+
415
+ Other libs/technologies using `X-Request-Start` are [rack-timeout](https://github.com/heroku/rack-timeout) and [NewRelic](https://docs.newrelic.com/docs/apm/applications-menu/features/request-queue-server-configuration-examples). There's no [support for ELB](https://forums.aws.amazon.com/message.jspa?messageID=396283) :(
416
+
417
+ ## Logstash, Logrotate, Filebeat
418
+
419
+ Kiev does not provide facilities to log directly to ElasticSearch. This is done for simplicity. Instead we recommend using [Filebeat](https://www.elastic.co/products/beats/filebeat) to deliver logs to ElasticSearch.
420
+
421
+ When storing logs on disk, we recommend using Logrotate in truncate mode.
422
+
423
+ You can use [jq](https://stedolan.github.io/jq/) to traverse JSON log files, when you're not running Kiev in *development mode*.
424
+
425
+ ## Alternatives
426
+
427
+ ### Logging
428
+
429
+ - [semantic_logger](http://rocketjob.github.io/semantic_logger/)
430
+ - [lograge](https://github.com/roidrage/lograge)
431
+ - [logging](https://github.com/TwP/logging)
432
+
433
+ ### Request-Id
434
+
435
+ - [Pliny::Middleware::RequestID](https://github.com/interagent/pliny/blob/master/lib/pliny/middleware/request_id.rb)
436
+ - [ActionDispatch::RequestId](http://api.rubyonrails.org/classes/ActionDispatch/RequestId.html)
437
+ - [request_id](https://github.com/remind101/request_id)
438
+
439
+ ## Development
440
+
441
+ Pull the code:
442
+
443
+ ```
444
+ git clone git@github.com:blacklane/kiev.git
445
+ ```
446
+
447
+ Run tests:
448
+
449
+ ```sh
450
+ bundle exec rake
451
+ ```
452
+
453
+ Run tests for different rubies, frameworks and framework versions:
454
+
455
+ ```sh
456
+ # Create a Postgres test database for Que
457
+ createdb que_test
458
+
459
+ # Run the tests (replace myuser with your username)
460
+ DATABASE_URL=postgres://myuser:@localhost/que_test bundle exec wwtd
461
+ ```