kiev 4.0.0 → 4.5.0

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/push.yml +80 -0
  3. data/.rubocop.yml +10 -3
  4. data/.ruby-version +1 -1
  5. data/Gemfile +1 -1
  6. data/README.md +63 -2
  7. data/gemfiles/que_0.12.2.gemfile +3 -1
  8. data/gemfiles/que_0.12.3.gemfile +3 -1
  9. data/gemfiles/rails_4.1.gemfile +4 -2
  10. data/gemfiles/rails_4.2.gemfile +4 -2
  11. data/gemfiles/rails_5.2.gemfile +15 -0
  12. data/gemfiles/ruby_kafka.gemfile +11 -0
  13. data/gemfiles/{shoryuken_3.1.gemfile → shoryuken_4.0.gemfile} +4 -2
  14. data/gemfiles/sidekiq_4.2.gemfile +3 -2
  15. data/gemfiles/sinatra_1.4.gemfile +4 -2
  16. data/gemfiles/sinatra_2.0.gemfile +4 -2
  17. data/kiev.gemspec +8 -6
  18. data/lib/kiev.rb +2 -0
  19. data/lib/kiev/aws_sns.rb +16 -0
  20. data/lib/kiev/aws_sns/context_injector.rb +22 -0
  21. data/lib/kiev/base.rb +4 -1
  22. data/lib/kiev/base52.rb +1 -1
  23. data/lib/kiev/config.rb +1 -0
  24. data/lib/kiev/context_reader.rb +6 -3
  25. data/lib/kiev/hanami.rb +19 -0
  26. data/lib/kiev/kafka.rb +22 -0
  27. data/lib/kiev/kafka/context_extractor.rb +26 -0
  28. data/lib/kiev/kafka/context_injector.rb +20 -0
  29. data/lib/kiev/kafka/message_context.rb +27 -0
  30. data/lib/kiev/logger.rb +8 -4
  31. data/lib/kiev/que/job.rb +4 -1
  32. data/lib/kiev/rack/request_id.rb +15 -13
  33. data/lib/kiev/rack/request_logger.rb +15 -1
  34. data/lib/kiev/request_id.rb +2 -1
  35. data/lib/kiev/request_logger.rb +5 -2
  36. data/lib/kiev/shoryuken/context_reader.rb +2 -0
  37. data/lib/kiev/shoryuken/middleware/message_tracer.rb +4 -7
  38. data/lib/kiev/test.rb +2 -1
  39. data/lib/kiev/util.rb +1 -0
  40. data/lib/kiev/version.rb +1 -1
  41. metadata +39 -30
  42. data/.travis.yml +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d931febf4f2ad051147ce2b796985482fe2510de337882f4a0ddf5405dec6dfa
4
- data.tar.gz: c05aa2ff341c312313d2e10da5dbb2d552d7266827ea4f7f62529080013bc34b
3
+ metadata.gz: f160c0e287ac552fab279ebc1974bf2de5704af494766872e1e2abfa11b48766
4
+ data.tar.gz: f2601c9be33ecc63b3741e76832b2b409250a4b5e7365ede3a40cc6a59c0eb6f
5
5
  SHA512:
6
- metadata.gz: 3c57d6b5a18cc053e3e6fdde63d7e9c83fc3348b050d7ffee4bfcc8dbede1ddd843974faf3256e524a0eca3ec2aa0075a25d1917becdaa263c367a1a1d1b660b
7
- data.tar.gz: 9b95396a7c5ed4f1a0a90d607b9bae3eaf1254aa4dbab23e8d7da0a3b2d5e37256d84d19ec8366d317a7194b68cdae3f69dd6f46f90c2e2c04c62d3de336cd2d
6
+ metadata.gz: 9560e9929db4c64e968dc39d2efadb6b9a09cff5ced0a7e5ffacd823c63273bcd05be4e1918b5e4b70586c73f8520e4d62dccc9e83795908f0981d0ed14ca6e1
7
+ data.tar.gz: 4caa6b83331e522867ec3254ac5a3f0356c3336a452ade39e9c8df022f5409a17cc1a024c10cee80353252bec8da66b36aea5430cc4947c59acc0dab0fa1eccd
@@ -0,0 +1,80 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Main CI
9
+
10
+ on:
11
+ push:
12
+ branches: [ master ]
13
+ pull_request:
14
+ branches: [ master ]
15
+
16
+ jobs:
17
+ test:
18
+
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ matrix:
22
+ ruby: ['2.5.1', '2.7.2', '2.6.5']
23
+ gemfile:
24
+ - gemfiles/ruby_kafka.gemfile
25
+ - gemfiles/que_0.12.2.gemfile
26
+ - gemfiles/que_0.12.3.gemfile
27
+ - gemfiles/rails_5.2.gemfile
28
+ - gemfiles/shoryuken_4.0.gemfile
29
+ - gemfiles/sidekiq_4.2.gemfile
30
+ - gemfiles/sinatra_1.4.gemfile
31
+ - gemfiles/sinatra_2.0.gemfile
32
+ allow_failures:
33
+ - false
34
+ include:
35
+ - os: ubuntu
36
+ ruby-version: ruby-head
37
+ gemfile: gemfiles/rails_5.2.gemfile
38
+ allow_failures: true
39
+ env:
40
+ BUNDLE_GEMFILE: "${{ matrix.gemfile }}"
41
+ ALLOW_FAILURES: "${{ matrix.allow_failures }}"
42
+ REDIS_URL: "redis://localhost:6379/4"
43
+ DATABASE_URL: ${{ (startsWith(matrix.gemfile,'gemfiles/que') && 'postgres://postgres:postgres@localhost:5432/que_test') || 'sqlite3:db/combustion_test.sqlite'}}
44
+ continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }}
45
+
46
+ # Service containers to run with `container-job`
47
+ services:
48
+ redis:
49
+ image: redis:latest
50
+ ports:
51
+ - 6379:6379
52
+ # Label used to access the service container
53
+ postgres:
54
+ # Docker Hub image
55
+ image: postgres:9.4
56
+ # Provide the password for postgres
57
+ env:
58
+ POSTGRES_PASSWORD: postgres
59
+ POSTGRES_DB: que_test
60
+ # Set health checks to wait until postgres has started
61
+ ports:
62
+ - 5432:5432
63
+ options: >-
64
+ --health-cmd pg_isready
65
+ --health-interval 10s
66
+ --health-timeout 5s
67
+ --health-retries 5
68
+ steps:
69
+ - uses: actions/checkout@v2
70
+ - name: Set up Ruby
71
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
72
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
73
+ # uses: ruby/setup-ruby@v1
74
+ uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
75
+ with:
76
+ ruby-version: ${{ matrix.ruby }}
77
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
78
+
79
+ - name: Run tests
80
+ run: bundle exec rake || $ALLOW_FAILURES
data/.rubocop.yml CHANGED
@@ -1,10 +1,17 @@
1
1
  inherit_from:
2
2
  - https://raw.githubusercontent.com/blacklane/rubocop/master/rubocop.yml
3
3
 
4
- Lint/HandleExceptions:
4
+ AllCops:
5
+ TargetRubyVersion: 2.5
5
6
  Exclude:
6
- - test/rails_integration_test.rb
7
- - test/sinatra_integration_test.rb
7
+ - test/rails_app/**/*.rb # auto-generated
8
+ - spec/**/*.rb
9
+ - test/**/*.rb
10
+ - vendor/bundle/**/*
11
+ Lint/SuppressedException:
12
+ Exclude:
13
+ - test/**/*.rb
14
+ - spec/**/*.rb
8
15
  Lint/RescueException:
9
16
  Exclude:
10
17
  - lib/kiev/request_body_filter/json.rb
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.3.3
1
+ 2.5.1
data/Gemfile CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- eval_gemfile(File.join(File.dirname(__FILE__), "gemfiles/rails_4.1.gemfile"))
3
+ eval_gemfile(File.join(File.dirname(__FILE__), "gemfiles/rails_5.2.gemfile"))
4
4
 
5
5
  gem "wwtd"
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Kiev [![Build Status](https://travis-ci.org/blacklane/kiev.svg?branch=master)](https://travis-ci.org/blacklane/kiev)
1
+ # Kiev [![Build Status](https://github.com/blacklane/kiev/workflows/Main%20CI/badge.svg?branch=master)](https://github.com/blacklane/kiev/actions?query=workflow%3A%22Main+CI%22) [![Gem Version](https://badge.fury.io/rb/kiev.svg)](https://badge.fury.io/rb/kiev)
2
2
 
3
3
  Kiev is a comprehensive logging library aimed at covering a wide range of frameworks and tools from the Ruby ecosystem:
4
4
 
@@ -107,6 +107,35 @@ end
107
107
  run(app)
108
108
  ```
109
109
 
110
+ ### Hanami
111
+
112
+ Place your configuration under `config/initializers/kiev.rb`:
113
+
114
+ ```ruby
115
+ require "kiev"
116
+
117
+ Kiev.configure do |config|
118
+ config.app = :my_app
119
+ config.development_mode = Hanami.env?(:development)
120
+ config.log_path = File.join("log", "structured.log")
121
+ end
122
+ ```
123
+
124
+ Within your `MyApp::Application` file, include the `Kiev::Hanami` module, in order to register the middleware stack.
125
+ The `include` should be added before `configure` block.
126
+
127
+ ```ruby
128
+ module MyApp
129
+ class Application < Hanami::Application
130
+ include Kiev::Hanami
131
+
132
+ configure do
133
+ # ...
134
+ end
135
+ end
136
+ end
137
+ ```
138
+
110
139
  ### Sidekiq
111
140
 
112
141
  Add the following lines to your initializer code:
@@ -125,6 +154,36 @@ Kiev::Shoryuken.enable
125
154
 
126
155
  The name of the worker class is not logged by default. Configure [`persistent_log_fields` option](#persistent_log_fields) to include `"shoryuken_class"` if you want this.
127
156
 
157
+ ### AWS SNS
158
+
159
+ To enhance messages published to SNS topics you can use the ContextInjector:
160
+
161
+ ```ruby
162
+ sns_message = { topic_arn: "...", message: "{...}" }
163
+ Kiev::Kafka.inject_context(sns_message[:message_attributes])
164
+
165
+ ```
166
+
167
+ After this operation the message attributes will also include required context for the Kiev logger.
168
+
169
+ ### Kafka
170
+
171
+ To enhance messages published to Kafka topics you can use the ContextInjector:
172
+
173
+ ```ruby
174
+ Kiev::Kafka.inject_context(headers)
175
+ ```
176
+
177
+ After this operation the headers variable will also include required context for the Kiev logger.
178
+
179
+ If you have a consumed `Kafka::FetchedMessage` you can extract logger context with:
180
+
181
+ ```ruby
182
+ Kiev::Kafka.extract_context(message)
183
+ ```
184
+
185
+ This will work regardless if headers are in HTTP format, e.g. `X-Tracking-Id` or plain field names: `tracking_id`. Plus the `message_key` field will contain the key of processed message. In case you want to log some more fields configure `persistent_log_fields` and `jobs_propagated_fields`.
186
+
128
187
  ### Que
129
188
 
130
189
  Add the following lines to your initializer code:
@@ -190,7 +249,9 @@ For web requests the Kiev middleware will log the following information by defau
190
249
 
191
250
  * `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.
192
251
 
193
- * `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).
252
+ * `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). (currently deprecated in favor of a new name: `tracking_id`)
253
+
254
+ * `tracking_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). If not provided the value is seeded from deprecated `request_id`.
194
255
 
195
256
  * `request_depth` represents the position of the current request within a chain of requests. It starts with 0.
196
257
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gem "oj"
@@ -11,4 +13,4 @@ gem "rack-test", require: false
11
13
  gem "rspec", require: false
12
14
  gem "minitest-reporters", require: false
13
15
 
14
- gemspec :path => "../"
16
+ gemspec path: "../"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  # need it because of bug in https://github.com/chanks/que/issues/191
@@ -12,4 +14,4 @@ gem "rack-test", require: false
12
14
  gem "rspec", require: false
13
15
  gem "minitest-reporters", require: false
14
16
 
15
- gemspec :path => "../"
17
+ gemspec path: "../"
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gem "oj"
4
6
 
5
7
  gem "rails", "4.1.16"
6
- gem "sqlite3"
8
+ gem "sqlite3", "1.3.13"
7
9
 
8
10
  gem "combustion"
9
11
  gem "rspec", require: false
10
12
  gem "rspec-rails", require: false
11
13
  gem "minitest-reporters", require: false
12
14
 
13
- gemspec :path => "../"
15
+ gemspec path: "../"
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gem "oj"
4
6
 
5
7
  gem "rails", "4.2.7"
6
- gem "sqlite3"
8
+ gem "sqlite3", "1.3.13"
7
9
 
8
10
  gem "combustion"
9
11
  gem "rspec", require: false
10
12
  gem "rspec-rails", require: false
11
13
  gem "minitest-reporters", require: false
12
14
 
13
- gemspec :path => "../"
15
+ gemspec path: "../"
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "oj"
6
+
7
+ gem "rails", "~> 5.2.4"
8
+ gem "sqlite3", "1.3.13"
9
+
10
+ gem "combustion"
11
+ gem "rspec", require: false
12
+ gem "rspec-rails", require: false
13
+ gem "minitest-reporters", require: false
14
+
15
+ gemspec path: "../"
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "ruby-kafka", "~> 0.7.10"
6
+
7
+ gem "rack-test", require: false
8
+ gem "rspec", require: false
9
+ gem "minitest-reporters", require: false
10
+
11
+ gemspec path: "../"
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gem "aws-sdk", "~> 2.0"
4
- gem "shoryuken", "~> 3.1.0"
6
+ gem "shoryuken", "~> 4.0"
5
7
 
6
8
  gem "rack-test", require: false
7
9
  gem "minitest-reporters", require: false
8
10
 
9
- gemspec :path => "../"
11
+ gemspec path: "../"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gem "oj", "~> 2"
@@ -10,5 +12,4 @@ gem "minitest-reporters", require: false
10
12
 
11
13
  gem "her"
12
14
 
13
- gemspec :path => "../"
14
-
15
+ gemspec path: "../"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gem "oj"
@@ -6,10 +8,10 @@ gem "xml-simple"
6
8
 
7
9
  gem "sinatra", "1.4.7"
8
10
  gem "sinatra-contrib"
9
- gem "rack-parser", :require => "rack/parser"
11
+ gem "rack-parser", require: "rack/parser"
10
12
 
11
13
  gem "rack-test", require: false
12
14
  gem "rspec", require: false
13
15
  gem "minitest-reporters", require: false
14
16
 
15
- gemspec :path => "../"
17
+ gemspec path: "../"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gem "oj"
@@ -6,10 +8,10 @@ gem "xml-simple"
6
8
 
7
9
  gem "sinatra", "2.0.0"
8
10
  gem "sinatra-contrib"
9
- gem "rack-parser", :require => "rack/parser"
11
+ gem "rack-parser", require: "rack/parser"
10
12
 
11
13
  gem "rack-test", require: false
12
14
  gem "rspec", require: false
13
15
  gem "minitest-reporters", require: false
14
16
 
15
- gemspec :path => "../"
17
+ gemspec path: "../"
data/kiev.gemspec CHANGED
@@ -9,7 +9,9 @@ Gem::Specification.new do |spec|
9
9
  spec.licenses = ["MIT"]
10
10
 
11
11
  spec.summary = "Distributed logging to JSON integrated with various Ruby frameworks and tools"
12
- spec.description = "Kiev is a logging tool aimed at distributed environments. It logs to JSON, while providing human-readable output in development mode. It integrates nicely with Rails, Sinatra and other Rack-based frameworks, Sidekiq, Que, HTTParty, Her and other Faraday-based HTTP clients."
12
+ spec.description = "Kiev is a logging tool aimed at distributed environments. It logs to JSON, while providing "\
13
+ "human-readable output in development mode. It integrates nicely with Rails, Sinatra and other"\
14
+ " Rack-based frameworks, Sidekiq, Que, HTTParty, Her and other Faraday-based HTTP clients."
13
15
  spec.homepage = "https://github.com/blacklane/kiev"
14
16
 
15
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
@@ -17,12 +19,12 @@ Gem::Specification.new do |spec|
17
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
20
  spec.require_paths = ["lib"]
19
21
 
20
- spec.required_ruby_version = ">= 2.0.0"
22
+ spec.required_ruby_version = ">= 2.5"
23
+ spec.add_dependency "oga", "~> 2.2"
21
24
  spec.add_dependency "rack", ">= 1", "< 3"
22
25
  spec.add_dependency "request_store", ">= 1.0", "< 1.4"
23
- spec.add_dependency "oga", "~> 2.2"
24
26
  spec.add_dependency "ruby_dig", "~> 0.0.2" # to support ruby 2.2
25
- spec.add_development_dependency "rake"
26
- spec.add_development_dependency "rspec"
27
- spec.add_development_dependency "rubocop", "0.49.1"
27
+ spec.add_development_dependency "rake", "~> 0"
28
+ spec.add_development_dependency "rspec", "~> 3.10"
29
+ spec.add_development_dependency "rubocop", "~> 0.54"
28
30
  end
data/lib/kiev.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "kiev/base"
4
+ require_relative "kiev/aws_sns" if defined?(AWS::SNS)
5
+ require_relative "kiev/kafka" if defined?(Kafka)
4
6
  require_relative "kiev/rack" if defined?(Rack)
5
7
  require_relative "kiev/railtie" if defined?(Rails)
6
8
  require_relative "kiev/sidekiq" if defined?(Sidekiq)
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Kiev
6
+ module AwsSns
7
+ require_relative "kafka/context_injector"
8
+
9
+ class << self
10
+ # @param [Hash] headers
11
+ def inject_context(headers = {})
12
+ Kiev::AwsSns::ContextInjector.new.call(headers)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kiev/config"
4
+ require "kiev/subrequest_helper"
5
+
6
+ module Kiev
7
+ module AwsSns
8
+ class ContextInjector
9
+ # @param [Hash] message_attributes Injects context headers
10
+ # @return [Hash]
11
+ def call(message_attributes = {})
12
+ Kiev::SubrequestHelper.payload.each do |key, value|
13
+ message_attributes[key] = {
14
+ data_type: "String",
15
+ string_value: value.to_s
16
+ }
17
+ end
18
+ message_attributes
19
+ end
20
+ end
21
+ end
22
+ end
data/lib/kiev/base.rb CHANGED
@@ -12,6 +12,7 @@ require_relative "version"
12
12
  require_relative "config"
13
13
  require_relative "util"
14
14
  require_relative "subrequest_helper"
15
+ require_relative "hanami"
15
16
 
16
17
  module Kiev
17
18
  class << self
@@ -46,7 +47,9 @@ module Kiev
46
47
  end
47
48
 
48
49
  def request_id
49
- RequestStore.store[:request_id]
50
+ RequestStore.store[:tracking_id]
50
51
  end
52
+
53
+ alias_method :tracking_id, :request_id
51
54
  end
52
55
  end
data/lib/kiev/base52.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Kiev
4
4
  module Base52
5
5
  KEYS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".freeze
6
- BASE = KEYS.length.freeze
6
+ BASE = KEYS.length
7
7
 
8
8
  def self.encode(num)
9
9
  return KEYS[0] if num == 0
data/lib/kiev/config.rb CHANGED
@@ -63,6 +63,7 @@ module Kiev
63
63
  ) << :tempfile).freeze
64
64
 
65
65
  DEFAULT_HTTP_PROPAGATED_FIELDS = {
66
+ tracking_id: "X-Tracking-Id",
66
67
  request_id: "X-Request-Id",
67
68
  request_depth: "X-Request-Depth",
68
69
  tree_path: "X-Tree-Path"
@@ -6,6 +6,7 @@ module Kiev
6
6
  # change field lookup.
7
7
  class ContextReader
8
8
  REQUEST_ID = "request_id"
9
+ TRACKING_ID = "tracking_id"
9
10
  REQUEST_DEPTH = "request_depth"
10
11
  TREE_PATH = "tree_path"
11
12
 
@@ -17,12 +18,14 @@ module Kiev
17
18
  subject[key]
18
19
  end
19
20
 
20
- def request_id
21
- self[REQUEST_ID] || SecureRandom.uuid
21
+ def tracking_id
22
+ self[TRACKING_ID] || self[REQUEST_ID] || SecureRandom.uuid
22
23
  end
23
24
 
25
+ alias_method :request_id, :tracking_id
26
+
24
27
  def tree_root?
25
- !self[REQUEST_ID]
28
+ !self[TRACKING_ID] && !self[REQUEST_ID]
26
29
  end
27
30
 
28
31
  def request_depth
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rack/request_logger"
4
+ require_relative "rack/store_request_details"
5
+ require_relative "rack/request_id"
6
+
7
+ module Kiev
8
+ module Hanami
9
+ def self.included(base)
10
+ base.configure do
11
+ # The order is important
12
+ middleware.use(::RequestStore::Middleware)
13
+ middleware.use(Kiev::Rack::RequestLogger)
14
+ middleware.use(Kiev::Rack::StoreRequestDetails)
15
+ middleware.use(Kiev::Rack::RequestId)
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/kiev/kafka.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Kiev
6
+ module Kafka
7
+ require_relative "kafka/context_extractor"
8
+ require_relative "kafka/context_injector"
9
+
10
+ class << self
11
+ # @param [Kafka::FetchedMessage] message
12
+ def extract_context(message)
13
+ Kiev::Kafka::ContextExtractor.new.call(message)
14
+ end
15
+
16
+ # @param [Hash] headers
17
+ def inject_context(headers = {})
18
+ Kiev::Kafka::ContextInjector.new.call(headers)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "message_context"
4
+ require "kiev/request_id"
5
+ require "kiev/context_reader"
6
+
7
+ module Kiev
8
+ module Kafka
9
+ class ContextExtractor
10
+ include Kiev::RequestId::Mixin
11
+
12
+ # @param [Kafka::FetchedMessage] message
13
+ def call(message)
14
+ context = Kiev::Kafka::MessageContext.new(message)
15
+ context_reader = Kiev::ContextReader.new(context)
16
+ wrap_request_id(context_reader) {}
17
+
18
+ Kiev[:message_key] = message.key
19
+
20
+ Config.instance.jobs_propagated_fields.each do |key|
21
+ Kiev[key] = context_reader[key]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kiev/config"
4
+ require "kiev/subrequest_helper"
5
+
6
+ module Kiev
7
+ module Kafka
8
+ class ContextInjector
9
+ # @param [Hash] headers Injects context headers
10
+ # @return [Hash]
11
+ def call(headers = {})
12
+ Kiev::SubrequestHelper.payload.each do |key, value|
13
+ field_key = Kiev::Config::DEFAULT_HTTP_PROPAGATED_FIELDS.fetch(key.to_sym, key)
14
+ headers[field_key] = value
15
+ end
16
+ headers
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kiev
4
+ module Kafka
5
+ class MessageContext
6
+ # @param [Kafka::FetchedMessage] message
7
+ def initialize(message)
8
+ @headers = message.headers
9
+ end
10
+
11
+ def value(field)
12
+ headers[header_key(field)] || headers[field.to_s]
13
+ end
14
+
15
+ alias_method :[], :value
16
+
17
+ private
18
+
19
+ attr_reader :headers
20
+
21
+ # @param [String] field
22
+ def header_key(field)
23
+ "x_#{field}".gsub("_", " ").split.map(&:capitalize).join("-")
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/kiev/logger.rb CHANGED
@@ -13,6 +13,8 @@ module Kiev
13
13
  def_delegators(*([:@logger] + ::Logger.instance_methods(false)))
14
14
 
15
15
  DEFAULT_EVENT_NAME = "log"
16
+ LOG_ERROR = "ERROR"
17
+ ERROR_STATUS = 500
16
18
 
17
19
  FORMATTER = proc do |severity, time, event_name, data|
18
20
  entry =
@@ -21,6 +23,7 @@ module Kiev
21
23
  event: event_name || DEFAULT_EVENT_NAME,
22
24
  level: severity,
23
25
  timestamp: time.utc,
26
+ tracking_id: RequestStore.store[:tracking_id],
24
27
  request_id: RequestStore.store[:request_id],
25
28
  request_depth: RequestStore.store[:request_depth],
26
29
  tree_path: RequestStore.store[:tree_path]
@@ -54,8 +57,11 @@ module Kiev
54
57
  entry.merge!(data)
55
58
  elsif !data.nil?
56
59
  entry[:message] = data.to_s
60
+ entry[:status] = ERROR_STATUS if data.to_s.downcase.include?(LOG_ERROR)
57
61
  end
58
62
 
63
+ entry[:level] = LOG_ERROR if entry[:status].to_i.between?(400, 599)
64
+
59
65
  # Save some disk space
60
66
  entry.reject! { |_, value| value.nil? }
61
67
 
@@ -87,10 +93,8 @@ module Kiev
87
93
  entry << "(#{duration}ms)" if duration
88
94
  entry << "\n"
89
95
 
90
- meta = {
91
- request_id: RequestStore.store[:request_id],
92
- request_depth: RequestStore.store[:request_depth]
93
- }.merge!(Hash(RequestStore.store[:payload]))
96
+ meta = RequestStore.store.slice(:trakcing_id, :request_id, :request_depth)
97
+ .reverse_merge!(Hash(RequestStore.store[:payload]))
94
98
 
95
99
  meta.reject! { |_, value| value.nil? }
96
100
 
data/lib/kiev/que/job.rb CHANGED
@@ -27,6 +27,7 @@ module Kiev
27
27
  private
28
28
 
29
29
  NEW_LINE = "\n"
30
+ LOG_ERROR = "ERROR"
30
31
 
31
32
  def kiev_run
32
33
  args = attrs[:args]
@@ -46,7 +47,8 @@ module Kiev
46
47
  Kiev[key] = payload[key]
47
48
  end
48
49
  request_store = Kiev::RequestStore.store
49
- request_store[:request_id] = payload[:request_id]
50
+ request_store[:tracking_id] = payload[:tracking_id]
51
+ request_store[:request_id] = payload[:tracking_id] || payload[:request_id]
50
52
  request_store[:request_depth] = payload[:request_depth].to_i + 1
51
53
  request_store[:tree_path] = payload[:tree_path]
52
54
 
@@ -69,6 +71,7 @@ module Kiev
69
71
  data[:error_class] = error.class.name
70
72
  data[:error_message] = error.message[0..5000]
71
73
  data[:error_backtrace] = Array(error.backtrace).join(NEW_LINE)[0..5000]
74
+ data[:level] = LOG_ERROR
72
75
  end
73
76
 
74
77
  Kiev.event(:job_finished, data)
@@ -14,24 +14,26 @@ module Kiev
14
14
 
15
15
  def call(env)
16
16
  request_id_header_out = to_rack(:request_id)
17
- request_id_header_in = to_http(:request_id)
17
+ tracking_id_header_out = to_rack(:tracking_id)
18
18
 
19
- request_id = make_request_id(env[RAILS_REQUEST_ID] || env[request_id_header_in])
20
- RequestStore.store[:request_id] = request_id
19
+ tracking_id = make_tracking_id(env[to_http(:tracking_id)] || env[RAILS_REQUEST_ID] || env[to_http(:request_id)])
20
+ RequestStore.store[:tracking_id] = tracking_id
21
+ RequestStore.store[:request_id] = tracking_id
21
22
  RequestStore.store[:request_depth] = request_depth(env)
22
23
  RequestStore.store[:tree_path] = tree_path(env)
23
24
 
24
- @app.call(env).tap { |_status, headers, _body| headers[request_id_header_out] = request_id }
25
+ @app.call(env).tap do |_status, headers, _body|
26
+ headers[tracking_id_header_out] = tracking_id
27
+ headers[request_id_header_out] = tracking_id
28
+ end
25
29
  end
26
30
 
27
31
  private
28
32
 
29
- # TODO: in Rails 5 they set `headers[X_REQUEST_ID]`, so this will not work
30
- # https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/request_id.rb
31
- # https://github.com/interagent/pliny/blob/master/lib/pliny/middleware/request_id.rb
32
33
  def tree_root?(env)
34
+ tracking_id_header_in = to_http(:tracking_id)
33
35
  request_id_header_in = to_http(:request_id)
34
- !env[request_id_header_in]
36
+ !env[tracking_id_header_in] && !env[request_id_header_in]
35
37
  end
36
38
 
37
39
  def request_depth(env)
@@ -52,15 +54,15 @@ module Kiev
52
54
  Config.instance.all_http_propagated_fields[value]
53
55
  end
54
56
 
55
- def make_request_id(request_id)
56
- if request_id.nil? || request_id.empty?
57
- internal_request_id
57
+ def make_tracking_id(tracking_id)
58
+ if tracking_id.nil? || tracking_id.empty?
59
+ internal_tracking_id
58
60
  else
59
- Util.sanitize(request_id)
61
+ Util.sanitize(tracking_id)
60
62
  end
61
63
  end
62
64
 
63
- def internal_request_id
65
+ def internal_tracking_id
64
66
  SecureRandom.uuid
65
67
  end
66
68
  end
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "zlib"
4
+
3
5
  module Kiev
4
6
  module Rack
5
7
  class RequestLogger
6
8
  ERROR_STATUS = 500
7
9
  ERROR_HEADERS = [].freeze
8
10
  ERROR_BODY = [""].freeze
11
+ LOG_ERROR = "ERROR"
9
12
 
10
13
  def initialize(app)
11
14
  @app = app
@@ -94,6 +97,8 @@ module Kiev
94
97
  route: extract_route(env)
95
98
  }
96
99
 
100
+ data[:level] = LOG_ERROR if data[:status].to_i.between?(400, 599)
101
+
97
102
  if env[HTTP_X_REQUEST_START]
98
103
  data[:request_latency] = ((began_at - env[HTTP_X_REQUEST_START].to_f) * 1000).round(3)
99
104
  end
@@ -115,6 +120,15 @@ module Kiev
115
120
  full_body << str
116
121
  end
117
122
  data[:body] = full_body.join
123
+ if data[:body] && !data[:body].empty? && response.headers["Content-Encoding"] == "gzip"
124
+ begin
125
+ sio = StringIO.new(data[:body])
126
+ gz = Zlib::GzipReader.new(sio)
127
+ data[:body] = gz.read
128
+ rescue Zlib::GzipFile::Error => e
129
+ data[:gzip_parse_error] = e.message
130
+ end
131
+ end
118
132
  end
119
133
 
120
134
  should_log_errors = config.log_request_error_condition.call(request, response)
@@ -122,8 +136,8 @@ module Kiev
122
136
  data[:error_class] = exception.class.name
123
137
  data[:error_message] = exception.message[0..5000]
124
138
  data[:error_backtrace] = Array(exception.backtrace).join(NEW_LINE)[0..5000]
139
+ data[:level] = LOG_ERROR
125
140
  end
126
-
127
141
  data
128
142
  end
129
143
 
@@ -7,7 +7,8 @@ module Kiev
7
7
 
8
8
  def wrap_request_id(context_reader, &_block)
9
9
  request_store = Kiev::RequestStore.store
10
- request_store[:request_id] = context_reader.request_id
10
+ request_store[:tracking_id] = context_reader.tracking_id || context_reader.request_id
11
+ request_store[:request_id] = request_store[:tracking_id]
11
12
  request_store[:request_depth] = context_reader.request_depth
12
13
  request_store[:tree_path] = context_reader.tree_path
13
14
  yield
@@ -4,6 +4,7 @@ module Kiev
4
4
  module RequestLogger
5
5
  module Mixin
6
6
  NEW_LINE = "\n"
7
+ LOG_ERROR = "ERROR"
7
8
 
8
9
  def wrap_request_logger(event, **data, &_block)
9
10
  began_at = Time.now
@@ -11,8 +12,8 @@ module Kiev
11
12
 
12
13
  begin
13
14
  return_value = yield
14
- rescue StandardError => exception
15
- error = exception
15
+ rescue StandardError => e
16
+ error = e
16
17
  end
17
18
 
18
19
  begin
@@ -21,11 +22,13 @@ module Kiev
21
22
  data[:error_class] = error.class.name
22
23
  data[:error_message] = error.message[0..5000]
23
24
  data[:error_backtrace] = Array(error.backtrace).join(NEW_LINE)[0..5000]
25
+ data[:level] = LOG_ERROR
24
26
  end
25
27
 
26
28
  Kiev.event(event, data)
27
29
  ensure
28
30
  raise error if error
31
+
29
32
  return_value
30
33
  end
31
34
  end
@@ -12,8 +12,10 @@ module Kiev
12
12
 
13
13
  def [](key)
14
14
  return unless @message_attributes.key?(key)
15
+
15
16
  attribute_value = @message_attributes[key]
16
17
  return unless attribute_value.data_type == "String"
18
+
17
19
  attribute_value.string_value
18
20
  end
19
21
  end
@@ -1,17 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "kiev/aws_sns/context_injector"
4
+
3
5
  module Kiev
4
6
  module Shoryuken
5
7
  module Middleware
6
8
  class MessageTracer
7
9
  def call(options)
8
- attrbutes = options[:message_attributes] ||= {}
9
- SubrequestHelper.payload.each do |key, value|
10
- attrbutes[key] = {
11
- data_type: "String",
12
- string_value: value.to_s
13
- }
14
- end
10
+ options[:message_attributes] ||= {}
11
+ Kiev::AwsSns::ContextInjector.new.call(options[:message_attributes])
15
12
  yield
16
13
  end
17
14
  end
data/lib/kiev/test.rb CHANGED
@@ -25,8 +25,9 @@ module Kiev
25
25
 
26
26
  def entries
27
27
  return @logs unless @logs.empty?
28
+
28
29
  @logs = raw_logs.each_line.map(&::JSON.method(:parse))
29
- rescue
30
+ rescue StandardError
30
31
  puts raw_logs
31
32
  raise
32
33
  end
data/lib/kiev/util.rb CHANGED
@@ -4,6 +4,7 @@ module Kiev
4
4
  module Util
5
5
  def self.sanitize(value)
6
6
  return unless value
7
+
7
8
  value.gsub(/[^\w\-]/, "")[0...255]
8
9
  end
9
10
 
data/lib/kiev/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kiev
4
- VERSION = "4.0.0"
4
+ VERSION = "4.5.0"
5
5
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kiev
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Blacklane
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-07-19 00:00:00.000000000 Z
11
+ date: 2021-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: oga
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.2'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rack
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -50,20 +64,6 @@ dependencies:
50
64
  - - "<"
51
65
  - !ruby/object:Gem::Version
52
66
  version: '1.4'
53
- - !ruby/object:Gem::Dependency
54
- name: oga
55
- requirement: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - "~>"
58
- - !ruby/object:Gem::Version
59
- version: '2.2'
60
- type: :runtime
61
- prerelease: false
62
- version_requirements: !ruby/object:Gem::Requirement
63
- requirements:
64
- - - "~>"
65
- - !ruby/object:Gem::Version
66
- version: '2.2'
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: ruby_dig
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -82,44 +82,44 @@ dependencies:
82
82
  name: rake
83
83
  requirement: !ruby/object:Gem::Requirement
84
84
  requirements:
85
- - - ">="
85
+ - - "~>"
86
86
  - !ruby/object:Gem::Version
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
90
  version_requirements: !ruby/object:Gem::Requirement
91
91
  requirements:
92
- - - ">="
92
+ - - "~>"
93
93
  - !ruby/object:Gem::Version
94
94
  version: '0'
95
95
  - !ruby/object:Gem::Dependency
96
96
  name: rspec
97
97
  requirement: !ruby/object:Gem::Requirement
98
98
  requirements:
99
- - - ">="
99
+ - - "~>"
100
100
  - !ruby/object:Gem::Version
101
- version: '0'
101
+ version: '3.10'
102
102
  type: :development
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  requirements:
106
- - - ">="
106
+ - - "~>"
107
107
  - !ruby/object:Gem::Version
108
- version: '0'
108
+ version: '3.10'
109
109
  - !ruby/object:Gem::Dependency
110
110
  name: rubocop
111
111
  requirement: !ruby/object:Gem::Requirement
112
112
  requirements:
113
- - - '='
113
+ - - "~>"
114
114
  - !ruby/object:Gem::Version
115
- version: 0.49.1
115
+ version: '0.54'
116
116
  type: :development
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
- - - '='
120
+ - - "~>"
121
121
  - !ruby/object:Gem::Version
122
- version: 0.49.1
122
+ version: '0.54'
123
123
  description: Kiev is a logging tool aimed at distributed environments. It logs to
124
124
  JSON, while providing human-readable output in development mode. It integrates nicely
125
125
  with Rails, Sinatra and other Rack-based frameworks, Sidekiq, Que, HTTParty, Her
@@ -129,11 +129,11 @@ executables: []
129
129
  extensions: []
130
130
  extra_rdoc_files: []
131
131
  files:
132
+ - ".github/workflows/push.yml"
132
133
  - ".gitignore"
133
134
  - ".rspec"
134
135
  - ".rubocop.yml"
135
136
  - ".ruby-version"
136
- - ".travis.yml"
137
137
  - Gemfile
138
138
  - LICENSE.md
139
139
  - README.md
@@ -144,20 +144,29 @@ files:
144
144
  - gemfiles/que_0.12.3.gemfile
145
145
  - gemfiles/rails_4.1.gemfile
146
146
  - gemfiles/rails_4.2.gemfile
147
- - gemfiles/shoryuken_3.1.gemfile
147
+ - gemfiles/rails_5.2.gemfile
148
+ - gemfiles/ruby_kafka.gemfile
149
+ - gemfiles/shoryuken_4.0.gemfile
148
150
  - gemfiles/sidekiq_4.2.gemfile
149
151
  - gemfiles/sinatra_1.4.gemfile
150
152
  - gemfiles/sinatra_2.0.gemfile
151
153
  - kiev.gemspec
152
154
  - lib/ext/rack/common_logger.rb
153
155
  - lib/kiev.rb
156
+ - lib/kiev/aws_sns.rb
157
+ - lib/kiev/aws_sns/context_injector.rb
154
158
  - lib/kiev/base.rb
155
159
  - lib/kiev/base52.rb
156
160
  - lib/kiev/config.rb
157
161
  - lib/kiev/context_reader.rb
162
+ - lib/kiev/hanami.rb
158
163
  - lib/kiev/her_ext/client_request_id.rb
159
164
  - lib/kiev/httparty.rb
160
165
  - lib/kiev/json.rb
166
+ - lib/kiev/kafka.rb
167
+ - lib/kiev/kafka/context_extractor.rb
168
+ - lib/kiev/kafka/context_injector.rb
169
+ - lib/kiev/kafka/message_context.rb
161
170
  - lib/kiev/logger.rb
162
171
  - lib/kiev/param_filter.rb
163
172
  - lib/kiev/que/job.rb
@@ -206,7 +215,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
206
215
  requirements:
207
216
  - - ">="
208
217
  - !ruby/object:Gem::Version
209
- version: 2.0.0
218
+ version: '2.5'
210
219
  required_rubygems_version: !ruby/object:Gem::Requirement
211
220
  requirements:
212
221
  - - ">="
@@ -214,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
223
  version: '0'
215
224
  requirements: []
216
225
  rubyforge_project:
217
- rubygems_version: 2.7.7
226
+ rubygems_version: 2.7.6
218
227
  signing_key:
219
228
  specification_version: 4
220
229
  summary: Distributed logging to JSON integrated with various Ruby frameworks and tools
data/.travis.yml DELETED
@@ -1,28 +0,0 @@
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/shoryuken_3.1.gemfile
24
- - gemfiles/sidekiq_4.2.gemfile
25
- - gemfiles/sinatra_1.4.gemfile
26
- - gemfiles/sinatra_2.0.gemfile
27
- matrix:
28
- fast_finish: true