Giter VIP home page Giter VIP logo

yabeda's Introduction

Yabeda

Gem Version Tests status

Extendable solution for easy setup of monitoring in your Ruby apps.

Sponsored by Evil Martians

Read more about Yabeda and the reasoning behind it in Martian Chronicles: “Meet Yabeda: Modular framework for instrumenting Ruby applications”

Installation

Most of the time you don't need to add this gem to your Gemfile directly (unless you're only collecting your custom metrics):

gem 'yabeda'

# Add some plugins to quickly start collecting some essential metrics:
# gem 'yabeda-rails'
# gem 'yabeda-sidekiq'

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

And then execute:

$ bundle

Usage

  1. Declare your metrics:

    Yabeda.configure do
      group :your_app do
        counter   :bells_rang_count, comment: "Total number of bells being rang", tags: %i[bell_size]
        gauge     :whistles_active,  comment: "Number of whistles ready to whistle"
        histogram :whistle_runtime do
          comment "How long whistles are being active"
          unit :seconds
        end
        summary :bells_ringing_duration, unit: :seconds, comment: "How long bells are ringing"
      end
    end
  2. After your application was initialized and all metrics was declared, you need to apply Yabeda configuration:

    Yabeda.configure!

    If you're using Ruby on Rails then it will be configured automatically!

  3. Access metric in your app and use it!

    def ring_the_bell(id)
      bell = Bell.find(id)
      bell.ring!
      Yabeda.your_app.bells_rang_count.increment({bell_size: bell.size}, by: 1)
    end
    
    def whistle!
      Yabeda.your_app.whistle_runtime.measure do
        # Run your code
      end
    end
  4. Setup collecting of metrics that do not tied to specific events in you application. E.g.: reporting your app's current state

    Yabeda.configure do
      # This block will be executed periodically few times in a minute
      # (by timer or external request depending on adapter you're using)
      # Keep it fast and simple!
      collect do
        your_app.whistles_active.set({}, Whistle.where(state: :active).count)
      end
    end
  5. Optionally setup default tags for all appropriate metrics

    Yabeda.configure do
      # matches all metrics in all groups
      default_tag :rails_environment, 'production'
    
      # matches all metrics in the :your_app group
      default_tag :tag_name, 'override', group: :your_app
    end
    
    # You can redefine them for limited amount of time
    Yabeda.with_tags(rails_environment: 'staging') do
      Yabeda.your_app.bells_rang_count.increment({bell_size: bell.size}, by: 1)
    end

    Note: any usage of with_tags must have all those tags defined on all metrics that are generated in the block.

  6. Optionally override default tags using precedence:

    The tag precedence from high to low is:

    • Manually specified tags
    • Thread local tags (specified by Yabeda.with_tags)
    • Group specific tags
    • Global tags
  7. See the docs for the adapter you're using

  8. Enjoy!

Available monitoring system adapters

Maintained by Yabeda

Third-party adapters

These are developed and maintained by other awesome folks:

Available plugins to collect metrics

Maintained by Yabeda

Third-party plugins

These are developed and maintained by other awesome folks:

Configuration

Configuration is handled by anyway_config gem. With it you can load settings from environment variables (which names are constructed from config key upcased and prefixed with YABEDA_), YAML files, and other sources. See anyway_config docs for details.

Config key Type Default Description
debug boolean false Collects metrics measuring Yabeda performance

Debugging metrics

  • Time of collector block run: yabeda_collect_duration (segmented by block source location). Collector blocks are used for collecting metrics of application state and usually makes some potentially slow queries to databases, network requests, etc.

These are only enabled in debug mode. To enable it either set debug config key to true (e.g. by specifying YABEDA_DEBUG=true in your environment variables or executing Yabeda.debug! in your code).

Testing

RSpec

Add the following to your rails_helper.rb (or spec_helper.rb):

require "yabeda/rspec"

Now you can use increment_yabeda_counter, update_yabeda_gauge, measure_yabeda_histogram, and observe_yabeda_summary matchers:

it "increments counters" do
  expect { subject }.to increment_yabeda_counter(Yabeda.myapp.foo_count).by(3)
end

You can scope metrics by used tags with with_tags:

it "updates gauges" do
  expect { subject }.to \
    update_yabeda_gauge("some_gauge_name").
    with_tags(method: "command", command: "subscribe")
end

Note that tags you specified doesn't need to be exact, but can be a subset of tags used on metric update. In this example updates with following sets of tags { method: "command", command: "subscribe", status: "SUCCESS" } and { method: "command", command: "subscribe", status: "FAILURE" } will make test example to pass.

And check for values with by for counters, to for gauges, and with for histograms and summaries (and you can use other matchers here):

expect { subject }.to \
  measure_yabeda_histogram(Yabeda.something.anything_runtime).
  with(be_between(0.005, 0.05))

You also can specify multiple tags and their expected values in with:

expect { whatever }.to increment_yabeda_counter(:my_counter).with(
  { tag: "foo" } => 1,
  { tag: "bar" } => (be >= 42),
)

Roadmap (aka TODO or Help wanted)

  • Ability to change metric settings for individual adapters

    histogram :foo, comment: "say what?" do
      adapter :prometheus do
        buckets [0.01, 0.5, , 60, 300, 3600]
      end
    end
  • Ability to route some metrics only for given adapter:

    adapter :prometheus do
      include_group :sidekiq
    end

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.

Releasing

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

    In case of pre-releases keep in mind rubygems/rubygems#3086 and check version with command like Gem::Version.new(Yabeda::VERSION).to_s

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

  3. Make a commit:

    git add lib/yabeda/version.rb CHANGELOG.md
    version=$(ruby -r ./lib/yabeda/version.rb -e "puts Gem::Version.new(Yabeda::VERSION)")
    git commit --message="${version}: " --edit
  4. Create annotated tag:

    git tag v${version} --annotate --message="${version}: " --edit --sign
  5. Fill version name into subject line and (optionally) some description (changes will be taken from changelog and appended automatically)

  6. Push it:

    git push --follow-tags

Contributing

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

License

The gem is available as open source under the terms of the MIT License.

yabeda's People

Contributors

asusikov avatar dmitrytsepelev avatar dsalahutdinov avatar envek avatar gkostin1966 avatar ianks avatar ilyasvaliullov avatar lewispb avatar liaden avatar olleolleolle avatar retsef avatar stympy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

yabeda's Issues

Global tags

What is about global/common tags? For example, we have few microservices and one prometheus server. But we want to filter metrics by app name. It will be awesome to add app name tag to any metric.
How I see this feature. We define global label in configuration

Yabeda.configure do
  # declare metrics
  global_label :app_name, value: 'awesome_app'
end 

It will be add tag app_name with value awesome_app to each metrics.
What is your opinion about it?

Access Puma stats in models

Hi, this is not really an issue with the code, rather a request for information. I have a Rails (API) only application, running on Puma, and I need to know how much load my application is under in the Rails model (it will inform the behaviour of the model), Initially I was planning to interrogate the Puma state, then the socket and so, on, but I see all this is already implemented in Yabeda, so seems a no-brainer to use it, but I've been grepping around the documentation, the code, I can't seem to find out how to access the Puma stats directly. Any pointers would be much appreciated!

Thanks

Current handling of groups leaves room for user error/confusion

Given the current implementation of group method for DSL:

def group(group_name)
@group = group_name
return unless block_given?
yield
@group = nil
end

We can configure sibling groups

Yabeda.configure do
  group :g1
  # config
  group :g2
  # config
  end

or attempt to have nested groups

Yabeda.configure do
  group :g1 do
    # config
    group :g2 do
      # config
    end
    # more g1 config 
  end 

The nested groups are really the same as the first situation where we create two siblings. Both default_tags and looking up metrics would be handled the same in both cases with an additional surprise that the 'more g1 config'j would apply to the global scope.

I think that it would be good to raise an exception or at least warn when someone attempts to nest two groups to prevent the issue until there is a need for deeper nesting?

Originally posted by @liaden in #19 (comment)

Allow to run `collect` blocks in every process of multi-process app

We managed to make this work by adding the following to puma.rb

on_worker_boot(:connection_pool_timer) do |index, data|
  ActiveSupport.on_load(:active_record) do
    data[:connection_pool_timer] = Concurrent::TimerTask.new(execution_interval: 2) do
      ActiveRecord::Base.connection_handler.all_connection_pools.each do |connection_pool|
        tags = { index: index, name: connection_pool.db_config.name }
        connection_pool.stat.each do |name, value|
          Yabeda.activerecord.send(:"connection_pool_#{name}").set(tags, value)
        end
      end
    end.tap(&:execute)
  end
end

on_worker_shutdown(:connection_pool_timer) do |index, data|
  data[:connection_pool_timer]&.shutdown
end

But what do you think about supporting this out the box?

Make it possible to export multiple sets of metrics in the same process

Use case

There are multiple instances of an application (running on different servers). Some metrics are instance-specific (e.g. about the requests handled by a specific instance). Some metrics are not instance-specific (those are based on data from a single database). In our case, the application is a Rails application, but that shouldn’t matter for much for this feature request.

For the instance-specific metrics, each instance is scraped (in our case, by Prometheus). The metrics that are not instance-specific should be exported by each instance but only a single instance should scraped at a given time (in our case, there is load balancing by Kubernetes, but the details don’t matter here).

Current solutions

If we export both sets of metrics on each instance, the same data (metrics that are not instance-specific) will be collected multiple times by Prometheus, wasting resources and making handling of the data more complicated.

We could launch separate processes to export the metrics that are not instance-specific, but that complicates deployment and uses extra resources.

Desired solution

It should be possible to export both sets of metrics in the same process but on different ports and / or paths.

Proposed feature

It should be possible to create multiple instances of the Yabeda class that each can be configured separately.

Provide DSL to configure store settings

Prometheus define new settings for metrics store_setting
https://github.com/prometheus/client_ruby#aggregation-settings-for-multi-process-stores

It would be nice to have something like this:

gauge :users_count do
  comment "Number of total users"
  store aggregation: :max
end

Looks like enough to add one more option here:
https://github.com/yabeda-rb/yabeda/blob/master/lib/yabeda/metric.rb

option :store,   default: -> { {} }, optional: true, comment: "Custom store settings"

Yabeda.configure! is not called when starting server via bundle exec puma

Hi,

If you're using Ruby on Rails then it will be configured automatically!

README claims that Yabeda.configure! is automatically called when it comes to rails but when the server is booted via bundle exec puma, Yabeda.configure! is not called and leads to NoSuchMethod error (e.g., undefined method sidekiq_jobs_enqueued_total' for Yabeda:Module). Manually calling Yabeda.configure!inconfig/initializers/yabeda.rb` fixed the problem.

Thanks.

Allow Yabeda::Counter#increment to be called without argument

Hi, this is a small feature request that should be backwards compatible.

Issue

We have some counters in our app that we're not using any tags with. These counters are currently incremented like this

Yabeda.some_counter.increment({})

The increment method has one required positional argument -tags- which has no default value.

def increment(tags, by: 1)

Suggestion

Define tags to have a default value of {} so that increment can be called without parentheses

def increment(tags = {}, by: 1)
Yabeda.some_counter.increment

Doesn't work in Ruby 2.2

I tried to use the yabeda gems and after bundle install when I am going to run the rails s, I have this error.

option_names': undefined method dry_initializer' for Yabeda::Counter:Class (NoMethodError)

I am trying to use them in a rails 4.2 project with ruby 2.2.1

Can you help me? thank you.

Export only desired metrics

Hey @Envek ,

Right now hitting /metrics displays all the default metrics. How can I restrict it to display only those metrics which I need?

No apply default_tags to all metrics

Hi,

Just wanted to ask if it was possible to add default tags only to specific metrics and not all.
In my use case I have set up a default tag that's currently being applied to all metrics exported by yabeda-rails but, there are other metrics where I don't want that tag to appear, is it possible to hide/not apply them?

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.