Module: Dalli::Instrumentation

Defined in:
lib/dalli/instrumentation.rb

Overview

Instrumentation support for Dalli. Provides hooks for distributed tracing via OpenTelemetry when the SDK is available.

When OpenTelemetry is loaded, Dalli automatically creates spans for cache operations. When OpenTelemetry is not available, all tracing methods are no-ops with zero overhead.

Span Attributes

All spans include the following default attributes:

  • db.system - Always “memcached”

Single-key operations (get, set, delete, incr, decr, etc.) add:

  • db.operation - The operation name (e.g., “get”, “set”)

  • server.address - The memcached server handling the request (e.g., “localhost:11211”)

Multi-key operations (get_multi) add:

  • db.operation - “get_multi”

  • db.memcached.key_count - Number of keys requested

  • db.memcached.hit_count - Number of keys found in cache

  • db.memcached.miss_count - Number of keys not found

Bulk write operations (set_multi, delete_multi) add:

  • db.operation - The operation name

  • db.memcached.key_count - Number of keys in the operation

Error Handling

When an exception occurs during a traced operation:

  • The exception is recorded on the span via record_exception

  • The span status is set to error with the exception message

  • The exception is re-raised to the caller

Examples:

Checking if tracing is enabled

Dalli::Instrumentation.enabled? # => true if OpenTelemetry is loaded

Constant Summary collapse

DEFAULT_ATTRIBUTES =

Default attributes included on all memcached spans.

Returns:

  • (Hash)

    frozen hash with ‘db.system’ => ‘memcached’

{ 'db.system' => 'memcached' }.freeze

Class Method Summary collapse

Class Method Details

.enabled?Boolean

Returns true if instrumentation is enabled (OpenTelemetry SDK is available).

Returns:

  • (Boolean)

    true if tracing is active, false otherwise



62
63
64
# File 'lib/dalli/instrumentation.rb', line 62

def enabled?
  !tracer.nil?
end

.trace(name, attributes = {}) { ... } ⇒ Object

Wraps a block with a span if instrumentation is enabled.

Creates a client span with the given name and attributes merged with DEFAULT_ATTRIBUTES. The block is executed within the span context. If an exception occurs, it is recorded on the span before re-raising.

When tracing is disabled (OpenTelemetry not loaded), this method simply yields directly with zero overhead.

Examples:

Tracing a set operation

trace('set', { 'db.operation' => 'set', 'server.address' => 'localhost:11211' }) do
  server.set(key, value, ttl)
end

Parameters:

  • name (String)

    the span name (e.g., ‘get’, ‘set’, ‘delete’)

  • attributes (Hash) (defaults to: {})

    span attributes to merge with defaults. Common attributes include:

    • ‘db.operation’ - the operation name

    • ‘server.address’ - the target server

    • ‘db.memcached.key_count’ - number of keys (for multi operations)

Yields:

  • the cache operation to trace

Returns:

  • (Object)

    the result of the block

Raises:

  • (StandardError)

    re-raises any exception from the block



90
91
92
93
94
95
96
97
98
99
100
# File 'lib/dalli/instrumentation.rb', line 90

def trace(name, attributes = {})
  return yield unless enabled?

  tracer.in_span(name, attributes: DEFAULT_ATTRIBUTES.merge(attributes), kind: :client) do |span|
    yield
  rescue StandardError => e
    span.record_exception(e)
    span.status = OpenTelemetry::Trace::Status.error(e.message)
    raise
  end
end

.trace_with_result(name, attributes = {}) {|OpenTelemetry::Trace::Span, nil| ... } ⇒ Object

Like trace, but yields the span to allow adding attributes after execution.

This is useful for operations where metrics are only known after the operation completes, such as get_multi where hit/miss counts depend on the cache response.

When tracing is disabled, yields nil as the span argument.

Examples:

Recording hit/miss metrics after get_multi

trace_with_result('get_multi', { 'db.operation' => 'get_multi' }) do |span|
  results = fetch_from_cache(keys)
  if span
    span.set_attribute('db.memcached.hit_count', results.size)
    span.set_attribute('db.memcached.miss_count', keys.size - results.size)
  end
  results
end

Parameters:

  • name (String)

    the span name (e.g., ‘get_multi’)

  • attributes (Hash) (defaults to: {})

    initial span attributes to merge with defaults

Yields:

  • (OpenTelemetry::Trace::Span, nil)

    the span object, or nil if disabled

Returns:

  • (Object)

    the result of the block

Raises:

  • (StandardError)

    re-raises any exception from the block



126
127
128
129
130
131
132
133
134
135
136
# File 'lib/dalli/instrumentation.rb', line 126

def trace_with_result(name, attributes = {})
  return yield(nil) unless enabled?

  tracer.in_span(name, attributes: DEFAULT_ATTRIBUTES.merge(attributes), kind: :client) do |span|
    yield(span)
  rescue StandardError => e
    span.record_exception(e)
    span.status = OpenTelemetry::Trace::Status.error(e.message)
    raise
  end
end

.tracerOpenTelemetry::Trace::Tracer?

Returns the OpenTelemetry tracer if available, nil otherwise.

The tracer is cached after first lookup for performance. Uses the library name ‘dalli’ and current Dalli::VERSION.

Returns:

  • (OpenTelemetry::Trace::Tracer, nil)

    the tracer or nil if OTel unavailable



53
54
55
56
57
# File 'lib/dalli/instrumentation.rb', line 53

def tracer
  return @tracer if defined?(@tracer)

  @tracer = (OpenTelemetry.tracer_provider.tracer('dalli', Dalli::VERSION) if defined?(OpenTelemetry))
end