Full Code of yabeda-rb/yabeda-rails for AI

master 9fbdca63efe2 cached
22 files
30.7 KB
8.6k tokens
39 symbols
1 requests
Download .txt
Repository: yabeda-rb/yabeda-rails
Branch: master
Commit: 9fbdca63efe2
Files: 22
Total size: 30.7 KB

Directory structure:
gitextract__sq7l337/

├── .github/
│   └── workflows/
│       ├── lint.yml
│       ├── release.yml
│       └── test.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── CHANGELOG.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin/
│   ├── console
│   └── setup
├── lib/
│   └── yabeda/
│       ├── rails/
│       │   ├── config.rb
│       │   ├── event.rb
│       │   ├── railtie.rb
│       │   └── version.rb
│       └── rails.rb
├── spec/
│   ├── spec_helper.rb
│   ├── support/
│   │   └── rails_app.rb
│   └── yabeda/
│       └── rails_spec.rb
└── yabeda-rails.gemspec

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint

on:
  pull_request:
  push:
    branches:
      - '**'
    tags-ignore:
      - 'v*'

jobs:
  rubocop:
    # Skip running tests for local pull requests (use push event instead), run only for foreign ones
    if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login != github.event.pull_request.base.repo.owner.login
    name: RuboCop
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.4"
          bundler-cache: true
      - name: Lint Ruby code with RuboCop
        run: |
          bundle exec rubocop


================================================
FILE: .github/workflows/release.yml
================================================
name: Build and release gem

on:
  push:
    tags:
      - v*

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      id-token: write
      packages: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Fetch current tag as annotated. See https://github.com/actions/checkout/issues/290
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.4"
      - name: "Extract data from tag: version, message, body"
        id: tag
        run: |
          git fetch --tags --force # Really fetch annotated tag. See https://github.com/actions/checkout/issues/290#issuecomment-680260080
          echo ::set-output name=version::${GITHUB_REF#refs/tags/v}
          echo ::set-output name=subject::$(git for-each-ref $GITHUB_REF --format='%(contents:subject)')
          BODY="$(git for-each-ref $GITHUB_REF --format='%(contents:body)')"
          # Extract changelog entries between this and previous version headers
          escaped_version=$(echo ${GITHUB_REF#refs/tags/v} | sed -e 's/[]\/$*.^[]/\\&/g')
          changelog=$(awk "BEGIN{inrelease=0} /## ${escaped_version}/{inrelease=1;next} /## [0-9]+\.[0-9]+\.[0-9]+/{inrelease=0;exit} {if (inrelease) print}" CHANGELOG.md)
          # Multiline body for release. See https://github.community/t/set-output-truncates-multiline-strings/16852/5
          BODY="${BODY}"$'\n'"${changelog}"
          BODY="${BODY//'%'/'%25'}"
          BODY="${BODY//$'\n'/'%0A'}"
          BODY="${BODY//$'\r'/'%0D'}"
          echo "::set-output name=body::$BODY"
          # Add pre-release option if tag name has any suffix after vMAJOR.MINOR.PATCH
          if [[ ${GITHUB_REF#refs/tags/} =~ ^v[0-9]+\.[0-9]+\.[0-9]+.+ ]]; then
            echo ::set-output name=prerelease::true
          fi
      - name: Build gem
        run: gem build
      - name: Calculate checksums
        run: sha256sum yabeda-rails-${{ steps.tag.outputs.version }}.gem > SHA256SUM
      - name: Check version
        run: ls -l yabeda-rails-${{ steps.tag.outputs.version }}.gem
      - name: Create Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: ${{ steps.tag.outputs.subject }}
          body: ${{ steps.tag.outputs.body }}
          draft: false
          prerelease: ${{ steps.tag.outputs.prerelease }}
      - name: Upload built gem as release asset
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: yabeda-rails-${{ steps.tag.outputs.version }}.gem
          asset_name: yabeda-rails-${{ steps.tag.outputs.version }}.gem
          asset_content_type: application/x-tar
      - name: Upload checksums as release asset
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: SHA256SUM
          asset_name: SHA256SUM
          asset_content_type: text/plain
      - name: Publish to GitHub packages
        env:
          GEM_HOST_API_KEY: Bearer ${{ secrets.GITHUB_TOKEN }}
        run: |
          gem push yabeda-rails-${{ steps.tag.outputs.version }}.gem --host https://rubygems.pkg.github.com/${{ github.repository_owner }}
      - name: Configure RubyGems Credentials
        uses: rubygems/configure-rubygems-credentials@main
      - name: Publish to RubyGems
        run: |
          gem push yabeda-rails-${{ steps.tag.outputs.version }}.gem


================================================
FILE: .github/workflows/test.yml
================================================
name: Tests

on:
  pull_request:
  push:
    branches:
      - '**'
    tags-ignore:
      - 'v*'

jobs:
  test:
    name: 'Rails ${{ matrix.rails }} × Ruby ${{ matrix.ruby }}'
    # Skip running tests for local pull requests (use push event instead), run only for foreign ones
    if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login != github.event.pull_request.base.repo.owner.login
    runs-on: ubuntu-latest
    continue-on-error: ${{ matrix.optional || false }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - ruby: "head"
            rails: "HEAD"
            optional: true
          - ruby: "4.0"
            rails: "8.1"
          - ruby: "3.4"
            rails: "8.1"
          - ruby:  "3.4"
            rails: "8.0"
          - ruby:  "3.3"
            rails: "7.2"
          - ruby:  "3.2"
            rails: "7.1"
          - ruby:  "3.1"
            rails: "7.0"
          - ruby:  "3.0"
            rails: "6.1"
          - ruby:  "2.7"
            rails: "6.0"
    env:
      CI: true
      RAILS_VERSION: ${{ matrix.rails }}
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: ${{ matrix.ruby }}
          bundler-cache: true
      - name: Run RSpec
        run: bundle exec rspec


================================================
FILE: .gitignore
================================================
/.bundle/
/.yardoc
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
Gemfile.lock

# rspec failure tracking
.rspec_status


================================================
FILE: .rspec
================================================
--format documentation
--color
--require spec_helper


================================================
FILE: .rubocop.yml
================================================
---
require:
- rubocop-rspec

AllCops:
  TargetRubyVersion: 2.5

Metrics/BlockLength:
  Exclude:
  - "Gemfile"
  - "spec/**/*"

Style/StringLiterals:
  EnforcedStyle: double_quotes

# Allow to use let!
RSpec/LetSetup:
  Enabled: false

RSpec/MultipleExpectations:
  Enabled: false

Bundler/OrderedGems:
  Enabled: false

Style/TrailingCommaInArguments:
  Description: 'Checks for trailing comma in argument lists.'
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-params-comma'
  Enabled: true
  EnforcedStyleForMultiline: consistent_comma

Style/TrailingCommaInArrayLiteral:
  Description: 'Checks for trailing comma in array literals.'
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas'
  Enabled: true
  EnforcedStyleForMultiline: consistent_comma

Style/TrailingCommaInHashLiteral:
  Description: 'Checks for trailing comma in hash literals.'
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas'
  Enabled: true
  EnforcedStyleForMultiline: consistent_comma

Bundler/DuplicatedGem:
  Enabled: false


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## 0.11.0 - 2025-12-03

### Added

- Ability to use procs and regexps in `ignore_actions` configuration. [@lewispb][] in [#34](https://github.com/yabeda-rb/yabeda-rails/pull/34), [@Envek][]

## 0.10.0 - 2025-09-09

### Added

- Add Passenger server to auto register list [@mabrikan][] in [#28](https://github.com/yabeda-rb/yabeda-rails/pull/28)
- Allow defining default_tags only for rails group [@magec][] in [#30](https://github.com/yabeda-rb/yabeda-rails/pull/30)
- Ability to customize the bucket sizing for histograms [@skateman][] in [#32](https://github.com/yabeda-rb/yabeda-rails/pull/32)
- Ability to ignore certain controller#actions [@zzip][] in [#33](https://github.com/yabeda-rb/yabeda-rails/pull/33)

## 0.9.0 - 2023-08-03

### Added

- Ability to switch controller name case in `controller` tag between `:snake` and `:camel` case. [@lewispb][] in [#26](https://github.com/yabeda-rb/yabeda-rails/pull/26)

## Changed

- Minimal Ruby version increased to 2.5. [@Envek][]

## 0.8.1 - 2022-06-06

### Fixed

- Fill status codes for responses with unhandled exceptions. [@dks17][] in [#24](https://github.com/yabeda-rb/yabeda-rails/pull/24)

## 0.8.0 - 2022-05-30

### Added

- Add ability to expose custom Apdex target value for later use in graphs/alerts. [@Envek][] in [#18](https://github.com/yabeda-rb/yabeda-rails/pull/18)

### Changed

- Reduce number of dependencies by depending only on railties instead of the whole Ruby on Rails. [@lautis][] in [#23](https://github.com/yabeda-rb/yabeda-rails/pull/23).

## 0.7.2 - 2021-03-15

### Fixed

- Fix undesirable overwrite of metric tags when global `default_tag` is declared with one of tag names that are being used by yabeda-rails, like `controller`. [@liaden] in [#19](https://github.com/yabeda-rb/yabeda-rails/pull/19)

## 0.7.1 - 2020-10-02

### Changed

 - Explicitly require previously removed railtie to fix case when it doesn't get required in `yabeda` gem (if `yabeda` is required before `rails`). See [yabeda-rb/yabeda#15](https://github.com/yabeda-rb/yabeda/issues/15). @Envek

## 0.7.0 - 2020-08-21

### Removed

 - Railtie to configure Yabeda – it is moved into Yabeda itself. Increase required Yabeda version to keep behavior for users who require only `yabeda-rails` in their Gemfiles. @Envek

## 0.6.0 - 2020-08-06

### Added

 - Ability to add default/custom tags to metrics from controllers. @raivil in [#13](https://github.com/yabeda-rb/yabeda-rails/pull/13)

## 0.5.0 - 2020-03-27

### Added

 - Support for Unicorn application server. @vast in [#9](https://github.com/yabeda-rb/yabeda-rails/pull/9)

## 0.4.0 - 2020-01-28

### Changed

 - Configure Yabeda after application initialization as since 0.4.0 Yabeda requires to call configuration logic explicitly. @Envek

## 0.2.0 - 2020-01-14

### Changed

 - Added `tags` option to metric declarations for compatibility with yabeda and yabeda-prometheus 0.2. @Envek

## 0.1.2 - 2019-01-19

### Added

 - Support for Puma application server. @daffydowden

## 0.1.1 - 2018-10-17

### Changed

 - Renamed evil-metrics-rails gem to yabeda-rails. @Envek

## 0.1.0 - 2018-10-03

 - Initial release of evil-metrics-rails gem. @Envek

   Basic metrics for request durations by controller, action, status, format, and method. ActiveRecord and ActionView timings.

[@Envek]: https://github.com/Envek "Andrey Novikov"
[@liaden]: https://github.com/liaden "Joel Johnson"
[@lautis]: https://github.com/lautis "Ville Lautanala"
[@dks17]: https://github.com/dks17 "Konstantin"
[@lewispb]: https://github.com/lewispb "Lewis Buckley"
[@mabrikan]: https://github.com/mabrikan "Musaed Albrikan"
[@magec]: https://github.com/magec "Jose Fernández"
[@skateman]: https://github.com/skateman "Halász Dávid"
[@zzip]: https://github.com/zzip "Dale Hofkens"


================================================
FILE: Gemfile
================================================
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }

# Specify your gem's dependencies in yabeda-rails.gemspec
gemspec

rails_version = ENV.fetch("RAILS_VERSION", "~> 8.0")
case rails_version
when "HEAD"
  git "https://github.com/rails/rails.git" do
    gem "rails"
    gem "activesupport"
    gem "railties"
  end
else
  rails_version = "~> #{rails_version}.0" if rails_version.match?(/^\d+\.\d+$/)
  gem "rails", rails_version
  gem "activesupport", rails_version
  gem "railties", rails_version
end

group :development, :test do
  gem "yabeda", "~> 0.11" # Test helpers
  gem "rspec-rails"

  gem "debug"

  gem "rubocop", "~> 1.8"
  gem "rubocop-rspec"
end


================================================
FILE: LICENSE.txt
================================================
The MIT License (MIT)

Copyright (c) 2018 Andrey Novikov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
# ![Yabeda::Rails](./yabeda-rails-logo.png)

Built-in metrics for out-of-the box [Rails] applications monitoring.

If your monitoring system already collects Rails metrics (e.g. NewRelic) then most probably you don't need this gem.

Sample Grafana dashboard ID: [11668](https://grafana.com/grafana/dashboards/11668)

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'yabeda-rails'
# Then add monitoring system adapter, e.g.:
# gem 'yabeda-prometheus'
```

And then execute:

    $ bundle

### Registering metrics on server process start

Currently, yabeda-rails automatically registers rails metrics when a server is started via `rails server`, `puma -C config/puma.rb` or `unicorn -c`. However, other application servers or launching via `rackup` aren't supported at the moment.

A possible workaround is to detect server process and manually activate yabeda-rails in an initializer:

```ruby
# config/initializers/yabeda.rb

if your_app_server_process? # Your logic here
  Yabeda::Rails.install!
end
```

You always can add support for your app server to [lib/yabeda/rails/railtie.rb](lib/yabeda/rails/railtie.rb). Pull Requests are always welcome!


## Metrics

 - Total web requests received: `rails_requests_total`
 - Web request duration: `rails_request_duration` (in seconds)
 - Views rendering duration: `rails_view_runtime` (in seconds)
 - DB request duration: `rails_db_runtime` (in seconds)


## Hooks

 - `on_controller_action`: Allows to collect

    ```ruby
    Yabeda::Rails.on_controller_action do |event, labels|
      next unless event.payload[:ext_service_runtime]
      time_in_seconds = event.payload[:ext_service_runtime] / 1000.0
      rails_ext_service_runtime.measure(labels, time_in_seconds)
    end
    ```

## Custom tags

You can add additional tags to the existing metrics by adding custom payload to your controller.

```ruby
# This block is optional but some adapters (like Prometheus) requires that all tags should be declared in advance
Yabeda.configure do
  default_tag :importance, nil
end

class ApplicationController < ActionController::Base
  def append_info_to_payload(payload)
    super
    payload[:importance] = extract_importance(params)
  end
end
```
`append_info_to_payload` is a method from [ActionController::Instrumentation](https://api.rubyonrails.org/classes/ActionController/Instrumentation.html#method-i-append_info_to_payload)

## Configuration

Configuration is handled by [anyway_config] gem. With it you can load settings from environment variables (upcased and prefixed with `YABEDA_RAILS_`), YAML files, and other sources. See [anyway_config] docs for details.

| Config key             | Type    | Default | Description                                                                                                                                           |
| ---------------------- | ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `apdex_target`         | integer | nil     | Tolerable time for Apdex in seconds, exposed as gauge if set.                                                                                         |
| `controller_name_case` | symbol  | :snake  | Defines whether controller name is reported in camel case (:camel) or snake case (:snake).                                                            |
| `ignore_actions`       | array or proc | []      | array of controller#action strings or a proc that receives the controller#action string and returns true if the action should be ignored. Controller should be in camel case, example `['HealthCheck::HealthCheckController#index']` or `->(controller_action) { controller_action.start_with?("HealthCheck") }` |

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).

### Releasing

 1. Bump version number in `lib/yabeda/rails/version.rb`

    In case of pre-releases keep in mind [rubygems/rubygems#3086](https://github.com/rubygems/rubygems/issues/3086) and check version with command like `Gem::Version.new(Yabeda::Rails::VERSION).to_s`

 2. Fill `CHANGELOG.md` with missing changes, add header with version and date.

 3. Make a commit:

    ```sh
    git add lib/yabeda/rails/version.rb CHANGELOG.md
    version=$(ruby -r ./lib/yabeda/rails/version.rb -e "puts Gem::Version.new(Yabeda::Rails::VERSION)")
    git commit --message="${version}: " --edit
    ```

 4. Create annotated tag:

    ```sh
    git tag v${version} --annotate --message="${version}: " --edit --sign
    ```

 5. Fill version name into subject line and (optionally) some description (list of changes will be taken from changelog and appended automatically)

 6. Push it:

    ```sh
    git push --follow-tags
    ```

 7. You're done!

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/yabeda-rb/yabeda-rails.

## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).

[Rails]: https://rubyonrails.org "Ruby on Rails MVC web-application framework optimized for programmer happiness"


================================================
FILE: Rakefile
================================================
# frozen_string_literal: true

require "bundler/gem_tasks"
require "rspec/core/rake_task"

RSpec::Core::RakeTask.new(:spec)

task default: :spec


================================================
FILE: bin/console
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

require "bundler/setup"
require "yabeda/rails"

# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.

require "pry"
Pry.start


================================================
FILE: bin/setup
================================================
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx

bundle install

# Do any other automated setup that you need to do here


================================================
FILE: lib/yabeda/rails/config.rb
================================================
# frozen_string_literal: true

require "anyway"

module Yabeda
  module Rails
    # yabeda-rails configuration
    class Config < ::Anyway::Config
      config_name :yabeda_rails

      attr_config :apdex_target
      attr_config :buckets
      attr_config controller_name_case: :snake
      attr_config ignore_actions: []
    end
  end
end


================================================
FILE: lib/yabeda/rails/event.rb
================================================
# frozen_string_literal: true

module Yabeda
  module Rails
    # ActiveSupport Event with added logic for Yabeda tags formatting
    class Event < ActiveSupport::Notifications::Event
      def labels
        @labels ||= begin
          labels = {
            controller: controller,
            action: action,
            status: status,
            format: format,
            method: method,
          }
          labels.merge(payload.slice(*(Yabeda.default_tags.keys + Yabeda.rails.default_tags.keys) - labels.keys))
        end
      end

      def duration
        ms2s super
      end

      def view_runtime
        ms2s payload[:view_runtime]
      end

      def db_runtime
        ms2s payload[:db_runtime]
      end

      def controller_action
        "#{payload[:controller]}##{payload[:action]}"
      end

      private

      def controller
        case Yabeda::Rails.config.controller_name_case
        when :camel
          payload[:controller]
        else
          payload[:params]["controller"]
        end
      end

      def action
        payload[:action]
      end

      def status
        if payload[:status].nil? && payload[:exception].present?
          ActionDispatch::ExceptionWrapper.status_code_for_exception(payload[:exception].first)
        else
          payload[:status]
        end
      end

      def format
        payload[:format]
      end

      def method
        payload[:method].downcase
      end

      def ms2s(milliseconds)
        (milliseconds.to_f / 1000).round(3)
      end
    end
  end
end


================================================
FILE: lib/yabeda/rails/railtie.rb
================================================
# frozen_string_literal: true

# Explicitly require yabeda's railtie in case if its require was skipped there.
# See https://github.com/yabeda-rb/yabeda/issues/15
require "yabeda/railtie"

module Yabeda
  module Rails
    class Railtie < ::Rails::Railtie # :nodoc:
      def rails_server?
        ::Rails.const_defined?(:Server)
      end

      def puma_server?
        ::Rails.const_defined?("Puma::CLI")
      end

      def unicorn_server?
        ::Rails.const_defined?("Unicorn::Launcher")
      end

      def passenger_server?
        ::Rails.const_defined?("PhusionPassenger")
      end

      initializer "yabeda-rails.metrics" do
        ::Yabeda::Rails.install! if rails_server? || puma_server? || unicorn_server? || passenger_server?
      end
    end
  end
end


================================================
FILE: lib/yabeda/rails/version.rb
================================================
# frozen_string_literal: true

module Yabeda
  module Rails
    VERSION = "0.11.0"
  end
end


================================================
FILE: lib/yabeda/rails.rb
================================================
# frozen_string_literal: true

require "yabeda"
require "active_support"
require "rails/railtie"
require "yabeda/rails/railtie"
require "yabeda/rails/config"
require "yabeda/rails/event"

module Yabeda
  # Minimal set of Rails-specific metrics for using with Yabeda
  module Rails
    LONG_RUNNING_REQUEST_BUCKETS = [
      0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, # standard
      30, 60, 120, 300, 600, # Sometimes requests may be really long-running
    ].freeze

    class << self
      def controller_handlers
        @controller_handlers ||= []
      end

      def on_controller_action(&block)
        controller_handlers << block
      end

      # Declare metrics and install event handlers for collecting themya
      # rubocop: disable Metrics/MethodLength, Metrics/BlockLength, Metrics/AbcSize
      def install!
        Yabeda.configure do
          config = ::Yabeda::Rails.config
          buckets = config.buckets || LONG_RUNNING_REQUEST_BUCKETS

          group :rails

          counter   :requests_total,   comment: "A counter of the total number of HTTP requests rails processed.",
                                       tags: %i[controller action status format method]

          histogram :request_duration, tags: %i[controller action status format method],
                                       unit: :seconds,
                                       buckets: buckets,
                                       comment: "A histogram of the response latency."

          histogram :view_runtime, unit: :seconds, buckets: buckets,
                                   comment: "A histogram of the view rendering time.",
                                   tags: %i[controller action status format method]

          histogram :db_runtime, unit: :seconds, buckets: buckets,
                                 comment: "A histogram of the activerecord execution time.",
                                 tags: %i[controller action status format method]

          if config.apdex_target
            gauge :apdex_target, unit: :seconds,
                                 comment: "Tolerable time for Apdex (T value: maximum duration of satisfactory request)"
            collect { rails_apdex_target.set({}, config.apdex_target) }
          end

          ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*args|
            event = Yabeda::Rails::Event.new(*args)

            # rubocop: disable Style/CaseEquality
            next if Array(config.ignore_actions).any? { |action| action === event.controller_action }
            # rubocop: enable Style/CaseEquality

            rails_requests_total.increment(event.labels)
            rails_request_duration.measure(event.labels, event.duration)
            rails_view_runtime.measure(event.labels, event.view_runtime)
            rails_db_runtime.measure(event.labels, event.db_runtime)

            Yabeda::Rails.controller_handlers.each do |handler|
              handler.call(event, event.labels)
            end
          end
        end
      end
      # rubocop: enable Metrics/MethodLength, Metrics/BlockLength, Metrics/AbcSize

      def config
        @config ||= Config.new
      end
    end
  end
end


================================================
FILE: spec/spec_helper.rb
================================================
# frozen_string_literal: true

ENV["RAILS_ENV"] = "test"

require "bundler/setup"
require "logger"
require "debug"
require "yabeda/rails"
require "yabeda/rspec"

require_relative "support/rails_app"

require "rspec/rails"

RSpec.configure do |config|
  # Enable flags like --only-failures and --next-failure
  config.example_status_persistence_file_path = ".rspec_status"

  # Disable RSpec exposing methods globally on `Module` and `main`
  config.disable_monkey_patching!

  config.expect_with :rspec do |c|
    c.syntax = :expect
  end

  Kernel.srand config.seed
  config.order = :random

  config.before(:suite) do
    Yabeda::Rails.install!
  end
end


================================================
FILE: spec/support/rails_app.rb
================================================
# frozen_string_literal: true

require "rails"
require "action_controller/railtie"
require "active_support/railtie"

class TestApplication < Rails::Application
  config.logger = Logger.new($stdout)
  config.log_level = :fatal
  config.consider_all_requests_local = true
  config.eager_load = true

  routes.append do
    get "/hello/world" => "hello#world"
    get "/hello/long" => "hello#long"
    get "/hello/internal_server_error" => "hello#internal_server_error"
  end
end

class HelloController < ActionController::API
  def append_info_to_payload(payload)
    super
    payload[:custom_tag_from_rails] = "hello-world-from-rails"
    payload[:custom_tag] = "hello-world"
  end

  def world
    render json: { hello: :world }
  end

  def long
    sleep(0.01)
    render json: { good: :morning }
  end

  def internal_server_error
    raise StandardError
  end
end

TestApplication.initialize!


================================================
FILE: spec/yabeda/rails_spec.rb
================================================
# frozen_string_literal: true

require "action_controller/test_case"

RSpec.describe Yabeda::Rails, type: :integration do
  include ActionDispatch::Integration::Runner
  include ActionDispatch::IntegrationTest::Behavior

  def app
    TestApplication
  end

  it "increments counters for every request" do
    expect { get "/hello/world" }.to \
      increment_yabeda_counter(Yabeda.rails.requests_total)
      .with_tags(controller: "hello", action: "world", status: 200, method: "get", format: :html)
      .by(1)
  end

  it "measure action runtime for every request" do
    expect { get "/hello/long" }.to \
      measure_yabeda_histogram(Yabeda.rails.request_duration)
      .with_tags(controller: "hello", action: "long", status: 200, method: "get", format: :html)
      .with(be_between(0.005, 0.05))
  end

  it "returns internal_server_error status code" do
    expect { get "/hello/internal_server_error" }.to \
      increment_yabeda_counter(Yabeda.rails.requests_total)
      .with_tags(controller: "hello", action: "internal_server_error", status: 500, method: "get", format: :html)
  end

  context "with changed controller name case config tp camel case" do
    around do |example|
      original_case = described_class.config.controller_name_case
      described_class.config.controller_name_case = :camel
      example.call
    ensure
      described_class.config.controller_name_case = original_case
    end

    it "reports controller tag in camel case" do
      expect { get "/hello/world" }.to \
        increment_yabeda_counter(Yabeda.rails.requests_total)
        .with_tags(controller: "HelloController", action: "world", status: 200, method: "get", format: :html)
        .by(1)
    end
  end

  context "with default_tags set" do
    before do
      Yabeda.default_tag :custom_tag, nil
    end

    it "increments counters for every request" do
      expect { get "/hello/world" }.to \
        increment_yabeda_counter(Yabeda.rails.requests_total)
        .with_tags(custom_tag: "hello-world")
        .by(1)
    end
  end

  context "with ':rails' default_tags set" do
    before do
      Yabeda.default_tag :custom_tag_from_rails, nil, group: :rails
    end

    it "increments counters for every request" do
      expect { get "/hello/world" }.to \
        increment_yabeda_counter(Yabeda.rails.requests_total)
        .with_tags(custom_tag_from_rails: "hello-world-from-rails")
        .by(1)
    end
  end

  context "with ignore_actions" do
    around do |example|
      original_ignore_actions = described_class.config.ignore_actions
      described_class.config.ignore_actions = ["HelloController#world", "HelloController#long"]
      example.call
    ensure
      described_class.config.ignore_actions = original_ignore_actions
    end

    it "ignores actions matching the proc" do
      expect { get "/hello/world" }.not_to \
        increment_yabeda_counter(Yabeda.rails.requests_total)
    end
  end

  context "with ignore_actions as a proc" do
    around do |example|
      original_ignore_actions = described_class.config.ignore_actions
      described_class.config.ignore_actions = lambda { |controller_action|
        controller_action.start_with?("HelloController#")
      }
      example.call
    ensure
      described_class.config.ignore_actions = original_ignore_actions
    end

    it "ignores actions matching the proc" do
      expect { get "/hello/world" }.not_to \
        increment_yabeda_counter(Yabeda.rails.requests_total)
    end
  end
end


================================================
FILE: yabeda-rails.gemspec
================================================
# frozen_string_literal: true

lib = File.expand_path("lib", __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "yabeda/rails/version"

Gem::Specification.new do |spec|
  spec.name          = "yabeda-rails"
  spec.version       = Yabeda::Rails::VERSION
  spec.authors       = ["Andrey Novikov"]
  spec.email         = ["envek@envek.name"]

  spec.summary       = "Extensible metrics for monitoring Ruby on Rails application"
  spec.description   = "Easy collecting your Rails apps metrics"
  spec.homepage      = "https://github.com/yabeda-rb/yabeda-rails"
  spec.license       = "MIT"

  spec.metadata = {
    "changelog_uri" => "https://github.com/yabeda-rb/yabeda-rails/blob/master/CHANGELOG.md",
    "rubygems_mfa_required" => "true",
  }

  spec.files = `git ls-files -z`.split("\x0").reject do |f|
    f.match(%r{^(test|spec|features)/})
  end
  spec.bindir        = "exe"
  spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]

  spec.required_ruby_version = ">= 2.5"

  spec.add_dependency "activesupport"
  spec.add_dependency "anyway_config", ">= 1.3", "< 3"
  spec.add_dependency "railties"
  spec.add_dependency "yabeda", "~> 0.8"

  spec.add_development_dependency "bundler", ">= 2.0"
  spec.add_development_dependency "rake", "~> 13.0"
  spec.add_development_dependency "rspec", "~> 3.0"
end
Download .txt
gitextract__sq7l337/

├── .github/
│   └── workflows/
│       ├── lint.yml
│       ├── release.yml
│       └── test.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── CHANGELOG.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin/
│   ├── console
│   └── setup
├── lib/
│   └── yabeda/
│       ├── rails/
│       │   ├── config.rb
│       │   ├── event.rb
│       │   ├── railtie.rb
│       │   └── version.rb
│       └── rails.rb
├── spec/
│   ├── spec_helper.rb
│   ├── support/
│   │   └── rails_app.rb
│   └── yabeda/
│       └── rails_spec.rb
└── yabeda-rails.gemspec
Download .txt
SYMBOL INDEX (39 symbols across 7 files)

FILE: lib/yabeda/rails.rb
  type Yabeda (line 10) | module Yabeda
    type Rails (line 12) | module Rails
      function controller_handlers (line 19) | def controller_handlers
      function on_controller_action (line 23) | def on_controller_action(&block)
      function install! (line 29) | def install!
      function config (line 78) | def config

FILE: lib/yabeda/rails/config.rb
  type Yabeda (line 5) | module Yabeda
    type Rails (line 6) | module Rails
      class Config (line 8) | class Config < ::Anyway::Config

FILE: lib/yabeda/rails/event.rb
  type Yabeda (line 3) | module Yabeda
    type Rails (line 4) | module Rails
      class Event (line 6) | class Event < ActiveSupport::Notifications::Event
        method labels (line 7) | def labels
        method duration (line 20) | def duration
        method view_runtime (line 24) | def view_runtime
        method db_runtime (line 28) | def db_runtime
        method controller_action (line 32) | def controller_action
        method controller (line 38) | def controller
        method action (line 47) | def action
        method status (line 51) | def status
        method format (line 59) | def format
        method method (line 63) | def method
        method ms2s (line 67) | def ms2s(milliseconds)

FILE: lib/yabeda/rails/railtie.rb
  type Yabeda (line 7) | module Yabeda
    type Rails (line 8) | module Rails
      class Railtie (line 9) | class Railtie < ::Rails::Railtie # :nodoc:
        method rails_server? (line 10) | def rails_server?
        method puma_server? (line 14) | def puma_server?
        method unicorn_server? (line 18) | def unicorn_server?
        method passenger_server? (line 22) | def passenger_server?

FILE: lib/yabeda/rails/version.rb
  type Yabeda (line 3) | module Yabeda
    type Rails (line 4) | module Rails

FILE: spec/support/rails_app.rb
  class TestApplication (line 7) | class TestApplication < Rails::Application
  class HelloController (line 20) | class HelloController < ActionController::API
    method append_info_to_payload (line 21) | def append_info_to_payload(payload)
    method world (line 27) | def world
    method long (line 31) | def long
    method internal_server_error (line 36) | def internal_server_error

FILE: spec/yabeda/rails_spec.rb
  function app (line 9) | def app
Condensed preview — 22 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (34K chars).
[
  {
    "path": ".github/workflows/lint.yml",
    "chars": 646,
    "preview": "name: Lint\n\non:\n  pull_request:\n  push:\n    branches:\n      - '**'\n    tags-ignore:\n      - 'v*'\n\njobs:\n  rubocop:\n    #"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 3747,
    "preview": "name: Build and release gem\n\non:\n  push:\n    tags:\n      - v*\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    permissio"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 1335,
    "preview": "name: Tests\n\non:\n  pull_request:\n  push:\n    branches:\n      - '**'\n    tags-ignore:\n      - 'v*'\n\njobs:\n  test:\n    nam"
  },
  {
    "path": ".gitignore",
    "chars": 126,
    "preview": "/.bundle/\n/.yardoc\n/_yardoc/\n/coverage/\n/doc/\n/pkg/\n/spec/reports/\n/tmp/\nGemfile.lock\n\n# rspec failure tracking\n.rspec_s"
  },
  {
    "path": ".rspec",
    "chars": 53,
    "preview": "--format documentation\n--color\n--require spec_helper\n"
  },
  {
    "path": ".rubocop.yml",
    "chars": 1091,
    "preview": "---\nrequire:\n- rubocop-rspec\n\nAllCops:\n  TargetRubyVersion: 2.5\n\nMetrics/BlockLength:\n  Exclude:\n  - \"Gemfile\"\n  - \"spec"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 4017,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
  },
  {
    "path": "Gemfile",
    "chars": 741,
    "preview": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\n\ngit_source(:github) { |repo_name| \"https://github.com/#{re"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1081,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2018 Andrey Novikov\n\nPermission is hereby granted, free of charge, to any person ob"
  },
  {
    "path": "README.md",
    "chars": 5648,
    "preview": "# ![Yabeda::Rails](./yabeda-rails-logo.png)\n\nBuilt-in metrics for out-of-the box [Rails] applications monitoring.\n\nIf yo"
  },
  {
    "path": "Rakefile",
    "chars": 145,
    "preview": "# frozen_string_literal: true\n\nrequire \"bundler/gem_tasks\"\nrequire \"rspec/core/rake_task\"\n\nRSpec::Core::RakeTask.new(:sp"
  },
  {
    "path": "bin/console",
    "chars": 276,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nrequire \"bundler/setup\"\nrequire \"yabeda/rails\"\n\n# You can add fixture"
  },
  {
    "path": "bin/setup",
    "chars": 131,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\nIFS=$'\\n\\t'\nset -vx\n\nbundle install\n\n# Do any other automated setup that you need "
  },
  {
    "path": "lib/yabeda/rails/config.rb",
    "chars": 341,
    "preview": "# frozen_string_literal: true\n\nrequire \"anyway\"\n\nmodule Yabeda\n  module Rails\n    # yabeda-rails configuration\n    class"
  },
  {
    "path": "lib/yabeda/rails/event.rb",
    "chars": 1552,
    "preview": "# frozen_string_literal: true\n\nmodule Yabeda\n  module Rails\n    # ActiveSupport Event with added logic for Yabeda tags f"
  },
  {
    "path": "lib/yabeda/rails/railtie.rb",
    "chars": 775,
    "preview": "# frozen_string_literal: true\n\n# Explicitly require yabeda's railtie in case if its require was skipped there.\n# See htt"
  },
  {
    "path": "lib/yabeda/rails/version.rb",
    "chars": 93,
    "preview": "# frozen_string_literal: true\n\nmodule Yabeda\n  module Rails\n    VERSION = \"0.11.0\"\n  end\nend\n"
  },
  {
    "path": "lib/yabeda/rails.rb",
    "chars": 3217,
    "preview": "# frozen_string_literal: true\n\nrequire \"yabeda\"\nrequire \"active_support\"\nrequire \"rails/railtie\"\nrequire \"yabeda/rails/r"
  },
  {
    "path": "spec/spec_helper.rb",
    "chars": 657,
    "preview": "# frozen_string_literal: true\n\nENV[\"RAILS_ENV\"] = \"test\"\n\nrequire \"bundler/setup\"\nrequire \"logger\"\nrequire \"debug\"\nrequi"
  },
  {
    "path": "spec/support/rails_app.rb",
    "chars": 898,
    "preview": "# frozen_string_literal: true\n\nrequire \"rails\"\nrequire \"action_controller/railtie\"\nrequire \"active_support/railtie\"\n\ncla"
  },
  {
    "path": "spec/yabeda/rails_spec.rb",
    "chars": 3500,
    "preview": "# frozen_string_literal: true\n\nrequire \"action_controller/test_case\"\n\nRSpec.describe Yabeda::Rails, type: :integration d"
  },
  {
    "path": "yabeda-rails.gemspec",
    "chars": 1379,
    "preview": "# frozen_string_literal: true\n\nlib = File.expand_path(\"lib\", __dir__)\n$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?"
  }
]

About this extraction

This page contains the full source code of the yabeda-rb/yabeda-rails GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 22 files (30.7 KB), approximately 8.6k tokens, and a symbol index with 39 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!