activetracker 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +160 -0
- data/LICENSE.txt +21 -0
- data/README.md +399 -0
- data/Rakefile +21 -0
- data/THOUGHTS.md +50 -0
- data/activetracker.gemspec +49 -0
- data/app/assets/config/active_tracker_manifest.js +4 -0
- data/app/assets/images/active_tracker/.keep +0 -0
- data/app/assets/images/active_tracker/logo.svg +6 -0
- data/app/assets/images/active_tracker/reload.svg +10 -0
- data/app/assets/javascripts/active_tracker/active_tracker.js +15 -0
- data/app/assets/javascripts/active_tracker/tabs.js +12 -0
- data/app/assets/javascripts/active_tracker/tags.js +18 -0
- data/app/assets/javascripts/active_tracker/zepto.js +2 -0
- data/app/assets/stylesheets/active_tracker/active_tracker.css.scss +21 -0
- data/app/assets/stylesheets/active_tracker/tailwind.min.scss +1 -0
- data/app/controllers/active_tracker/base_controller.rb +22 -0
- data/app/controllers/active_tracker/dashboard_controller.rb +10 -0
- data/app/controllers/active_tracker/exceptions_controller.rb +41 -0
- data/app/controllers/active_tracker/queries_controller.rb +39 -0
- data/app/controllers/active_tracker/requests_controller.rb +41 -0
- data/app/helpers/active_tracker/application_helper.rb +4 -0
- data/app/helpers/active_tracker/images_helper.rb +8 -0
- data/app/helpers/active_tracker/output_helper.rb +35 -0
- data/app/helpers/active_tracker/pagination_helper.rb +16 -0
- data/app/jobs/active_tracker/application_job.rb +4 -0
- data/app/mailers/active_tracker/application_mailer.rb +6 -0
- data/app/models/active_tracker/application_record.rb +5 -0
- data/app/views/active_tracker/common/_empty.html.erb +9 -0
- data/app/views/active_tracker/common/_pagination.html.erb +22 -0
- data/app/views/active_tracker/common/_plugin_nav.html.erb +10 -0
- data/app/views/active_tracker/dashboard/index.html.erb +18 -0
- data/app/views/active_tracker/exceptions/index.html.erb +44 -0
- data/app/views/active_tracker/exceptions/show.html.erb +56 -0
- data/app/views/active_tracker/queries/index.html.erb +38 -0
- data/app/views/active_tracker/queries/show.html.erb +39 -0
- data/app/views/active_tracker/requests/_request.html.erb +30 -0
- data/app/views/active_tracker/requests/index.html.erb +26 -0
- data/app/views/active_tracker/requests/show.html.erb +81 -0
- data/app/views/layouts/active_tracker/active_tracker.html.erb +46 -0
- data/bin/console +14 -0
- data/bin/rails +25 -0
- data/bin/setup +8 -0
- data/doc/logo.md +11 -0
- data/integration/generators/installer.rb +7 -0
- data/integration/templates/initializer.rb +32 -0
- data/lib/active_tracker.rb +41 -0
- data/lib/active_tracker/configuration.rb +67 -0
- data/lib/active_tracker/engine.rb +24 -0
- data/lib/active_tracker/exception_capturer.rb +38 -0
- data/lib/active_tracker/model.rb +153 -0
- data/lib/active_tracker/output_capturer.rb +37 -0
- data/lib/active_tracker/plugin.rb +4 -0
- data/lib/active_tracker/plugin/base.rb +9 -0
- data/lib/active_tracker/plugin/exception.rb +113 -0
- data/lib/active_tracker/plugin/query.rb +128 -0
- data/lib/active_tracker/plugin/request.rb +163 -0
- data/lib/active_tracker/rails_logger.rb +120 -0
- data/lib/active_tracker/router.rb +16 -0
- data/lib/active_tracker/version.rb +3 -0
- data/lib/activetracker.rb +3 -0
- data/lib/tasks/active_tracker_tasks.rake +12 -0
- data/spec/activetracker_spec.rb +36 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/javascript/packs/application.js +15 -0
- data/spec/dummy/app/jobs/application_job.rb +7 -0
- data/spec/dummy/app/mailers/application_mailer.rb +4 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +33 -0
- data/spec/dummy/config.ru +5 -0
- data/spec/dummy/config/application.rb +37 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +54 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +62 -0
- data/spec/dummy/config/environments/production.rb +107 -0
- data/spec/dummy/config/environments/test.rb +48 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/dummy/config/initializers/assets.rb +12 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/content_security_policy.rb +28 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/disable_remote_forms.rb +1 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +33 -0
- data/spec/dummy/config/puma.rb +38 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/spring.rb +6 -0
- data/spec/dummy/config/storage.yml +34 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/lib/active_tracker/model_spec.rb +38 -0
- data/spec/spec_helper.rb +18 -0
- metadata +348 -0
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "activetracker"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/rails
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
|
3
|
+
# installed from the root of your application.
|
|
4
|
+
|
|
5
|
+
ENGINE_ROOT = File.expand_path('..', __dir__)
|
|
6
|
+
ENGINE_PATH = File.expand_path('../lib/active_tracker/engine', __dir__)
|
|
7
|
+
APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
|
|
8
|
+
|
|
9
|
+
# Set up gems listed in the Gemfile.
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
|
11
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
|
12
|
+
|
|
13
|
+
require "rails"
|
|
14
|
+
# Pick the frameworks you want:
|
|
15
|
+
require "active_model/railtie"
|
|
16
|
+
require "active_job/railtie"
|
|
17
|
+
require "active_record/railtie"
|
|
18
|
+
require "active_storage/engine"
|
|
19
|
+
require "action_controller/railtie"
|
|
20
|
+
require "action_mailer/railtie"
|
|
21
|
+
require "action_view/railtie"
|
|
22
|
+
# require "action_cable/engine"
|
|
23
|
+
require "sprockets/railtie"
|
|
24
|
+
require "rails/test_unit/railtie"
|
|
25
|
+
require 'rails/engine/commands'
|
data/bin/setup
ADDED
data/doc/logo.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Logo
|
|
2
|
+
|
|
3
|
+
The icon used as part of ActiveTracker's logo was a free/promotional order from Logofound.com (order #24181, September 27, 2019).
|
|
4
|
+
|
|
5
|
+
When used as part of referring to ActiveTracker, it should not be used in any way implying any form of endorsement.
|
|
6
|
+
|
|
7
|
+
## Original SVG
|
|
8
|
+
|
|
9
|
+
The SVG below is the one originally supplied to us, but run through [SVGOMG](https://jakearchibald.github.io/svgomg/) to optimise it
|
|
10
|
+
|
|
11
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="11in" height="816" viewBox="0 0 11000 8500" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"><g fill="#333"><path d="M7361 4662c-55 403-243 764-517 1039-330 330-786 534-1289 534-146 0-289-17-425-50l-912 1502c-142 234-447 309-681 167s-309-447-167-681l895-1474c-329-330-532-785-532-1287 0-85 6-169 17-251 209 90 429 167 647 230v21c0 320 130 610 339 819 210 210 499 339 819 339s610-130 819-339c142-142 247-320 300-519 222-3 441-17 652-45l34-5z" fill-rule="nonzero"/><path d="M4658 1194c315 0 478 330 975 330s696-330 1059-330c239 0 381 202 434 434 73 316 135 744 306 1225 111 45 216 93 312 141 479 243 769 528 726 794-46 282-454 481-1081 564-637 85-1469 47-2310-128-810-169-1443-428-1811-701-347-257-456-524-273-745 172-207 575-343 1090-400 67-255 90-511 138-750 48-234 195-434 434-434zm2866 2305c-438 81-864 96-1305 71-398-22-820-77-1245-165-417-87-692-185-1039-327-154-63-116-123-82-177 46-75 96-165 137-245 873 293 2373 625 3494 397 46 102 100 209 138 313 19 54 39 107-98 132z"/></g></svg>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Which plugins are active. The full list installed by default is:
|
|
2
|
+
#
|
|
3
|
+
# ActiveTracker::Plugin::Request # Entire request log, with response
|
|
4
|
+
# ActiveTracker::Plugin::Query # Queries performed with ms taken
|
|
5
|
+
# ActiveTracker::Plugin::Exception # When an exception is raised
|
|
6
|
+
# ActiveTracker::Plugin::Schedule # When a scheduled job is queued
|
|
7
|
+
# ActiveTracker::Plugin::ActiveJob # When a job is processed, with output
|
|
8
|
+
# ActiveTracker::Plugin::ActionMail # When an email is sent, with the body
|
|
9
|
+
# ActiveTracker::Plugin::Event # Random events triggered in the system
|
|
10
|
+
|
|
11
|
+
ActiveTracker::Configuration.plugins = [
|
|
12
|
+
ActiveTracker::Plugin::Request,
|
|
13
|
+
ActiveTracker::Plugin::Query,
|
|
14
|
+
ActiveTracker::Plugin::Exception,
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
# You should change this to be your correct Redis URL
|
|
18
|
+
ActiveTracker::Configuration.redis_url = "redis://localhost:6379/15"
|
|
19
|
+
|
|
20
|
+
# By default ActiveTracker is mounted at /activetracker
|
|
21
|
+
# if you'd like to change it, you can do so like this:
|
|
22
|
+
# ActiveTracker::Configuration.mountpoint = "debugger"
|
|
23
|
+
|
|
24
|
+
# If you want to authenticate requests to ActiveTracker with username and password
|
|
25
|
+
# ActiveTracker::Configuration.authentication = "username:password"
|
|
26
|
+
# or using a proc:
|
|
27
|
+
# ActiveTracker::Configuration.authentication do
|
|
28
|
+
# false unless params[:password] == "password"
|
|
29
|
+
# end
|
|
30
|
+
|
|
31
|
+
# If you would like a particular number of items per page
|
|
32
|
+
# ActiveTracker::Configuration.per_page = 20
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require "active_tracker/version"
|
|
2
|
+
require "redis"
|
|
3
|
+
|
|
4
|
+
module ActiveTracker
|
|
5
|
+
class Error < StandardError; end
|
|
6
|
+
|
|
7
|
+
def self.reset_connection
|
|
8
|
+
@redis = nil
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.connection
|
|
12
|
+
if @redis
|
|
13
|
+
begin
|
|
14
|
+
@redis.ping
|
|
15
|
+
rescue
|
|
16
|
+
@redis = nil
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@redis ||= Redis.new(url: ActiveTracker::Configuration.redis_url)
|
|
21
|
+
|
|
22
|
+
begin
|
|
23
|
+
@redis.ping
|
|
24
|
+
rescue
|
|
25
|
+
@redis = nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@redis
|
|
29
|
+
rescue Errno::ECONNREFUSED, Redis::CannotConnectError
|
|
30
|
+
@redis = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.connection_offline?
|
|
34
|
+
offline = connection.nil?
|
|
35
|
+
Rails.logger.error("ActiveTracker: Redis is offline/unreachable") if offline
|
|
36
|
+
offline
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
require "active_tracker/configuration"
|
|
41
|
+
require "active_tracker/engine"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module ActiveTracker
|
|
2
|
+
class Configuration
|
|
3
|
+
def self.plugins
|
|
4
|
+
@@plugins ||= [
|
|
5
|
+
ActiveTracker::Plugin::Request,
|
|
6
|
+
]
|
|
7
|
+
@@plugins
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.plugins=(items)
|
|
11
|
+
items.each do |i|
|
|
12
|
+
if i.respond_to?(:register)
|
|
13
|
+
i.register
|
|
14
|
+
else
|
|
15
|
+
raise PluginInvalidError.new("#{i.name} doesn't correctly implement the ActiveTracker API")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
@@plugins = items.dup
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.redis_url
|
|
23
|
+
@@redis_url ||= "redis://localhost:6379/15"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.redis_url=(url)
|
|
27
|
+
unless url.start_with?("redis://")
|
|
28
|
+
raise PluginInvalidError.new("redis_url isn't a valid Redis URL - should begin with redis://")
|
|
29
|
+
end
|
|
30
|
+
@@redis_url = url
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.mountpoint
|
|
34
|
+
@@mountpoint ||= "activetracker"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.root_path
|
|
38
|
+
"/#{mountpoint}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.mountpoint=(path)
|
|
42
|
+
@@mountpoint = path
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.per_page=(value)
|
|
46
|
+
@@per_page = value
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.per_page
|
|
50
|
+
@@per_page ||= 20
|
|
51
|
+
@@per_page.to_i
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.authentication(&block)
|
|
55
|
+
if block
|
|
56
|
+
@@authentication = block
|
|
57
|
+
end
|
|
58
|
+
@@authentication ||= nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.authentication=(value)
|
|
62
|
+
@@authentication = value
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class PluginInvalidError < ActiveTracker::Error ; end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module ActiveTracker
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
isolate_namespace ActiveTracker
|
|
4
|
+
config.autoload_paths += Dir["#{config.root}/lib/**/"]
|
|
5
|
+
|
|
6
|
+
initializer 'active_tracker_helper.action_controller' do
|
|
7
|
+
ActiveSupport.on_load :action_controller do
|
|
8
|
+
helper ActiveTracker::ImagesHelper
|
|
9
|
+
helper ActiveTracker::ApplicationHelper
|
|
10
|
+
helper ActiveTracker::PaginationHelper
|
|
11
|
+
helper ActiveTracker::OutputHelper
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
config.generators do |g|
|
|
16
|
+
g.test_framework :rspec, fixture: false
|
|
17
|
+
g.fixture_replacement :factory_girl, dir: 'spec/factories'
|
|
18
|
+
g.assets false
|
|
19
|
+
g.helper false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
config.assets.precompile += %w(active_tracker_manifest active_tracker/active_tracker.js active_tracker/active_tracker.css)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module ActiveTracker
|
|
2
|
+
class ExceptionCapturer
|
|
3
|
+
def initialize(app)
|
|
4
|
+
@app = app
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def call(env)
|
|
8
|
+
|
|
9
|
+
response = @app.call(env)
|
|
10
|
+
|
|
11
|
+
framework_exception = env['action_dispatch.exception']
|
|
12
|
+
if framework_exception
|
|
13
|
+
record_exception(env, framework_exception)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
response
|
|
17
|
+
rescue Exception => exception
|
|
18
|
+
|
|
19
|
+
record_exception(env, exception)
|
|
20
|
+
raise exception
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def record_exception(env, exception)
|
|
24
|
+
if env['action_dispatch.backtrace_cleaner']
|
|
25
|
+
backtrace = env['action_dispatch.backtrace_cleaner'].filter(exception.backtrace)
|
|
26
|
+
backtrace = exception.backtrace if backtrace.blank?
|
|
27
|
+
else
|
|
28
|
+
backtrace = exception.backtrace
|
|
29
|
+
end
|
|
30
|
+
class_name = exception.class.name
|
|
31
|
+
message = exception.message
|
|
32
|
+
backtrace = backtrace || []
|
|
33
|
+
|
|
34
|
+
ActiveTracker::Plugin::Exception.exception_capture(class_name, message, backtrace)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module ActiveTracker
|
|
4
|
+
class Model
|
|
5
|
+
PREFIX = "/ActiveTracker".freeze
|
|
6
|
+
|
|
7
|
+
def self.find(key)
|
|
8
|
+
return nil if ActiveTracker.connection_offline?
|
|
9
|
+
|
|
10
|
+
connection = ActiveTracker.connection
|
|
11
|
+
value = connection.get(key)
|
|
12
|
+
if value.nil?
|
|
13
|
+
raise NotFound.new("Couldn't find entry - #{key}")
|
|
14
|
+
else
|
|
15
|
+
if value.start_with?(PREFIX)
|
|
16
|
+
find(value)
|
|
17
|
+
else
|
|
18
|
+
self.new(key, value)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.delete(key)
|
|
24
|
+
ActiveTracker.connection.del(key)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.all(type, tags: {}, data_type: nil)
|
|
28
|
+
return [] if ActiveTracker.connection_offline?
|
|
29
|
+
|
|
30
|
+
keys = "#{PREFIX}/#{type}/"
|
|
31
|
+
|
|
32
|
+
keys += "*"
|
|
33
|
+
|
|
34
|
+
keys += tags.sort_by { |k,v| k.to_s }.map do |k,v|
|
|
35
|
+
"#{k}:#{CGI.escape(v.to_s)}"
|
|
36
|
+
end.join("*")
|
|
37
|
+
|
|
38
|
+
keys += "*" unless keys.end_with?("*")
|
|
39
|
+
|
|
40
|
+
if data_type
|
|
41
|
+
keys += "/#{data_type}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
connection = ActiveTracker.connection
|
|
45
|
+
result = connection.keys(keys).sort { |a,b| b <=> a }.map { |key| self.new(key, {}.to_json) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.generate_key(type, log_time, tags, data_type)
|
|
49
|
+
key = PREFIX + "/#{type}/#{log_time}"
|
|
50
|
+
converted_tags = tags.sort_by { |k,v| k.to_s }.map {|k,v| "#{k}:#{CGI.escape(v.to_s)}"}
|
|
51
|
+
if converted_tags.any?
|
|
52
|
+
key = key + "/" + converted_tags.join("/")
|
|
53
|
+
end
|
|
54
|
+
key += "/#{data_type}"
|
|
55
|
+
key.gsub!(%r{/{2,}}, '/')
|
|
56
|
+
key
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.save(type, data, tags: {}, data_type: nil, expiry: 7.days, log_at: Time.current)
|
|
60
|
+
return nil if ActiveTracker.connection_offline?
|
|
61
|
+
|
|
62
|
+
if log_at.respond_to?(:strftime)
|
|
63
|
+
log_time = log_at.strftime("%Y%m%d%H%M%S")
|
|
64
|
+
else
|
|
65
|
+
log_time = log_at
|
|
66
|
+
end
|
|
67
|
+
key = generate_key(type, log_time, tags, data_type)
|
|
68
|
+
value = data.to_json
|
|
69
|
+
connection = ActiveTracker.connection
|
|
70
|
+
connection.set(key, value)
|
|
71
|
+
connection.expire(key, expiry)
|
|
72
|
+
if tags[:id].present?
|
|
73
|
+
connection.set(tags[:id], key)
|
|
74
|
+
connection.expire(tags[:id], expiry)
|
|
75
|
+
end
|
|
76
|
+
key
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.find_or_create(type, tags: {}, data_type: nil)
|
|
80
|
+
keys = all(type, tags: tags, data_type: data_type)
|
|
81
|
+
if keys.length > 0
|
|
82
|
+
obj = find(keys.first.key) rescue nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if !obj
|
|
86
|
+
obj = new(generate_key(type, "-", tags, data_type), {}.to_json, false)
|
|
87
|
+
end
|
|
88
|
+
yield obj
|
|
89
|
+
obj.save
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.paginate(items, page, per_page)
|
|
93
|
+
page = (page || 1).to_i
|
|
94
|
+
|
|
95
|
+
total = items.length
|
|
96
|
+
start_point = (page - 1) * per_page
|
|
97
|
+
|
|
98
|
+
items = items[start_point, per_page]
|
|
99
|
+
|
|
100
|
+
total_pages = total / per_page
|
|
101
|
+
total_pages += 1 if (total % per_page != 0)
|
|
102
|
+
|
|
103
|
+
window = []
|
|
104
|
+
window << page - 2 if page > 2
|
|
105
|
+
window << page - 1 if page > 1
|
|
106
|
+
window << page
|
|
107
|
+
window << page + 1 if (total_pages - page) > 0
|
|
108
|
+
window << page + 2 if (total_pages - page) > 1
|
|
109
|
+
|
|
110
|
+
[items, {total: total, total_pages: total_pages, page: page, window: window}]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def initialize(key, value, persisted = true)
|
|
114
|
+
@attrs = {key: key, persisted: persisted}
|
|
115
|
+
key.gsub!(/^A#{PREFIX}/, "")
|
|
116
|
+
parts = key.split("/")
|
|
117
|
+
_ = parts.shift # Starts with a /
|
|
118
|
+
_ = parts.shift # then comes PREFIX
|
|
119
|
+
@attrs[:type] = parts.shift
|
|
120
|
+
@attrs[:data_type] = parts.pop
|
|
121
|
+
t = parts.shift
|
|
122
|
+
@attrs[:log_at] = Time.parse(t) rescue t
|
|
123
|
+
@attrs[:tags] = {}
|
|
124
|
+
parts.sort.each do |part|
|
|
125
|
+
tag_name, tag_value = part.split(":")
|
|
126
|
+
if tag_name == "id"
|
|
127
|
+
@attrs[:id] = tag_value
|
|
128
|
+
else
|
|
129
|
+
@attrs[:tags][tag_name.to_sym] = CGI.unescape("#{tag_value}")
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
value = JSON.parse(value)
|
|
133
|
+
self.send("data=", value)
|
|
134
|
+
value.each do |key, value|
|
|
135
|
+
self.send("#{key}=", value)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def save
|
|
140
|
+
self.class.save(@attrs[:type], data, tags: {id: id}.merge(@attrs[:tags]), data_type: @attrs[:data_type], expiry: expiry, log_at: log_at)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def method_missing(name, value = nil)
|
|
144
|
+
if name.to_s.end_with?("=")
|
|
145
|
+
@attrs[name.to_s.gsub("=", "").to_sym] = value
|
|
146
|
+
else
|
|
147
|
+
@attrs[name]
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
class NotFound < ActiveTracker::Error ; end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module ActiveTracker
|
|
2
|
+
class OutputCapturer
|
|
3
|
+
def initialize(app)
|
|
4
|
+
@app = app
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def call(env)
|
|
8
|
+
start_time = Time.current
|
|
9
|
+
status, headers, response = @app.call(env)
|
|
10
|
+
[status, headers, response]
|
|
11
|
+
ensure
|
|
12
|
+
capture(response)
|
|
13
|
+
duration = (Time.current.to_f - start_time.to_f) * 1000
|
|
14
|
+
ActiveTracker::Plugin::Request.record_duration(duration)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def capture(response)
|
|
18
|
+
body = response.body rescue nil
|
|
19
|
+
unless body.is_a?(String)
|
|
20
|
+
body = body.to_a rescue [body.body] rescue "No body given"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if body.respond_to?(:each)
|
|
24
|
+
output = []
|
|
25
|
+
body.each do |line|
|
|
26
|
+
output << line
|
|
27
|
+
end
|
|
28
|
+
output = output.join("\n")
|
|
29
|
+
else
|
|
30
|
+
output = body.to_s
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
ActiveTracker::Plugin::Request.output_capture(output)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|