/fiber_metrics.cr

Runtime and memory allocation metrics

Primary LanguageCrystalMIT LicenseMIT

Fiber metrics

Crystal CI GitHub release GitHub commits since latest release (by date) for a branch Docs

Track run time, wait time or memory allocations per Fiber, method or block.

  • Designed for low overhead use in production
  • Fast. Shared nothing (when possible)
  • Simple. 0 configuration

This shard is modular

require
fiber_metrics/all Batteries included. Start here
fiber_metrics Requires the minimum necessary to run with the lowest overhead

TODO

  • Channel.send,recv
  • sleep
  • Mutex
  • IO
  • Hook in to Scheduler
  • ENV to control printing

Installation

  1. Add the dependency to your shard.yml:

    dependencies:
      fiber_metrics:
        github: didactic-drunk/fiber_metrics.cr
  2. Run shards install

Usage

require "fiber_metrics/all"

class Example
  # Must be included in each class containing a `@[Measure]` annotation **including subclasses**
  include Fiber::Metrics

  @[Measure]
  def foo
    bar
    sleep 0.2
    Bytes.new 16384
  end

  @[Measure]
  def bar
    baz
    sleep 0.2
    Bytes.new 32768
  end

  @[Measure]
  def baz
    sleep 0.2
    Bytes.new 65536
  end
end

e = Example.new
e.foo

Fiber.print_stats

Example Output

┌───────┬───────┬───────┬───────┬───────┬───────┬───────┐
│ Calls │ IdleT │ BlkT  │ RunT  │ Total │ Mem   │ Name  │
├───────┼───────┼───────┼───────┼───────┼───────┼───────┤
│    1  │       │       │ 0.209 │ 0.209 │   64K │ C.baz │
│    1  │       │       │ 0.205 │ 0.414 │   32K │ C.bar │
│    1  │       │       │ 0.204 │ 0.622 │   16K │ C.foo │
└───────┴───────┴───────┴───────┴───────┴───────┴───────┘

Output is best viewed with [less -RS, bat --wrap=never]

Column Description
IdleT Time idle. Channel.recv, IO.read, etc
BlkT Time blocked. Channel.send, IO.write, etc
RunT RunTime: Time spent within the measured method - [idle, blk], but not sub measured methods
Total Total elapsed time including sub measured methods
Mem Memory Allocated

How it works

@[Measure] wraps each method in (2) Time.monotonic calls and records the difference.

The collected metrics use Fiber local storage (no locking) until the first measured call returns after which the local metrics are merged with a global set of metrics wrapped in a Mutex.

This means metrics are unavailable via Fiber.stats until the first measured method foo returns. This is ideal for

  • Applications that show/record stats before exit
  • Request/response (HTTP, message queue, etc) type servers that want periodic snapshots of activity

@[Measure] Logic

@[Measure]
def foo
  # Time.monotonic
  bar
  # Time.monotonic
  # Store metrics in fiber local storage
  # Merge fiber local metrics with global metrics
  # Reset fiber local metrics
end

@[Measure]
def bar
  # Time.monotonic
  baz
  # Time.monotonic
  # Store metrics in fiber local storage
end

@[Measure]
def baz
  # Time.monotonic
  ...
  # Time.monotonic
  # Store metrics in fiber local storage
end

More options to control the merging process may appear after use cases are discussed. Open a discussion if the current behavior doesn't fit your needs.

Contributing

  1. Fork it (https://github.com/didactic-drunk/fiber_metrics.cr/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors