SkillAgentSearch skills...

Kiev

A set of tools to do distributed logging for Ruby web applications

Install / Use

/learn @blacklane/Kiev
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Kiev Build Status Gem Version

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
}
  • 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.

  • 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)

  • 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.

  • request_depth represents the position of the current request within a chain of requests. It starts with 0.

  • route attribute will be set to either the Rails route (RootController#index) or Sinatra route (/) or the path, depending on the context.

  • request_duration is measured in miliseconds.

  • body attribute coresponds to the response body and will be logged depending on the #log_response_body_condition option.

  • tree_path attribute can be used to follow the branching of requests within a chain of requests. It's a lexicographically sortable string.

  • tree_leaf points 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

View on GitHub
GitHub Stars44
CategoryDevelopment
Updated27d ago
Forks4

Languages

Ruby

Security Score

90/100

Audited on Mar 3, 2026

No findings