reportsmash 0.3.2

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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +34 -0
  3. data/README.rdoc +2 -0
  4. data/Rakefile +32 -0
  5. data/lib/reportsmash/control/class_methods.rb +17 -0
  6. data/lib/reportsmash/control/instance_methods.rb +70 -0
  7. data/lib/reportsmash/control.rb +11 -0
  8. data/lib/reportsmash/engine/engine.rb +118 -0
  9. data/lib/reportsmash/engine/instrumentation/external.rb +16 -0
  10. data/lib/reportsmash/engine/instrumentation/memcache/dalli.rb +39 -0
  11. data/lib/reportsmash/engine/instrumentation/memcache/mem_cache.rb +39 -0
  12. data/lib/reportsmash/engine/instrumentation/memcache/memcached.rb +48 -0
  13. data/lib/reportsmash/engine/instrumentation/memcache.rb +7 -0
  14. data/lib/reportsmash/engine/instrumentation/redis.rb +28 -0
  15. data/lib/reportsmash/engine/instrumentation/typhoeus.rb +22 -0
  16. data/lib/reportsmash/engine/instrumentation.rb +6 -0
  17. data/lib/reportsmash/engine/thread_state.rb +19 -0
  18. data/lib/reportsmash/engine/threading.rb +13 -0
  19. data/lib/reportsmash/engine/transaction.rb +116 -0
  20. data/lib/reportsmash/engine/worker.rb +56 -0
  21. data/lib/reportsmash/engine.rb +41 -0
  22. data/lib/reportsmash/rack/hooks.rb +37 -0
  23. data/lib/reportsmash/rack.rb +5 -0
  24. data/lib/reportsmash/version.rb +3 -0
  25. data/lib/reportsmash.rb +22 -0
  26. data/lib/tasks/reportsmash_tasks.rake +4 -0
  27. data/test/dummy/README.rdoc +28 -0
  28. data/test/dummy/Rakefile +6 -0
  29. data/test/dummy/app/assets/javascripts/application.js +13 -0
  30. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  31. data/test/dummy/app/controllers/application_controller.rb +5 -0
  32. data/test/dummy/app/helpers/application_helper.rb +2 -0
  33. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  34. data/test/dummy/bin/bundle +3 -0
  35. data/test/dummy/bin/rails +4 -0
  36. data/test/dummy/bin/rake +4 -0
  37. data/test/dummy/config/application.rb +23 -0
  38. data/test/dummy/config/boot.rb +5 -0
  39. data/test/dummy/config/database.yml +25 -0
  40. data/test/dummy/config/environment.rb +5 -0
  41. data/test/dummy/config/environments/development.rb +37 -0
  42. data/test/dummy/config/environments/production.rb +78 -0
  43. data/test/dummy/config/environments/test.rb +39 -0
  44. data/test/dummy/config/initializers/assets.rb +8 -0
  45. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  46. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  47. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  48. data/test/dummy/config/initializers/inflections.rb +16 -0
  49. data/test/dummy/config/initializers/mime_types.rb +4 -0
  50. data/test/dummy/config/initializers/session_store.rb +3 -0
  51. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  52. data/test/dummy/config/locales/en.yml +23 -0
  53. data/test/dummy/config/routes.rb +56 -0
  54. data/test/dummy/config/secrets.yml +22 -0
  55. data/test/dummy/config.ru +4 -0
  56. data/test/dummy/public/404.html +67 -0
  57. data/test/dummy/public/422.html +67 -0
  58. data/test/dummy/public/500.html +66 -0
  59. data/test/dummy/public/favicon.ico +0 -0
  60. data/test/reportsmash_test.rb +7 -0
  61. data/test/test_helper.rb +15 -0
  62. metadata +173 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a6e07dc3c76a8d907d9978bf102f4b8ed9565ee2
4
+ data.tar.gz: 8270ca13975c6396261d40c033fea7a4152780ca
5
+ SHA512:
6
+ metadata.gz: c35cb6fb18f52f6ea8db50a09a7edd13681bb8f843c6034ac819a325b38ff650143b2d9891087cfbf0971fe8d4b9c324f47dbc43de88eb2eebfac17e81d968cc
7
+ data.tar.gz: 8f9b37d5343c76fa8100bbfed9de6999b183c6fa7ba6f893e2800f408f9b9a8302bad599aadcebee274f109e9ba4d4a260f08cb85de58370d003dbee937cd0bb
data/LICENSE ADDED
@@ -0,0 +1,34 @@
1
+ Except for components included from external libraries
2
+ All source code of this product is
3
+ Copyright (c) 2015-2016 ReportsMash d.o.o. All rights reserved.
4
+
5
+ Subject to the terms of this notice, ReportsMash grants you a
6
+ nonexclusive, nontransferable license, without the right to
7
+ sublicense, to (a) install and execute one copy of these files on any
8
+ number of servers owned or controlled by you and (b) distribute
9
+ verbatim copies of these files to third parties. As a condition to the
10
+ foregoing grant, you must provide this notice along with each copy you
11
+ distribute and you must not remove, alter, or obscure this notice. All
12
+ other use, reproduction, modification, distribution, or other
13
+ exploitation of these files is strictly prohibited, except as may be set
14
+ forth in a separate written license agreement between you and ReportsMash.
15
+ The terms of any such license agreement will control over this notice.
16
+ The license stated above will be automatically terminated and revoked if
17
+ you exceed its scope or violate any of the terms of this notice.
18
+
19
+ This License does not grant permission to use the trade names,
20
+ trademarks, service marks, or product names of ReportsMash, except as
21
+ required for reasonable and customary use in describing the origin of
22
+ this file and reproducing the content of this notice. You may not
23
+ mark or brand this file with any trade name, trademarks, service
24
+ marks, or product names other than the original brand (if any)
25
+ provided by ReportsMash.
26
+
27
+ Unless otherwise expressly agreed by ReportsMash in a separate written
28
+ license agreement, these files are provided AS IS, WITHOUT WARRANTY OF
29
+ ANY KIND, including without any implied warranties of MERCHANTABILITY,
30
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE, or NON-INFRINGEMENT. As a
31
+ condition to your use of these files, you are solely responsible for
32
+ such use. ReportsMash will have no liability to you for direct,
33
+ indirect, consequential, incidental, special, or punitive damages or
34
+ for lost profits or data.
data/README.rdoc ADDED
@@ -0,0 +1,2 @@
1
+ = ReportsMash
2
+
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'ReportsMash'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.libs << 'test'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = false
29
+ end
30
+
31
+
32
+ task default: :test
@@ -0,0 +1,17 @@
1
+ module ReportsMash
2
+ class Control
3
+ module ClassMethods
4
+ def instance(create=true)
5
+ @instance ||= create && new
6
+ end
7
+
8
+ # clear out memoized Control and LocalEnv instances
9
+ def reset
10
+ @instance = nil
11
+ @local_env = nil
12
+ end
13
+
14
+ end
15
+ extend ClassMethods
16
+ end
17
+ end
@@ -0,0 +1,70 @@
1
+ module ReportsMash
2
+ class Control
3
+ module InstanceMethods
4
+ attr_writer :env
5
+ attr_reader :env
6
+ def init_plugin(options={})
7
+ # initialize the engine instance
8
+ ReportsMash::Engine.engine = ReportsMash::Engine::Engine.instance
9
+ ReportsMash::Engine.options = options
10
+
11
+ $stderr.puts "Initializing plugin ReportsMash - #{Process.pid}\n"
12
+ setup_rack_hooks(options[:config])
13
+ setup_database_hooks(options[:config], ReportsMash::Engine.engine)
14
+ setup_render_hooks(options[:config], ReportsMash::Engine.engine)
15
+
16
+ # if server is forking (eg, unicorn), we should not start the worker thread here
17
+ # it will be handled by Engine.transaction_start method
18
+ ReportsMash::Engine.engine.start_worker unless server_is_forking?
19
+ end
20
+
21
+ def setup_rack_hooks(config)
22
+ return if config.nil? || !config.respond_to?(:middleware)
23
+ begin
24
+ require 'reportsmash/rack/hooks'
25
+ config.middleware.use ReportsMash::Rack::Hooks
26
+ rescue => e
27
+ #::ReportsMash::Engine.logger.warn("Error installing ReportsMash Rack middleware", e)
28
+ end
29
+ end
30
+
31
+ def setup_database_hooks(config, engine)
32
+ ActiveSupport::Notifications.subscribe('sql.active_record') do |name, start, finish, id, payload|
33
+ unless payload[:name] == 'SCHEMA'
34
+ engine.save_db_transaction(id, (finish-start)*1000.0, payload[:sql], payload[:connection_id])
35
+ end
36
+ end
37
+ end
38
+
39
+ def setup_render_hooks(config, engine)
40
+ ActiveSupport::Notifications.subscribe('render_template.action_view') do |name, start, finish, id, payload|
41
+ view_name = payload[:identifier].split('app/views')[1]
42
+ engine.save_render_transaction(id, (finish-start)*1000.0, 'app/views'+view_name)
43
+ end
44
+ ActiveSupport::Notifications.subscribe('render_partial.action_view') do |name, start, finish, id, payload|
45
+ view_name = payload[:identifier].split('app/views')[1]
46
+ Rails.logger.debug "#{view_name} - #{(finish-start)*1000.0}"
47
+ engine.save_render_transaction(id, (finish-start)*1000.0, 'app/views'+view_name)
48
+ end
49
+ end
50
+
51
+ def server_is_forking?
52
+ # check for unicorn server
53
+ return true if defined?(::Unicorn) && defined?(::Unicorn::HttpServer) && object_running?(::Unicorn::HttpServer)
54
+
55
+ false
56
+ end
57
+
58
+ def object_running?(o)
59
+ ObjectSpace.each_object(o) do |exists|
60
+ return true
61
+ end
62
+ return false
63
+ end
64
+
65
+ end
66
+
67
+ include InstanceMethods
68
+ end
69
+ end
70
+
@@ -0,0 +1,11 @@
1
+ require 'reportsmash/control/instance_methods'
2
+ require 'reportsmash/control/class_methods'
3
+ require 'reportsmash/engine'
4
+ require 'reportsmash/engine/engine'
5
+
6
+ module ReportsMash
7
+ class Control
8
+ end
9
+ end
10
+
11
+
@@ -0,0 +1,118 @@
1
+ module ReportsMash
2
+ module Engine
3
+ class Engine
4
+ module ClassMethods
5
+ def instance
6
+ @instance ||= self.new
7
+ end
8
+ end
9
+ extend ClassMethods
10
+
11
+ attr_accessor :delivery_queue
12
+ attr_accessor :pid
13
+ attr_accessor :collector
14
+
15
+ def initialize
16
+ @transactions = {}
17
+ @engine_start = Time.now
18
+ @delivery_queue = []
19
+ @db_transactions = {}
20
+ @pid = Process.pid
21
+ @started = false
22
+ end
23
+
24
+ def boot_from_fork
25
+ return if @started
26
+ @started = true
27
+ @pid = Process.pid
28
+ @engine_start = Time.now
29
+
30
+ $stderr.puts "Initializing plugin ReportsMash in child #{Process.ppid} -> #{Process.pid}\n"
31
+ begin
32
+ start_worker
33
+ rescue => e
34
+ $stderr.puts "Error while starting worker #{e.inspect}"
35
+ end
36
+ end
37
+
38
+ def transaction_start(request)
39
+ if @pid != Process.pid && !@started
40
+ # we have been forked. Make sure engine worker is running in this child
41
+ ReportsMash::Engine.boot_from_fork()
42
+ end
43
+ transaction = ReportsMash::Engine::Transaction.new(@instance, request)
44
+ ReportsMash::Engine::ThreadState.get_current_state.transaction = transaction
45
+ request["X-REPORTSMASH-TRANSACTION-ID"] = transaction.tid
46
+ @transactions[transaction.tid] = transaction
47
+ end
48
+
49
+ def transaction_end(start_time, stop_time, e, response, env)
50
+ transaction = @transactions[env["X-REPORTSMASH-TRANSACTION-ID"]]
51
+ (status_code, headers, body) = response
52
+ transaction.end(start_time, stop_time, status_code, headers)
53
+ # don't allow more than 150 transactions in queue
54
+ @delivery_queue.push transaction if @delivery_queue.count < 150
55
+ env.delete("X-REPORTSMASH-TRANSACTION-ID") if env["X-REPORTSMASH-TRANSACTION-ID"]
56
+ ReportsMash::Engine::ThreadState.clear
57
+ end
58
+
59
+ def custom_attr(new_params)
60
+ trans = ReportsMash::Engine::ThreadState.get_current_state.transaction
61
+ trans.user_params.merge!(new_params) if new_params.is_a? Array
62
+ end
63
+
64
+ def start_worker
65
+ @worker_thread = ReportsMash::Engine::Threading::WorkerThread.new do
66
+ worker_loop = ReportsMash::Engine::WorkerLoop.new
67
+ worker_loop.run
68
+ end
69
+ end
70
+
71
+ def save_db_transaction(id, duration, sql, connection_id)
72
+ trans = ReportsMash::Engine::ThreadState.get_current_state.transaction
73
+ return unless trans
74
+ item = {
75
+ duration: duration,
76
+ sql: sql,
77
+ connection_id: connection_id
78
+ }
79
+ trans.db_transactions.push item
80
+ trans.db_duration_ms += duration
81
+ end
82
+
83
+ def save_render_transaction(id, duration, path)
84
+ trans = ReportsMash::Engine::ThreadState.get_current_state.transaction
85
+ return unless trans
86
+ item = {
87
+ duration: duration,
88
+ path: path
89
+ }
90
+ trans.render_transactions.push item
91
+ trans.render_duration_ms += duration
92
+ end
93
+
94
+ def save_external_http(item)
95
+ trans = ReportsMash::Engine::ThreadState.get_current_state.transaction
96
+ return unless trans
97
+ trans.external_http_requests.push item
98
+ trans.external_http_duration_ms += item[:duration]
99
+ end
100
+
101
+ def save_external_mc(op, duration)
102
+ trans = ReportsMash::Engine::ThreadState.get_current_state.transaction
103
+ return unless trans
104
+ h = {op.to_sym => duration}
105
+ trans.memcached_ops.push h
106
+ end
107
+
108
+ def save_external_redis(op, duration)
109
+ trans = ReportsMash::Engine::ThreadState.get_current_state.transaction
110
+ return unless trans
111
+ h = {op.to_sym => duration}
112
+ trans.redis_ops.push h
113
+ end
114
+
115
+ end
116
+ end
117
+ end
118
+
@@ -0,0 +1,16 @@
1
+ module ReportsMash::Engine::Instrumentation
2
+ module External
3
+ def self.record_http_trace(data)
4
+ ReportsMash::Engine.engine.save_external_http(data)
5
+ end
6
+
7
+ def self.record_mc_trace(op, duration)
8
+ ReportsMash::Engine.engine.save_external_mc(op, duration)
9
+ end
10
+
11
+ def self.record_redis_trace(op, duration)
12
+ ReportsMash::Engine.engine.save_external_redis(op, duration)
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ module ReportsMash::Engine::Instrumentation::Memcache
2
+ module Dalli
3
+
4
+ if defined?(::Dalli) && defined?(::Dalli::Client)
5
+ ::Dalli::Client.class_eval do
6
+
7
+ def reportsmash_mc_get(*args)
8
+ start_time = Time.now
9
+ rv = orig_get(*args)
10
+ ReportsMash::Engine::Instrumentation::External.record_mc_trace("get", (Time.now - start_time)*1000.0)
11
+ rv
12
+ end
13
+ alias :orig_get :get
14
+ alias :get :reportsmash_mc_get
15
+
16
+ def reportsmash_mc_set(*args)
17
+ start_time = Time.now
18
+ rv = orig_set(*args)
19
+ ReportsMash::Engine::Instrumentation::External.record_mc_trace("set", (Time.now - start_time)*1000.0)
20
+ rv
21
+ end
22
+ alias :orig_set :set
23
+ alias :set :reportsmash_mc_set
24
+
25
+
26
+ def reportsmash_mc_get_multi(*args)
27
+ start_time = Time.now
28
+ rv = orig_get_multi(*args)
29
+ ReportsMash::Engine::Instrumentation::External.record_mc_trace("get_multi", (Time.now - start_time)*1000.0)
30
+ rv
31
+ end
32
+ alias :orig_get_multi :get_multi
33
+ alias :get_multi :reportsmash_mc_get_multi
34
+
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ module ReportsMash::Engine::Instrumentation::Memcache
2
+ module MemCache
3
+
4
+ if defined?(::MemCache)
5
+ ::MemCache.class_eval do
6
+
7
+ def reportsmash_mc_get(*args)
8
+ start_time = Time.now
9
+ rv = orig_get(*args)
10
+ ReportsMash::Engine::Instrumentation::External.record_mc_trace("get", (Time.now - start_time)*1000.0)
11
+ rv
12
+ end
13
+ alias :orig_get :get
14
+ alias :get :reportsmash_mc_get
15
+
16
+ def reportsmash_mc_set(*args)
17
+ start_time = Time.now
18
+ rv = orig_set(*args)
19
+ ReportsMash::Engine::Instrumentation::External.record_mc_trace("set", (Time.now - start_time)*1000.0)
20
+ rv
21
+ end
22
+ alias :orig_set :set
23
+ alias :set :reportsmash_mc_set
24
+
25
+
26
+ def reportsmash_mc_get_multi(*args)
27
+ start_time = Time.now
28
+ rv = orig_get_multi(*args)
29
+ ReportsMash::Engine::Instrumentation::External.record_mc_trace("get_multi", (Time.now - start_time)*1000.0)
30
+ rv
31
+ end
32
+ alias :orig_get_multi :get_multi
33
+ alias :get_multi :reportsmash_mc_get_multi
34
+
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,48 @@
1
+ module ReportsMash::Engine::Instrumentation::Memcache
2
+ module Memcached
3
+
4
+ if defined?(::Memcached) && Gem::Version.new("1.8.0") <= Gem::Version.new(::Memcached::VERSION)
5
+ ::Memcached.class_eval do
6
+
7
+ def reportsmash_mc_single_get(*args)
8
+ start_time = Time.now
9
+ rv = orig_single_get(*args)
10
+ ReportsMash::Engine::Instrumentation::External.record_mc_trace("get", (Time.now - start_time)*1000.0)
11
+ rv
12
+ end
13
+ alias :orig_single_get :single_get
14
+ alias :single_get :reportsmash_mc_single_get
15
+
16
+ def reportsmash_mc_multi_get(*args)
17
+ start_time = Time.now
18
+ rv = orig_multi_get(*args)
19
+ ReportsMash::Engine::Instrumentation::External.record_mc_trace("get_multi", (Time.now - start_time)*1000.0)
20
+ rv
21
+ end
22
+ alias :orig_multi_get :multi_get
23
+ alias :multi_get :reportsmash_mc_multi_get
24
+
25
+
26
+ def reportsmash_mc_single_cas(*args)
27
+ start_time = Time.now
28
+ rv = orig_single_cas(*args)
29
+ ReportsMash::Engine::Instrumentation::External.record_mc_trace("set", (Time.now - start_time)*1000.0)
30
+ rv
31
+ end
32
+ alias :orig_single_cas :single_cas
33
+ alias :single_cas :reportsmash_mc_single_cas
34
+
35
+ def reportsmash_mc_multi_cas(*args)
36
+ start_time = Time.now
37
+ rv = orig_multi_cas(*args)
38
+ ReportsMash::Engine::Instrumentation::External.record_mc_trace("set", (Time.now - start_time)*1000.0)
39
+ rv
40
+ end
41
+ alias :orig_multi_cas :multi_cas
42
+ alias :multi_cas :reportsmash_mc_multi_cas
43
+
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,7 @@
1
+ module ReportsMash
2
+ module Engine
3
+ module Instrumentation
4
+ module Memcache
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,28 @@
1
+ module ReportsMash::Engine::Instrumentation
2
+ module Redis
3
+
4
+ if defined?(::Redis)
5
+ ::Redis.class_eval do
6
+
7
+ def reportsmash_redis_get(*args)
8
+ start_time = Time.now
9
+ rv = orig_get(*args)
10
+ ReportsMash::Engine::Instrumentation::External.record_redis_trace("get", (Time.now - start_time)*1000.0)
11
+ rv
12
+ end
13
+ alias :orig_get :get
14
+ alias :get :reportsmash_redis_get
15
+
16
+ def reportsmash_redis_set(*args)
17
+ start_time = Time.now
18
+ rv = orig_set(*args)
19
+ ReportsMash::Engine::Instrumentation::External.record_redis_trace("set", (Time.now - start_time)*1000.0)
20
+ rv
21
+ end
22
+ alias :orig_set :set
23
+ alias :set :reportsmash_redis_set
24
+
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ module ReportsMash::Engine::Instrumentation
2
+ SUPPORTED_VERSION = "0.5.3"
3
+ if defined?(::Typhoeus) && defined?(::Typhoeus::VERSION) && Gem::Version.new(SUPPORTED_VERSION) <= Gem::Version.new(::Typhoeus::VERSION)
4
+ Typhoeus.before do |request|
5
+ #start trace
6
+ start_time = Time.now
7
+ cb = Proc.new do
8
+ duration = (Time.now - start_time)*1000.0
9
+ data = {
10
+ client: "Typhoeus",
11
+ duration: duration,
12
+ response_code: request.response.response_code,
13
+ url: request.url,
14
+ method: request.original_options[:method].to_s.upcase
15
+ }
16
+ ReportsMash::Engine::Instrumentation::External.record_http_trace(data)
17
+ end
18
+ request.on_complete.unshift(cb)
19
+ true
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ module ReportsMash
2
+ module Engine
3
+ module Instrumentation
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,19 @@
1
+ module ReportsMash
2
+ module Engine
3
+ class ThreadState
4
+ attr_accessor :transaction
5
+ def initialize
6
+ @transaction = nil
7
+ end
8
+
9
+ def self.get_current_state
10
+ Thread.current[:reportsmash_state] ||= ThreadState.new
11
+ end
12
+
13
+ def self.clear
14
+ Thread.current[:reportsmash_state] = nil
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,13 @@
1
+ module ReportsMash
2
+ module Engine
3
+ module Threading
4
+
5
+ class WorkerThread < ::Thread
6
+ def initialize
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,116 @@
1
+ module ReportsMash::Engine
2
+ class Transaction
3
+ attr_accessor :user_params
4
+ attr_accessor :payload
5
+ attr_accessor :db_transactions, :db_duration_ms, :render_transactions, :render_duration_ms
6
+ attr_accessor :external_http_requests, :external_http_duration_ms
7
+ attr_accessor :memcached_ops, :redis_ops
8
+
9
+ def initialize(engine, request)
10
+ @engine = engine
11
+ @request = request
12
+ @response = nil
13
+ @tid = SecureRandom.uuid
14
+ @user_params = []
15
+ @start_time = nil
16
+ @total_duration_ms = 0
17
+ @db_duration_ms = 0
18
+ @render_duration_ms = 0
19
+ @payload = {}
20
+ @db_transactions = []
21
+ @render_transactions = []
22
+ @external_http_requests = []
23
+ @external_http_duration_ms = 0
24
+ @memcached_ops = []
25
+ @redis_ops = []
26
+ end
27
+
28
+ def tid
29
+ @tid
30
+ end
31
+
32
+ def add_user_params(new_params)
33
+ @user_params.merge!(new_params) if new_params.is_a? Hash
34
+ end
35
+
36
+ def end(start_time, stop_time, status_code, headers)
37
+ @start_time = start_time
38
+ response_content_type = (headers && headers["Content-Type"]) ? headers["Content-Type"] : ""
39
+ @total_duration_ms = (stop_time - start_time) * 1000.0
40
+ path_params = @request["action_dispatch.request.path_parameters"]
41
+ controller_action = "static_file_server"
42
+ if path_params.key?(:controller) && path_params.key?(:action)
43
+ controller_action = path_params[:controller]+"#"+path_params[:action]
44
+ end
45
+
46
+ @memcached_stats = {}
47
+ mc_duration_ms = 0
48
+ mc_counter = 0
49
+ if @memcached_ops.count > 0
50
+ %w[get set get_multi].each do |op|
51
+ stat = {count: 0, duration_ms: 0, avg_ms: 0}
52
+ @memcached_ops.select{|h| h.key? op.to_sym}.each do |trace|
53
+ stat[:count] += 1
54
+ stat[:duration_ms] += trace[op.to_sym]
55
+ stat[:avg_ms] = (stat[:duration_ms]/Float(stat[:count])).round(2)
56
+ @memcached_stats[op.to_sym] = stat
57
+ mc_duration_ms += stat[:duration_ms]
58
+ mc_counter += 1
59
+ end
60
+ end
61
+ #puts "GOT MC_OPS #{@memcached_ops.inspect}: #{@memcached_stats.inspect}"
62
+ end
63
+
64
+ @redis_stats = {}
65
+ redis_duration_ms = 0
66
+ redis_counter = 0
67
+ if @redis_ops.count > 0
68
+ %w[get set].each do |op|
69
+ stat = {count: 0, duration_ms: 0, avg_ms: 0}
70
+ @redis_ops.select{|h| h.key? op.to_sym}.each do |trace|
71
+ stat[:count] += 1
72
+ stat[:duration_ms] += trace[op.to_sym]
73
+ stat[:avg_ms] = (stat[:duration_ms]/Float(stat[:count])).round(2)
74
+ @redis_stats[op.to_sym] = stat
75
+ redis_duration_ms += stat[:duration_ms]
76
+ redis_counter += 1
77
+ end
78
+ end
79
+ #puts "GOT REDIS_OPS #{@redis_ops.inspect}: #{@redis_stats.inspect}"
80
+ end
81
+
82
+ @payload = {
83
+ :time_utc => @start_time.utc.to_i,
84
+ :user_ip => @request["REMOTE_ADDR"],
85
+ :user_agent => @request["HTTP_USER_AGENT"],
86
+ :method => @request["REQUEST_METHOD"],
87
+ :http_host => @request["HTTP_HOST"],
88
+ :request_uri => @request["REQUEST_URI"],
89
+ :request_path => @request["REQUEST_PATH"],
90
+ :controller_action => controller_action,
91
+ :response_content_type => response_content_type,
92
+ :status => status_code,
93
+ :total_duration_ms => @total_duration_ms.round(2),
94
+ :db_duration_ms => @db_duration_ms.round(2),
95
+ :render_duration_ms => @render_duration_ms.round(2),
96
+ :external_http_duration_ms => @external_http_duration_ms.round(2),
97
+ :external_memcache_duration_ms => mc_duration_ms.round(2),
98
+ :external_redis_duration_ms => redis_duration_ms.round(2),
99
+ :db_counter => @db_transactions.count,
100
+ :render_counter => @render_transactions.count,
101
+ :external_http_counter => @external_http_requests.count,
102
+ :external_memcache_counter => mc_counter,
103
+ :external_redis_counter => redis_counter,
104
+ :db_transactions => @db_transactions,
105
+ :render_transactions => @render_transactions,
106
+ :external_http_requests => @external_http_requests,
107
+ :external_memcache_requests => @memcached_stats,
108
+ :external_redis_requests => @redis_stats,
109
+
110
+ :custom_attributes => @user_params
111
+ }
112
+ end
113
+ end
114
+
115
+ end
116
+