Tracer.cr CI Tracer.cr.cr Build Docs

GitHub release GitHub commits since latest release (by SemVer)

Tracer.cr

Tracer.cr provides a facility for attaching tracing code to methods in Crystal code.

This library is a low level tracing interface. It provides a simple api to use to attach functionality to existing code. It is intended to be a building block for constructing higher level functionality - debugging, performance monitoring, or execution auditing, for example.

It works through a combination of macros, and leveraging the previous_def capability to invoke the previously defined version of a method.

The current version nominally works, but it is missing some key features that are on the short term roadmap:

  1. Add support for the Trace annotation so that tracing can be managed via annotations.
  2. Add support for dynamically disabling trace code. It would be really nifty if we could do object inheritance chain manipulation in Crystal like one can in Ruby, when leveraging prepend for this sort of stuff, but Crystal's compiled nature doesn't make it dynamic in that way, so the plan is to enable dynamic disabling of trace code through a lookup table and simple if statements. It will have some impact on performance, even when the trace code is disabled, but the impact should be extremely small.

Installation

  1. Add the dependency to your shard.yml:

    dependencies:
      tracer:
        github: wyhaines/tracer.cr
  2. Run shards install

Usage

require "tracer"

To use the tracer, require the shard. This will define macros to support all other tracing tasks.

The main macro that is used to establish tracing functions is trace. It takes two required arguments, and one optional argument.

trace(METHOD_NAME, CALLBACK, BLOCK_DEFINITION)

The METHOD_NAME should be a String that is the name of the method that is being traced. The CALLBACK can be either a string specifying a method to call both before and after METHOD_NAME is called, or a Proc that will be invoked both before and after METHOD_NAME is called.

A callback method is expected to take five arguments:

def callback(caller, method_name, phase, method_identifier, method_counter)

A simple example of setting up tracing on a method is:

trace("my_method", "my_callback")

This example will trace the method my_method and invoke the callback my_callback both before and after the method is called.

Callbacks can also be specified with blocks or Proc

trace("my_method", ->() {puts "Doing tracing stuff"})
trace("my_method", ->(method_name : String) {puts "Tracing #{method_name}"})
trace("my_method", ->(
  method_name : String,
  phase : Symbol
) {puts "Tracing #{method_name} in phase #{phase}"})
trace("my_method", ->(
  method_name : String,
  phase : Symbol,
  method_identifier : String
) {puts "Tracing #{method_name} in phase #{phase}; unique method identifier is #{method_identifier}"})
trace("my_method", ->(
  method_name : String,
  phase : Symbol,
  method_identifier : String,
  method_counter : U128
) {puts "Tracing #{method_name} in phase #{phase}; unique method identifier is #{method_identifier}; method counter is #{method_counter}"})
trace("my_method", ->(
  method_name : String,
  phase : Symbol,
  method_identifier : String,
  method_counter : U128,
  caller : Object
) {puts "Tracing #{method_name} on #{caller} in phase #{phase}; unique method identifier is #{method_identifier}; method counter is #{method_counter}"}

Tracing also accepts a block syntax:

trace("my_method") do |method_name, phase, method_identifier|
  EventLogger.log(method_name, phase, method_identifier)
end

Like the Proc support, the block support works with any number of arguments from zero to five.

Development

If you want to help with the development, email me, and fork the repo. Work on your changes in a branch out of your own repo, and when it is ready (with documentation and specs), send me a pull request telling me what you have done, why you have done it, and what you have done to make sure that it works as expected.

Contributing

  1. Fork it (https://github.com/wyhaines/tracer.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

GitHub code size in bytes GitHub issues