Kiev
A set of tools to do distributed logging for Ruby web applications
Install / Use
/learn @blacklane/KievREADME
Kiev

Kiev is a comprehensive logging library aimed at covering a wide range of frameworks and tools from the Ruby ecosystem:
- Rails
- Sinatra
- Rack and other Rack-based frameworks
- Sidekiq
- Que
- Shoryuken
- Her and other Faraday-based libraries
- HTTParty
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.
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.
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.
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.
Install
Add the gem to your Gemfile:
gem "kiev"
Don't forget to bundle install.
Configure
Rails
Place your configuration under config/initializers/kiev.rb:
require "kiev"
Kiev.configure do |config|
config.app = :my_app
config.development_mode = Rails.env.development?
config.log_path = Rails.root.join("log", "structured.log") unless Rails.env.development? || $stdout.isatty
end
The middleware stack is included automatically via a Railtie.
Sinatra
Somewhere in your code, ideally before the server configuration, add the following lines:
require "kiev"
Kiev.configure do |config|
config.app = :my_app
config.log_path = File.join("log", "structured.log")
end
Within your Sinatra::Base implementation, include the Kiev::Rack module, in order to register the middleware stack:
require "kiev"
require "sinatra/base"
class MyController < Sinatra::Base
include Kiev::Rack
use SomeOtherMiddleware
get "/hello" do
"world"
end
end
Rack
Somewhere in your code, ideally before the server configuration, add the following lines:
require "kiev"
Kiev.configure do |config|
config.app = :my_app
config.log_path = File.join("log", "structured.log")
end
Within your Rack::Builder implementation, include the Kiev::Rack module, in order to register the middleware stack:
require "kiev"
require "rack"
app = Rack::Builder.new do
include Kiev::Rack
use SomeOtherMiddleware
run labmda { |env| [ 200, {}, [ "hello world" ] ] }
end
run(app)
Hanami
Place your configuration under config/initializers/kiev.rb:
require "kiev"
Kiev.configure do |config|
config.app = :my_app
config.development_mode = Hanami.env?(:development)
config.log_path = File.join("log", "structured.log")
end
Within your MyApp::Application file, include the Kiev::Hanami module, in order to register the middleware stack.
The include should be added before configure block.
module MyApp
class Application < Hanami::Application
include Kiev::Hanami
configure do
# ...
end
end
end
Sidekiq
Add the following lines to your initializer code:
Kiev::Sidekiq.enable
Shoryuken
Add the following lines to your initializer code:
Kiev::Shoryuken.enable
The name of the worker class is not logged by default. Configure persistent_log_fields option to include "shoryuken_class" if you want this.
AWS SNS
To enhance messages published to SNS topics you can use the ContextInjector:
sns_message = { topic_arn: "...", message: "{...}" }
Kiev::Kafka.inject_context(sns_message[:message_attributes])
After this operation the message attributes will also include required context for the Kiev logger.
Kafka
To enhance messages published to Kafka topics you can use the ContextInjector:
Kiev::Kafka.inject_context(headers)
After this operation the headers variable will also include required context for the Kiev logger.
If you have a consumed Kafka::FetchedMessage you can extract logger context with:
Kiev::Kafka.extract_context(message)
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.
Que
Add the following lines to your initializer code:
require "kiev/que/job"
class MyJob < Kiev::Que::Job
...
end
Her
Add the following lines to your initializer code:
Her::API.setup(url: "https://api.example.com") do |c|
c.use Kiev::HerExt::ClientRequestId
# other middleware
end
Loading only the required parts
You can load only parts of the gem, if you don't want to use all features:
require "kiev/her_ext/client_request_id"
Logging
Requests
For web requests the Kiev middleware will log the following information by default:
{
"application":"my_app",
"event":"request_finished",
"level":"INFO",
"timestamp":"2017-01-27T16:11:44.123Z",
"host":"localhost",
"verb":"GET",
"path":"/",
"params":"{\"hello\":\"world\",\"password\":\"[FILTERED]\"}",
"ip":"127.0.0.1",
"request_id":"UUID",
"request_depth":0,
"route":"RootController#index",
"user_agent":"curl/7.50.1",
"status":200,
"request_duration":62.3773,
"body":"See #log_response_body_condition",
"error_message": "...",
"error_class": "...",
"error_backtrace": "...",
"tree_path": "ACE",
"tree_leaf": true
}
-
paramsattribute will store both query parameters and request body fields (as long as they are parseable). Sensitive fields will be filtered out - see the#filtered_paramsoption. -
request_idis 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) -
tracking_idis 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 deprecatedrequest_id. -
request_depthrepresents the position of the current request within a chain of requests. It starts with 0. -
routeattribute will be set to either the Rails route (RootController#index) or Sinatra route (/) or the path, depending on the context. -
request_durationis measured in miliseconds. -
bodyattribute coresponds to the response body and will be logged depending on the#log_response_body_conditionoption. -
tree_pathattribute can be used to follow the branching of requests within a chain of requests. It's a lexicographically sortable string. -
tree_leafpoints out that this request is a leaf in the request chain tree structure.
Background jobs
For background jobs, Kiev will log the following information by default:
{
"application":"my_app",
"event":"job_finished",
"level":"INFO",
"timestamp":"2017-01-27T16:11:44.123Z",
"job_name":"name",
"params": "...",
"jid":123,
"request_id":"UUID",
"request_depth":0,
"request_duration":0.000623773,
"error_message": "...",
"error_class": "...",
"error_backtrace": "...",
"tree_path": "BDF",
"tree_leaf": true
}
Appending data to the request log entry
You can also append arbitrary data to the request log by calling:
# Append structured data (will be merged)
Kiev.payload(first_name: "john", last_name: "smith")
# Same thing
Kiev[:first_name] = "john"
Kiev[:last_name] = "smith"
Other events
Kiev allows you to log custom events as well.
The recommended way to do this is by using the #event method:
# Log event without any data
Kiev.event(:my_event)
# Log structured data (will be merged)
Kiev.event(:my_event, { some_array: [1, 2, 3] })
# Log other data types (will be available under the `message` key)
Kiev.event(:my_event, "hello world")
# Log with given severity [debug, info, warn, error, fatal]
Kiev.info(:my_event)
Kiev.info(:my_event, { some_array: [1, 2, 3] })
Kiev.info(:my_event, "hello world")
However, Kiev.logger implements the Ruby Logger class, so all the other methods are available as well:
Kiev.logger.info("hello world")
Kiev.logger.debug({ first_name: "john", last_name: "smith" })
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.
Advanced configuration
development_mode
Kiev offers human-readable logging for development purposes. You can enable it via the development_mode option:
Kiev.configure do |config|
config.development_mode = Rails.env.development?
end
filtered_params
By default, Kiev filters out the values for the following parameters:
- client_secret
- token
- password,
- password_confirmation
- old_password
- credit_card_number
- credit_card_cvv
You can override this behaviour via the filtered_params option:
Kiev.configure do |config|
config.filtered_params = %w(email first_name last_name)
end
ignored_params
By default, Kiev ignores th
Related Skills
node-connect
341.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
341.8kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.6kCommit, push, and open a PR
