{"id":13880562,"url":"https://github.com/civo/activetracker","last_synced_at":"2025-04-10T12:41:14.152Z","repository":{"id":53521400,"uuid":"211323205","full_name":"civo/activetracker","owner":"civo","description":"A Rubygem to save full log/output of your requests, errors, etc for viewing within ActiveTracker","archived":false,"fork":false,"pushed_at":"2021-03-26T09:12:18.000Z","size":227,"stargazers_count":7,"open_issues_count":1,"forks_count":3,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-24T11:21:33.802Z","etag":null,"topics":["in-progress","ruby","ruby-on-rails"],"latest_commit_sha":null,"homepage":"","language":"SCSS","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/civo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-09-27T13:16:15.000Z","updated_at":"2023-09-28T04:41:11.000Z","dependencies_parsed_at":"2022-09-02T03:00:45.883Z","dependency_job_id":null,"html_url":"https://github.com/civo/activetracker","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/civo%2Factivetracker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/civo%2Factivetracker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/civo%2Factivetracker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/civo%2Factivetracker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/civo","download_url":"https://codeload.github.com/civo/activetracker/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248217156,"owners_count":21066634,"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":["in-progress","ruby","ruby-on-rails"],"created_at":"2024-08-06T08:03:12.807Z","updated_at":"2025-04-10T12:41:14.132Z","avatar_url":"https://github.com/civo.png","language":"SCSS","readme":"# ActiveTracker\n\n\u003e ActiveTracker is a Ruby gem implementing an engine that you add to your Rails application to track user requests through your logs, see errors raised, query usage along with other things configured via a plugin architecture.\n\n![Overview of ActiveTracker](https://user-images.githubusercontent.com/22904/67408234-9edf6300-f5b0-11e9-8eb5-eda79642e15f.png)\n\n## Contents\n\n* [Installation](#installation)\n    * [Quickstart](##tldr---quickstart)\n* [Prerequisites](#prerequisites)\n* [Configuration](#configuration)\n* [Request plugin](#request-plugin)\n    * [Filters](#filters)\n    * [Tagging your own requests](#tagging-your-own-requests)\n    * [Redaction](#redaction)\n* [Query plugin](#query-plugin)\n    * [Filters](#filters-1)\n    * [Slow queries](#slow-queries)\n* [Exception plugin](#exception-plugin)\n    * [Filters](#filters-2)\n* [Upcoming plans](#upcoming-plans)\n* [Writing plugins](#writing-plugins)\n    * [The plugin itself](#1-the-plugin-itself)\n    * [Controller and views](#2-controllers-and-views)\n* [How can I help?](#how-can-i-help)\n* [Contributing](#contributing)\n* [Licence](#licence)\n\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'activetracker'\n```\n\nAnd then execute:\n\n    $ bundle\n\nThen run:\n\n    $ bundle exec rails activetracker:install\n\nAfter this you can run your Rails application and visit http://your-host/activetracker to see ActiveTracker's capturing of your requests, etc.\n\n### TL;DR - Quickstart\n\n```sh\necho \"gem 'activetracker'\" \u003e\u003e Gemfile\nbundle\nbundle exec rails activetracker:install\nrails s\n# Then open http://localhost:3000/activetracker and hit other pages\n# on your site\n```\n\n## Prequisites\n\nThis gem is currently only tested against Rails 5.2. If you're using it with another version of Rails and it works, please let us know. If you find a bug with it using another version of Rails, please raise it as an [issue](https://github.com/civo/activetracker/issues).\n\nActiveTracker stores its data in Redis, so you must have a Redis service available for it.\n\n## Configuration\n\nAfter you run the `rails activetracker:install` command, you will have a file called `activetracker.rb` in `config/initializers`. This file controls which ActiveTracker plugins are enabled and configuring ActiveTracker as a whole as well as each individual plugin.\n\n#### Selecting plugins\n\nConfiguring the list of plugins is as easy as listing them:\n\n```\nActiveTracker::Configuration.plugins = [\n  ActiveTracker::Plugin::Request,\n  ActiveTracker::Plugin::Exception,\n]\n```\n\nThis will enable any Rails integration necessary and add them to ActiveTracker's sidebar.\n\n#### Redis\n\nActiveTracker stores its data in Redis. We recommend a separate Redis installation for this, with a memory limit set and configured to automatically delete least recently used items - put some lines like this in your Redis configuration file:\n\n```\nmaxmemory 250mb\nmaxmemory-policy allkeys-lru\n```\n\nYou should point ActiveTracker to your Redis server within the `activetracker.rb` initializer with:\n\n```\nActiveTracker::Configuration.redis_url = \"redis://localhost:6379/15\"\n```\n\n#### Pagination\n\nIf you don't like the default number of items per page in ActiveTracker, you can change it with:\n\n```\nActiveTracker::Configuration.per_page = 50\n```\n\n#### Different mountpoint\n\nYou can choose to have ActiveTracker available anywhere if you don't like the default of `/activetracker`:\n\n```\nActiveTracker::Configuration.mountpoint = \"debugger\"\n```\n\n#### Authentication\n\nIf you want to setup authentication for your installation:\n\n```\nActiveTracker::Configuration.authentication = \"username:password\"\n\n# or using a proc:\n\nActiveTracker::Configuration.authentication do\n  false unless params[:password] == \"password\"\nend\n```\n\nThe first example uses HTTP Basic authentication and the latter will reject unauthenticated requests with 404 (so as not to give away its existence). These are executed in the context of a controller, but it doesn't descend from ApplicationController, so you can't use any authentication methods defined there (if you wish to `extend` or `include` them on `ActiveTracker::BaseController` you can though).\n\n## Request plugin\n\n\u003e The request plugin captures the log and output from every request your Rails application receives.\n\n\u003cimg width=\"400\" alt=\"Screenshot 2019-10-23 at 16 19 14\" src=\"https://user-images.githubusercontent.com/22904/67408530-0d242580-f5b1-11e9-9d64-51bf52125978.png\"\u003e \u003cimg width=\"400\" alt=\"Screenshot 2019-10-23 at 16 19 20\" src=\"https://user-images.githubusercontent.com/22904/67408531-0d242580-f5b1-11e9-8cb6-62c81a84936f.png\"\u003e\n\n\nThere is a limit of 64KB for the log, but the full output is captured for every request.\n\n#### Filters\n\nYou can filter requests from being captured by adding to the `activetracker` initializer lines like:\n\n```ruby\nActiveTracker::Plugin::Request.filters \u003c\u003c /foobar/\n# or \nActiveTracker::Plugin::Request.filters += \"/foobar\"\n# or replace them entirely with\nActiveTracker::Plugin::Request.filters = [\"/foobar\"]\n```\n\nBy default ActiveTracker itself is filtered out. If a string is supplied it must match the start of the path of the request, not just any portion. Regular expression filters are applied against the whole path.\n\n#### Tagging your own requests\n\nDuring a request cycle you can add custom tags to requests by putting lines like this in your controller, model, helper, service, etc:\n\n```\nActiveTracker::Plugin::Request.tag_current(key: value, key2: value2)\n```\n\nThese tags are then shown alongside every request, and if you click the tag you can filter the requests down to only those matching that tag(s).\n\nThe tag names `user_avatar_url`, `user_name` and `user_email` have special meaning, if you set these tags, they will be displayed alongside the request in a nice format when you view it (see \"Test McPerson\" in the right hand screenshot above).\n\n#### Redaction\n\nThe easiest way of ensuring values such as passwords in the log and output aren't leaked to ActiveTracker is to tell ActiveTracker to explicitly redact that value. For example, in your controller:\n\n```\ndef login\n  ActiveTracker::Plugin::Request.redact(Current.user.password_hash)\n  ActiveTracker::Plugin::Request.redact(params[:password])\nend\n```\n\nThese are cleared upon each request.\n\n## Query plugin\n\n\u003e The query plugin saves a count for each SQL query executed and how long it took, to enable you to find queries executed too often or queries you consider to be too slow for your application.\n\n\u003cimg width=\"400\" alt=\"Screenshot 2019-10-24 at 08 55 40\" src=\"https://user-images.githubusercontent.com/22904/67464913-1a3a2680-f63c-11e9-9ba8-e1d28fcfa54f.png\"\u003e \u003cimg width=\"400\" alt=\"Screenshot 2019-10-24 at 08 55 45\" src=\"https://user-images.githubusercontent.com/22904/67464921-1c03ea00-f63c-11e9-95dc-568d5d8754ce.png\"\u003e\n\n#### Filters\n\nYou can filter queries from being captured by adding to the `activetracker` initializer lines to search both the SQL and the ActiveRecord `name` of the query (e.g. `Order Load`) like this:\n\n```ruby\nActiveTracker::Plugin::Query.filters \u003c\u003c /secret_records/\n# or \nActiveTracker::Plugin::Query.filters += \"secret_records\"\n# or replace them entirely with\nActiveTracker::Plugin::Query.filters = [\"secret_records\"]\n```\n\nBy default ActiveTracker filters out queries containing either `SCHEMA` or an empty value. These values are searched anywhere in the SQL or `name`.\n\n#### Slow queries\n\nYou can configure a threshold of how slow a query has to be before it's highlighted with a red time value using:\n\n```ruby\nActiveTracker::Plugin::Query.min_slow_duration_ms = 25\n```\n\nBy default this is set to 100ms, but this is probably too loose for most applications.\n\n## Exception plugin\n\n\u003e The exception plugin tracks unhandled exceptions, incrementing a counter for them and keeping a backtrace to where the error occured.\n\n\u003cimg width=\"400\" alt=\"Screenshot 2019-10-24 at 09 03 53\" src=\"https://user-images.githubusercontent.com/22904/67465517-3ee2ce00-f63d-11e9-8395-104ec31f046e.png\"\u003e \u003cimg width=\"400\" alt=\"Screenshot 2019-10-24 at 09 03 59\" src=\"https://user-images.githubusercontent.com/22904/67465525-42765500-f63d-11e9-82b1-e2b904c20025.png\"\u003e\n\n#### Filters\n\nYou can filter certain exceptions from being captured by adding to the `activetracker` initializer lines to specify a class name like this:\n\n```ruby\nActiveTracker::Plugin::Query.filters \u003c\u003c ActiveRecord::RecordNotFound\n# or \nActiveTracker::Plugin::Query.filters += \"ActiveRecord::RecordNotFound\"\n# or replace them entirely with\nActiveTracker::Plugin::Query.filters = [/.*RecordNotFound/]\n```\n\nThere are no exception filters by default. Strings and classes have to be exact matches, but regular expressions match against the name normally.\n\n## Upcoming plans\n\nThe next set of plugins we're planning on writing are:\n\n#### Resque\n\nStatistics for current queue lengths and current amounts of failed jobs. Clicking the sidebar will list jobs on each queue and failed jobs - just like the Resque Web UI but all in the same place as your other ActiveTracker monitoring panes.\n\n#### Mail\n\nRecording when emails are sent from your system, along with their body for easy previewing of what was sent and when.\n\n#### Cache\n\nUsing Rails notifications track cache hits and misses for each key.\n\n#### Events\n\nWe'd like a simple system of triggering events from within your application that simply are recorded to ActiveTracker. So maybe a key, a description and a backtrace would be enough?\n\n## Writing plugins\n\nIf you would like to write a plugin, there is a minimum set of code you need to write. In our example we're going to write a \"Fake\" plugin.\n\n### 1. The plugin itself\n\nA plain Ruby object on the path (in ActiveTracker these live in `lib/active_tracker/plugin`) namespaced under `ActiveTracker::Plugin`.  The minimum methods this should implement are:\n\n```ruby\nmodule ActiveTracker\n  module Plugin\n    class Fake \u003c Base\n      def self.register\n        # Subscribe to Notifications\n        # Insert Rails middleware\n        # Hook in to Rails/gem internals\n\n        @@registered = true\n      end\n\n      def self.registered?\n        @@registered rescue false\n      end\n\n      def self.resources_name\n        # return a symbol for use in config/routes.rb like:\n        # resources resources_name\n      end\n\n      def self.nav_svg\n        # return the HTML safe source of an SVG image with a class \n        # specified of 'fill-current' and 16x16 dimensions. Don't \n        # specify the colour on the paths, they should be monochrome\n      end\n\n      def self.nav_title\n        # return a string containing the title to use in the sidebar\n      end\n\n      def self.statistics\n        # Return an array of hashes containing keys of:\n        # {plugin: self, label: \"Something\", value: 1}\n        # if the statistic is bad, add a key of `error: true`\n        # \n        # if you don't report statistics, don't implement this method\n      end\n    end\n  end\nend\n```\n\nThe hooks/notifications/middleware/etc should insert records in to the `ActiveTracker::Model` using a couple of simple methods. If you are recording EVERY occurence of this event:\n\n```ruby\n# Type of object, JSON of the data for this object, tags are a hash of\n# keys and values that can be useful for filtering. Data type is if you\n# are saving multiple objects for each Fake.\nActiveTracker::Model.save(\"Fake\", {output: output},\n  tags: {my_value: something.value},\n  data_type: \"full\",\n  expiry: 7.days,\n  log_at: Time.now\n)\n```\n\nOr if you only want to track the latest one, but count all of them, you could do it like this:\n\n```ruby\nActiveTracker::Model.find_or_create(\"Fake\", tags:tags, data_type: \"full\") do |obj|\n  if obj.persisted\n    ActiveTracker::Model.delete(obj.key)\n  end\n  obj.data ||= {}\n  obj.data[\"count\"] = (obj.data[\"count\"] || 0) + 1\n\n  obj.data[\"output\"] = output\nend\n```\n\n### 2. Controller and views\n\nYou should implement an `ActiveTracker::FakesController` that implements the normal RESTful endpoints. A barebones example of how to write this would be something like:\n\n```ruby\nmodule ActiveTracker\n  class FakesController \u003c ActiveTracker::BaseController\n    def index\n      # Track how long this request takes\n      ts = Time.current.to_f\n      # Get all matching records from the model\n      @fakes = ActiveTracker::Model.all(\"Fake\")\n      # If you're providing filtering, use a method to reduce the recordset\n      filter_fakes if params[:q].present?\n      # Finish tracking this request's duration\n      @duration = (Time.current.to_f - ts) * 1000\n      # Paginate the objects\n      @fakes, @pagination = ActiveTracker::Model.paginate(@fakes, params[:page], ActiveTracker::Configuration.per_page)\n    end\n\n    def show\n      # Find the fake from the model\n      @request = ActiveTracker::Model.find(params[:id])\n    end\n\n    private\n\n    def filter_requests\n      # Go through each record in @fakes determining whether to select/reject\n      # it or not, based on params[:q] and reset @fakes to the new set \n      # if needed\n    end\n  end\nend\n```\n\nFor styling, have a look at the Tailwind classes used in the views for other plugins and try to maintain consistent styling - unless you're willing to upgrade all other plugins 😉\n\nOnce you've written your plugin, you can add it to `integration/templates/initializer.rb` either uncommented if it will be a new default plugin, or at least to the comment block at the top for available plugins.\n\n## How can I help?\n\nThe most obvious way to help is by jumping in and raising issues, creating your own plugins (we'll list them here when people start doing that, or maybe integrate them in to this repository if people want to just donate them and they have wide appeal).\n\nHowever, if you are looking for a way to help out but don't have any ideas, we've listed a few below that we'd appreciate some help with (we'll hopefully get to all of them over time, but have our own list above that we're planning on first):\n\n#### Webpacker\n\nCurrently ActiveTracker uses the asset pipeline. We'd love it if it would work with Webpacker or the asset pipeline (and maybe it already does), whichever the containing project is using.\n\n#### Ajax loading of results\n\nWe would love to have it Ajaxified so if you stay on the page (and maybe click a button to enable the feature) it automatically puts a banner bar at the top of results saying \"New requests/queries/whatever available\" and if you click it, the new entries will be Ajax loaded in.\n\n#### Automated testing\n\nHaving no experience of automated testing a Rails engine, if anyone fancies putting in some testing for us - we'd *LOVE* that!\n\n#### Responsive\n\nWe chose [Tailwind CSS](https://tailwindcss.com) because it's lovely to work with and you can quickly/easily put together fairly nice interfaces. It's also built to be responsive, but as this is a small, spare time project for us we haven't had chance to do this yet.\n\n#### Dark mode\n\nSome of our team love dark displays, but the main author of this gem doesn't. So while we'd love to have it, it's not high enough on the priority list to justify at the moment.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/civo/activetracker.\n\n## Licence\n\nThe gem is available as open source under the terms of the [MIT Licence](https://opensource.org/licenses/MIT). The [ActiveTracker logo](doc/logo.md) was an icon downloaded from LogoFound.com and combined with the name in Helvetica Neue.\n","funding_links":[],"categories":["SCSS"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcivo%2Factivetracker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcivo%2Factivetracker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcivo%2Factivetracker/lists"}