reportsmash 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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
+