{"id":13880244,"url":"https://github.com/basecamp/easymon","last_synced_at":"2025-05-15T03:05:11.348Z","repository":{"id":12040756,"uuid":"14626248","full_name":"basecamp/easymon","owner":"basecamp","description":"Easy Monitoring","archived":false,"fork":false,"pushed_at":"2024-11-25T16:32:10.000Z","size":100,"stargazers_count":204,"open_issues_count":3,"forks_count":17,"subscribers_count":30,"default_branch":"master","last_synced_at":"2025-05-15T03:04:59.198Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/basecamp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-11-22T18:41:52.000Z","updated_at":"2025-05-15T02:04:19.000Z","dependencies_parsed_at":"2022-09-03T14:31:12.539Z","dependency_job_id":"2b9ab250-3aa4-490e-9541-27a31c7654ab","html_url":"https://github.com/basecamp/easymon","commit_stats":{"total_commits":61,"total_committers":9,"mean_commits":6.777777777777778,"dds":"0.24590163934426235","last_synced_commit":"9c3a63193bca9de4b7c5488a600de51995643060"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basecamp%2Feasymon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basecamp%2Feasymon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basecamp%2Feasymon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basecamp%2Feasymon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/basecamp","download_url":"https://codeload.github.com/basecamp/easymon/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254264765,"owners_count":22041793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-06T08:02:53.448Z","updated_at":"2025-05-15T03:05:11.300Z","avatar_url":"https://github.com/basecamp.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Easymon\n\nEasymon helps you monitor your application's availability.  It provides a simple\nway to test the availability of resources your application needs, like the\napplication database, a memcached connection, or a redis instance.  These test\nresults can be used by a load balancer to determine the general health and\nviability of the node your application is running on.\n\nIt's packaged up as a rails engine for 3.1 and greater, and a plugin for 2.3 -\n3.0.\n\n## History\n\nThis gem extracts and modularizes the logic we had in our monitoring controllers\nand were copying back and forth between applications.\n\n## Installation\n\nAdd to Gemfile and bundle!:\n\n````ruby\ngem 'easymon'\n````\n\n## Usage\nTo get started, you'll need to add an initializer for this to do anything.\nIn `config/initializers/easymon.rb`:\n\n````ruby\nEasymon::Repository.add(\"application-database\", Easymon::ActiveRecordCheck.new(ActiveRecord::Base))\n````\n\nThis will register a check called `application-database` for use.\n\nNext, we need to add the routes to your application. Depending on the rails\nversion, this is done one of two ways:\n\n### Rails 2.3.x \u0026 3.0\nAdd `Easymon.routes(map)` to your `config/routes.rb`.  This will put the Easymon\nroutes under `/up`.  If you want Easymon mounted somewhere other than `/up`, use\n`Easymon.routes(map, \"/monitoring\")`.  That would put the Easymon paths under\n`/monitoring`.  For Rails 3.0, the default routes file does not provide `map`,\nso use `Easymon.routes(self)` instead.\n\n### Rails 3.1+\nRails 3.1+ gives us mountable engines, so use the standard syntax, adding\n`mount Easymon::Engine =\u003e \"/up\"` to your `config/routes.rb`.\n\n\nNow, you can run your entire checklist by visiting `/up`, or wherever you have\nmounted the application.  If you want to just test the single check, go to\n`/up/application-database`, and only the check named `application-database` will\nbe run.\n\n### Critical Checks\nIf you have several services that are critical to your app, and others that\nare not, you can segregate those for health check purposes if you wish.\nAssuming your database and redis are critical, but memcached is not, again in\n`config/initializers/easymon.rb`:\n\n````ruby\nEasymon::Repository.add(\n  \"application-database\",\n  Easymon::ActiveRecordCheck.new(ActiveRecord::Base),\n  :critical\n)\nEasymon::Repository.add(\n  \"redis\",\n  Easymon::RedisCheck.new(\n    YAML.load_file(\n      Rails.root.join(\"config/redis.yml\")\n    )[Rails.env].symbolize_keys\n  ),\n  :critical\n)\nEasymon::Repository.add(\n  \"memcached\",\n  Easymon::MemcachedCheck.new(Rails.cache)\n)\n````\n\nIn addition to the main route `/up`, this will register four checks,\nindividually available at:\n\n * `/up/application-database`\n * `/up/redis`\n * `/up/memcached`\n * `/up/critical` - Runs both the application-database and redis checks.\n\n## Security\n\nYou might not want to have this data available to everyone who hits your site,\nas it can expose both timing data and, depending on your check names, various\nbits of your infrastructure.  You can tell Easymon what addresses, headers,\nor whatever defines an authorized request by providing a block to\n`Easymon.authorize_with` that will be called with the current request object:\n\n```ruby\nEasymon.authorize_with = Proc.new { |request| request.remote_ip == '192.168.1.1'}\n# Or\nEasymon.authorize_with = Proc.new { |request|\n  request.headers[\"X-Forwarded-For\"].nil?\n}\n```\n\nThis will get run on each request, so keep it simple. (Actually, that's a good\nrule of thumb for any checks you write, too.  Remember, these are all in your\nmain app request pipeline!)\n\n## Checks\n\nA check can be any ruby code that responds_to? a #check method that returns a\ntwo element array. The first element is the result of executing the check and\nshould be true or false. The second element is the message describing what's\ngoing on.  The array would look something like this: `[true, \"Up\"]` in the\ncase of a successful check or `[false, \"Timeout\"]` in the case of a failed\ncheck.\n\n### Included Checks\n\n * ActiveRecord\n * Redis\n * Memcached\n * Semaphore\n * Traffic Enabled\n * Split ActiveRecord\n * Http\n\n### ActiveRecord\n`Easymon::ActiveRecordCheck` is a basic check that uses ActiveRecord to check\nthe availability of your main database.  It's usually invoked as such:\n\n````ruby\nEasymon::ActiveRecordCheck.new(ActiveRecord::Base)\n````\n\nInternally, it checks `klass.connection.active?` where klass is whatever class\nyou passed to the check.  Usually this will be ActiveRecord::Base, but feel free\nto go crazy.\n\n### Redis\n`Easymon::RedisCheck` will check the availability of a Redis server, given an\nappropriate config hash.  Typically, we'll read the config off disk, but as long\nas you get a valid config hash, this will work:\n\n````ruby\nEasymon::RedisCheck.new(\n  YAML.load_file(Rails.root.join(\"config/redis.yml\"))[Rails.env].symbolize_keys\n)\n````\n\nThis is the most visually complex test to instantiate, but it's only because\nwe're loading the config from disk and getting the config block that matches\nthe Rails.env in one line.  As long as you pass a hash that can be used by\nRedis.new, it doesn't care where the config comes from.\n\n### Memcached\n`Easymon::MemcachedCheck` is a basic check that will write and then read a key\nfrom the cache.  It expects a cache instance to check, so it could be as easy\nas:\n\n````ruby\nEasymon::MemcachedCheck.new(Rails.cache)\n````\n\n### Semaphore\n`Easymon::Semaphore` checks for the presence of a file on disk relative to the\nRails.root of the current application.\n\n````ruby\ncheck = Easymon::SemaphoreCheck.new(\"config/redis.yml\")\n````\n\nThis is mainly a check that gets subclassed by the next check.\n\n### Traffic Enabled\n`Easymon::TrafficEnabledCheck` is fairly specific, but when we want a server to\naccept traffic, we can place a file in the Rails.root, and the load balancers\ncan use the result of this check to help decide whether or not to send traffic\nto the node.\n\n````ruby\nEasymon::TrafficEnabledCheck.new(\"enable-traffic\")\n````\nThis is a subclass of the Semaphore check mentioned above.\n\n### Split ActiveRecord\n`Easymon::SplitActiveRecordCheck` is the most complicated check, as it's not\nsomething you can use out of the gate. Here we pass a block so we get a fresh\ninstance of `ActiveRecord::Base` or whatever other class we might be using to\nmake a secondary database connection.\n\nFor example, given the following other class:\n\n````ruby\nmodule Easymon\n  class Base \u003c ActiveRecord::Base\n    def establish_connection(spec = nil)\n      if spec\n        super\n      elsif config = Easymon.database_configuration\n        super config\n      end\n    end\n    def database_configuration\n      env = \"#{Rails.env}_replica\"\n      config = YAML.load_file(Rails.root.join('config/database.yml'))[env]\n    end\n  end\nend\n````\n\nWe would check both it and `ActiveRecord::Base` like so:\n\n````ruby\ncheck = Easymon::SplitActiveRecordCheck.new {\n  [ActiveRecord::Base.connection, Easymon::Base.connection]\n}\nEasymon::Repository.add(\"split-database\", check)\n````\n\n### Http\n`Easymon::HttpCheck` will check the return status of a HEAD request to a URL.\nGreat for checking service endpoint availability! The following will make a\nrequest to port 9200 on localhost, which is where you might have Elasticsearch\nrunning:\n\n````ruby\nEasymon::HttpCheck.new(\"http://localhost:9200\")\n````\n\nTypically, we'll read an elasticsearch config off disk, and use the URL like so:\n\n````ruby\nconfig = YAML.load_file(Rails.root.join(\"config/elasticsearch.yml\"))[Rails.env].symbolize_keys\nEasymon::HttpCheck.new(config[:url])\n````\n\n## Testing\n\nTo run the tests, you need MySQL server installed and running, and accepting connections\non localhost:3306 for the `root` user with a blank password, as configured in\n[database.yml](./test/dummy/config/database.yml).\n\nCreate the MySQL test databases by running:\n\n````\nbundle exec rake db:create\n````\n\nTo run tests on PostgreSQL, you need the server installed and running, and accepting\nconnections on localhost:5432 for the `dummy` user.  You can create the dummy user\nwith the following command in `psql`:\n\n````sql\nCREATE USER dummy WITH PASSWORD 'dummy';\n````\n\nThen run the tests with:\n\n````\nbundle exec rake test\n````\n\n## How to contribute\n\nHere's the most direct way to get your work merged into the project:\n\n1. Fork the project\n2. Clone down your fork\n3. Create a feature branch\n4. Add your feature + tests\n5. Document new features in the README\n6. Make sure everything still passes by running the tests\n7. If necessary, rebase your commits into logical chunks, without errors\n8. Push the branch up\n9. Send a pull request for your branch\n\nIf you're going to make a major change ask first to make sure it's in line with\nthe project goals.\n\n## To Do\n\nSee the issues page. :smile:\n\n## Authors\n\n* [Nathan Anderson](mailto:andnat@gmail.com)\n\n## License\nSee [LICENSE](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbasecamp%2Feasymon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbasecamp%2Feasymon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbasecamp%2Feasymon/lists"}