Module: Ask::Observability

Defined in:
lib/ask/observability.rb,
lib/ask/observability/metrics.rb,
lib/ask/observability/railtie.rb,
lib/ask/observability/version.rb,
lib/ask/observability/bootstrap.rb,
lib/ask/observability/subscriber.rb,
lib/ask/observability/metrics_app.rb,
lib/ask/observability/configuration.rb,
lib/generators/ask/observability/install_generator.rb

Overview

Ask::Observability — Prometheus metrics, OpenTelemetry bootstrap, and structured logging for the ask-rb ecosystem.

The ask-rb observability story composes:

* ask-instrumentation   — emits events for every LLM operation
* ask-opentelemetry     — events → OpenTelemetry spans
* ask-observability     — events → Prometheus metrics, plus the
                        OTel/logging bootstrap and /metrics
* ask-monitoring        — events → in-app dashboard + alerts

Usage

Ask::Observability.install

In a Rails app the railtie auto-installs (bootstrap + subscriber + /metrics); configure it first:

Ask::Observability.configure do |config|
config.service_name = "my-app"
config. = [:workspace_id]
end

Defined Under Namespace

Modules: Bootstrap Classes: Configuration, InstallGenerator, Metrics, MetricsApp, Railtie, Subscriber

Constant Summary collapse

VERSION =
'0.1.2'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.registryPrometheus::Client::Registry

The Prometheus registry backing every metric (and the /metrics endpoint). Tests inject a fresh registry to isolate state.

Returns:

  • (Prometheus::Client::Registry)


62
63
64
# File 'lib/ask/observability.rb', line 62

def registry
  @registry ||= Prometheus::Client::Registry.new
end

Class Method Details

.configConfiguration

The process-wide configuration. Configure before install (or before the railtie runs) so metrics carry the final label set.

Returns:



49
50
51
# File 'lib/ask/observability.rb', line 49

def config
  @config ||= Configuration.new
end

.configure {|Configuration| ... } ⇒ Object

Yields:



54
55
56
# File 'lib/ask/observability.rb', line 54

def configure
  yield config
end

.counter(name, **kwargs) ⇒ Prometheus::Client::Counter

Register-or-resolve a counter on the shared registry. Reload-safe: in development, Zeitwerk re-evaluates app files that register metrics, which would re-register the same name against a registry that still holds it and raise AlreadyRegisteredError (surfacing as a 500 in the app). Resolving the existing metric keeps registrations idempotent instead.

Parameters:

  • name (Symbol)

    metric name, e.g. :voice_turns_total

Returns:

  • (Prometheus::Client::Counter)


77
78
79
80
81
# File 'lib/ask/observability.rb', line 77

def counter(name, **kwargs)
  registry.get(name) || registry.counter(name, **kwargs)
rescue Prometheus::Client::Registry::AlreadyRegisteredError
  registry.get(name)
end

.histogram(name, **kwargs) ⇒ Prometheus::Client::Histogram

Register-or-resolve a histogram (see #counter).

Returns:

  • (Prometheus::Client::Histogram)


86
87
88
89
90
# File 'lib/ask/observability.rb', line 86

def histogram(name, **kwargs)
  registry.get(name) || registry.histogram(name, **kwargs)
rescue Prometheus::Client::Registry::AlreadyRegisteredError
  registry.get(name)
end

.installObject

Subscribe to every ask-instrumentation event and start maintaining Prometheus metrics. Idempotent — subsequent calls are no-ops.



94
95
96
97
98
99
100
# File 'lib/ask/observability.rb', line 94

def install
  return if installed?
  return if config.enabled == false

  @subscriber = Ask::Instrumentation.subscribe(/\.ask$/, Subscriber.new)
  @installed = true
end

.installed?Boolean

Returns whether the subscriber is installed.

Returns:

  • (Boolean)

    whether the subscriber is installed



112
113
114
# File 'lib/ask/observability.rb', line 112

def installed?
  !!@installed
end

.metricsMetrics

The metric definitions for the current registry.

Returns:



137
138
139
# File 'lib/ask/observability.rb', line 137

def metrics
  Metrics.for(registry, metadata_label_keys: config.)
end

.uninstallObject

Remove the subscriber (tests use this to keep suites isolated).



103
104
105
106
107
108
109
# File 'lib/ask/observability.rb', line 103

def uninstall
  return unless @subscriber

  Ask::Instrumentation.unsubscribe(@subscriber)
  @subscriber = nil
  @installed = false
end

.with_context(metadata) { ... } ⇒ Object

Wrap a block so the given metadata flows into ask-instrumentation events (and thus into spans and metrics) AND into Rails log lines as tags, when Rails is present. One call to correlate a unit of work — a request, a job, a call — everywhere.

Parameters:

  • metadata (Hash)

    e.g. "abc", workspace_id: 4

Yields:

  • the block to run under the context

Returns:

  • (Object)

    the block's return value



124
125
126
127
128
129
130
131
132
# File 'lib/ask/observability.rb', line 124

def with_context(, &block)
  Ask::Instrumentation.() do
    if defined?(::Rails) && ::Rails.respond_to?(:logger) && ::Rails.logger.respond_to?(:tagged)
      ::Rails.logger.tagged(**, &block)
    else
      yield
    end
  end
end