> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appsignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Minutely probes

export const Compatibility = ({versions = [], label = "Available in"}) => {
  if (!Array.isArray(versions) || versions.length === 0) {
    return null;
  }
  const defaultPillStyle = {
    borderColor: "#d4d4d8",
    background: "#f4f4f5",
    color: "#3f3f46"
  };
  const pillStyles = {
    "AppSignal for Elixir": {
      background: "#f3e8ff",
      borderColor: "#d8b4fe",
      color: "#6b21a8"
    },
    "AppSignal for Front-end": {
      background: "#fef9c3",
      borderColor: "#fde047",
      color: "#854d0e"
    },
    "AppSignal for Go": {
      background: "#ccfbf1",
      borderColor: "#5eead4",
      color: "#115e59"
    },
    "AppSignal for JavaScript": {
      background: "#fef9c3",
      borderColor: "#fde047",
      color: "#854d0e"
    },
    "AppSignal for Node.js": {
      background: "#dcfce7",
      borderColor: "#86efac",
      color: "#166534"
    },
    "AppSignal for Python": {
      background: "#dbeafe",
      borderColor: "#93c5fd",
      color: "#1e40af"
    },
    "AppSignal for Ruby": {
      background: "#fee2e2",
      borderColor: "#fca5a5",
      color: "#991b1b"
    },
    "AppSignal for Rust": {
      background: "#ffedd5",
      borderColor: "#fdba74",
      color: "#9a3412"
    }
  };
  const getPillStyle = name => ({
    ...defaultPillStyle,
    ...pillStyles[name] || ({})
  });
  return <div className="not-prose my-4 rounded-lg border border-zinc-200 bg-zinc-50 px-4 py-3 text-sm dark:border-white/10 dark:bg-white/5">
      <div className="flex flex-wrap items-center gap-x-2 gap-y-1">
        <span className="font-semibold text-zinc-700 dark:text-zinc-200">
          {label}:
        </span>
        {versions.map((v, i) => <span key={`${v.name}-${v.version}-${i}`} className="inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium" style={getPillStyle(v.name)}>
            <span>{v.name}</span>
            <span className="opacity-70">
              {v.version}
              {v.exact ? "" : "+"}
            </span>
          </span>)}
      </div>
    </div>;
};

Minutely probes are a mechanism to periodically send [custom metrics](/metrics/custom) to AppSignal. This is a system that is included in the AppSignal Ruby gem by default. At the start of every minute the minutely probes are triggered one by one to collect metrics and then snoozed until the next minute.

By default the AppSignal Ruby gem enables probes for [libraries](/ruby/integrations) that are detected for your app.

<Tip>
  **Note**: In AppSignal Ruby gem 3.7.0 and lower, use the
  `Appsignal::Minutely.probes` module to register probes, rather than the new
  `Appsignal::Probes` API.
</Tip>

## Usage

The minutely probes allow the AppSignal Ruby gem to collect [custom metrics](/metrics/custom) by default for [integrations](/ruby/integrations) and app-specific metrics by [creating your own probe](#creating-probes).

### Multiple instances

Once activated, the minutely probes system runs on every instance of an app. This means that if a probe report metrics without some kind of differentiating tag, global metrics may be overwritten by instance-level metrics. For example, there's a probe that tracks how large the local background job queue is for an app. The queue database runs locally on the instance and queue sizes may vary wildly. Each instance of an app reports different metric values for the same metric and tags, overwriting the metric value every minute with those of the last reporting instance.

To remedy this, we suggest [tagging](/metrics/custom#metric-tags) your metrics with the hostname or something else unique for each instance. For example, the [Sidekiq probe](/ruby/integrations/sidekiq#minutely-probe) tags metrics with the Redis hostname by default.

Alternatively you can [disable minutely probes](/ruby/configuration/options#option-enable_minutely_probes) for all but one instance, on which the minutely probes process is run. We suggest using the [`APPSIGNAL_ENABLE_MINUTELY_PROBES`](/ruby/configuration/options#option-enable_minutely_probes) environment variable to only enable it on the instance of your choosing.

## Configuration

The minutely probes are configured using the [`enable_minutely_probes`](/ruby/configuration/options#option-enable_minutely_probes) config option.

## Creating probes

If you want to track more [custom metrics](/metrics/custom) from your app than our the default probes that ship with our [integrations](/ruby/integrations/), you can add your own probe(s).

An AppSignal minutely probe can be either of three things. Which of the three types to use for your class depends on the use case.

* [Lambda probe](#lambda-probe)
* [Class probe](#class-probe)
* [Initialized class probe](#initialized-class-probe)

### Lambda probe

The simplest probe type to register. If you have no dependencies for your probe this is the preferred method.

<CodeGroup>
  ```ruby Ruby theme={null}
  # config/initializers/appsignal.rb or a file that's loaded on boot
  Appsignal::Probes.register(:my_probe, lambda do
    Appsignal.set_gauge("database_size", 10)
  end)
  ```
</CodeGroup>

### Class probe

<Compatibility versions={[{ name: "AppSignal for Ruby", version: "2.9.0" }]} />

A class probe is a lazy initialized probe. It will only be started if the minutely probes are started. It will be initialized at the start of the minutely probes thread and remain initialized for as long as your app is running.

This method is useful when you do not have [`enable_minutely_probes`](/ruby/configuration/options#option-enable_minutely_probes) enabled for every environment and don't want it to be initialized by default.

<CodeGroup>
  ```ruby Ruby theme={null}
  # config/initializers/appsignal.rb or a file that's loaded on boot

  # Creating a probe using a Ruby class
  class BackgroundJobLibraryProbe
    def initialize
      # This is only called when the minutely probe gets initialized
      require "background_job_library"
      @connection = BackgroundJobLibrary.connection
    end

    def call
      stats = @connection.fetch_queue_stats
      Appsignal.set_gauge "background_job_library_queue_length", stats.queue_length
      Appsignal.set_gauge "background_job_library_processed_jobs", stats.processed_jobs
    end
  end

  # Registering a Class probe
  Appsignal::Probes.register(
    :background_job_library_probe,
    BackgroundJobLibraryProbe
  )
  ```
</CodeGroup>

### Initialized class probe

<Compatibility versions={[{ name: "AppSignal for Ruby", version: "2.9.0" }]} />

This method is most useful for [overriding default probes](#overriding-default-probes). The AppSignal Ruby gem ships some probes for [integrations](/ruby/integrations) by default. These default probes register probes as a [class](#class-probe) so they won't be initialized when overridden. When the probe default config does not fit your use case you can [override a default probe](#overriding-default-probes) with your own config.

Note that we do not register a [class as a probe](#class-probe), but an instance of the class using `.new` and pass along a config object, e.g. `BackgroundJobLibraryProbe.new(<config>)`.

<CodeGroup>
  ```ruby Ruby theme={null}
  # config/initializers/appsignal.rb or a file that's loaded on boot
  require "background_job_library"

  # Creating a probe using a Ruby class
  class BackgroundJobLibraryProbe
    def initialize(config)
      @connection = BackgroundJobLibrary.connection(config)
    end

    def call
      stats = @connection.fetch_queue_stats
      Appsignal.set_gauge "background_job_library_queue_length", stats.queue_length
      Appsignal.set_gauge "background_job_library_processed_jobs", stats.processed_jobs
    end
  end

  # Registering an initialized Class probe
  Appsignal::Probes.register(
    :background_job_library_probe,
    BackgroundJobLibraryProbe.new(:database => "redis://localhost:6379")
  )
  ```
</CodeGroup>

## Registering probes

<Compatibility versions={[{ name: "AppSignal for Ruby", version: "2.9.6" }]} />

Probes can be registered with the `register` method on the `Appsignal::Probes` module.
This method accepts two arguments.

* `key` - This is the key/name of the probe. This will be used to identify the probe in case an error occurs while executing the probe (which will be logged to the [appsignal.log file](/support/debugging#logs)) and to [override an existing probe](#overriding-default-probes).
* `probe` - This is one of the [supported probe types](#creating-probes) that should be called every minute to collect metrics.

<CodeGroup>
  ```ruby Ruby theme={null}
  # config/initializers/appsignal.rb or a file that's loaded on boot

  Appsignal::Probes.register(:my_probe, lambda do
    Appsignal.set_gauge("database_size", 10)
  end)
  ```
</CodeGroup>

### Rails apps

We recommend registering your probes in a Rails initializer, like: `config/initializers/my_probe.rb`.

## Dependency requirements

<Compatibility versions={[{ name: "AppSignal for Ruby", version: "2.9.6" }]} />

Not always should a probe be added to the list of probes by default. Some dependency may be required for the probe to work properly. This dependency check is used in probes shipped in the gem, but can also be used in your own probes.

By adding a class method called `dependencies_present?`, a check can be performed ahead of starting the probe whether not it should be started. This works on both [class probes](#class-probe) and [initialized class probes](#initialized-class-probe). For the latter scenario there should be no call to said dependency in the `initialize` method of the probe as it's already initialized.

<CodeGroup>
  ```ruby Ruby theme={null}
  # config/initializers/appsignal.rb or a file that's loaded on boot
  require "background_job_library"

  # Creating a probe using a Ruby class
  class BackgroundJobLibraryProbe
    def self.dependencies_present?
      # Only start the probe if the BackgroundJobLibrary version is higher or
      # equal to 1.0.0.
      Gem::Version.new(BackgroundJobLibrary::Version) >= Gem::Version.new("1.0.0")
    end

    def initialize(config = {})
      @config = config
    end

    def call
      stats = connection.fetch_queue_stats
      Appsignal.set_gauge "background_job_library_queue_length", stats.queue_length
      Appsignal.set_gauge "background_job_library_processed_jobs", stats.processed_jobs
    end

    private

    def connection
      @connection ||= BackgroundJobLibrary.connection(@config)
    end
  end

  # Registering a Class probe with a `dependencies_present?` check
  Appsignal::Probes.register(
    :background_job_library_probe,
    BackgroundJobLibraryProbe
  )
  # Also works for initialized probes
  Appsignal::Probes.register(
    :background_job_library_probe,
    BackgroundJobLibraryProbe.new(:url => "schema://my_connection_url:9090")
  )
  ```
</CodeGroup>

## Overriding default probes

AppSignal ships with default probes for certain [integrations](/ruby/integrations/). If for any reason this probe does not function properly or requires some additional configuration for your use case, you can override the default probe by [initializing the probe class](#initialized-class-probe) with your own config.

Example overriding the [Sidekiq probe](/ruby/integrations/sidekiq#minutely-probe).

<CodeGroup>
  ```ruby Ruby theme={null}
  # config/initializers/appsignal.rb or a file that's loaded on boot

  Appsignal::Probes.register(
    :sidekiq, # Use the same key as the default Sidekiq probe to override it
    Appsignal::Probes::SidekiqProbe.new(:hostname => ENV["REDIS_URL"])
  )
  ```
</CodeGroup>

(Note that you'll need to set the `REDIS_URL` environment variable yourself.)

Overriding probes will log a debug message in the [appsignal.log file](/support/debugging#logs) which can help to detect if a probe is correctly overridden.

## Unregister probes

<Compatibility versions={[{ name: "AppSignal for Ruby", version: "3.7.1" }]} />

To unregister previously registered probes, including the default probes, use the `Appsignal::Probes.unregister` method. Call the unregister method with the name of the probe to unregister it.

<CodeGroup>
  ```ruby Ruby theme={null}
  Appsignal::Probes.unregister(
    :probe_name # Use the same key as used in `Probes.register` to unregister it
  )
  ```
</CodeGroup>
