tingyun_rpm 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +14 -0
  4. data/.travis.yml +4 -0
  5. data/CODE_OF_CONDUCT.md +13 -0
  6. data/Gemfile +3 -0
  7. data/Guardfile +25 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +41 -0
  10. data/cert/cacert.pem +0 -0
  11. data/lib/ting_yun/agent/agent.rb +128 -0
  12. data/lib/ting_yun/agent/class_methods.rb +21 -0
  13. data/lib/ting_yun/agent/collector/base_sampler.rb +2 -0
  14. data/lib/ting_yun/agent/collector/error_collector/error_trace_array.rb +88 -0
  15. data/lib/ting_yun/agent/collector/error_collector/noticed_error.rb +129 -0
  16. data/lib/ting_yun/agent/collector/error_collector.rb +165 -0
  17. data/lib/ting_yun/agent/collector/middle_ware_collector/cpu_sampler.rb +68 -0
  18. data/lib/ting_yun/agent/collector/middle_ware_collector/memory_sampler.rb +139 -0
  19. data/lib/ting_yun/agent/collector/middle_ware_collector/middle_ware.rb +13 -0
  20. data/lib/ting_yun/agent/collector/middle_ware_collector/sampler.rb +59 -0
  21. data/lib/ting_yun/agent/collector/middle_ware_collector.rb +80 -0
  22. data/lib/ting_yun/agent/collector/sql_sampler.rb +299 -0
  23. data/lib/ting_yun/agent/collector/stats_engine/metric_stats.rb +170 -0
  24. data/lib/ting_yun/agent/collector/stats_engine/stats_hash.rb +172 -0
  25. data/lib/ting_yun/agent/collector/stats_engine.rb +28 -0
  26. data/lib/ting_yun/agent/collector/transaction_sampler/slowest_sample_buffer.rb +25 -0
  27. data/lib/ting_yun/agent/collector/transaction_sampler/transaction_sample_buffer_base.rb +96 -0
  28. data/lib/ting_yun/agent/collector/transaction_sampler.rb +226 -0
  29. data/lib/ting_yun/agent/container_data_manager.rb +94 -0
  30. data/lib/ting_yun/agent/cross_app/cross_app_monitor.rb +131 -0
  31. data/lib/ting_yun/agent/cross_app/cross_app_tracing.rb +202 -0
  32. data/lib/ting_yun/agent/cross_app/inbound_request_monitor.rb +22 -0
  33. data/lib/ting_yun/agent/database.rb +410 -0
  34. data/lib/ting_yun/agent/datastore/metric_helper.rb +82 -0
  35. data/lib/ting_yun/agent/datastore/mongo.rb +44 -0
  36. data/lib/ting_yun/agent/datastore.rb +33 -0
  37. data/lib/ting_yun/agent/dispatcher.rb +39 -0
  38. data/lib/ting_yun/agent/event/event_listener.rb +47 -0
  39. data/lib/ting_yun/agent/event/event_loop.rb +194 -0
  40. data/lib/ting_yun/agent/instance_methods/connect.rb +164 -0
  41. data/lib/ting_yun/agent/instance_methods/container_data_manager.rb +137 -0
  42. data/lib/ting_yun/agent/instance_methods/handle_errors.rb +71 -0
  43. data/lib/ting_yun/agent/instance_methods/start.rb +219 -0
  44. data/lib/ting_yun/agent/instance_methods/start_worker_thread.rb +51 -0
  45. data/lib/ting_yun/agent/instance_methods.rb +39 -0
  46. data/lib/ting_yun/agent/method_tracer.rb +256 -0
  47. data/lib/ting_yun/agent/method_tracer_helpers.rb +85 -0
  48. data/lib/ting_yun/agent/threading/agent_thread.rb +49 -0
  49. data/lib/ting_yun/agent/transaction/attributes.rb +22 -0
  50. data/lib/ting_yun/agent/transaction/request_attributes.rb +126 -0
  51. data/lib/ting_yun/agent/transaction/trace.rb +125 -0
  52. data/lib/ting_yun/agent/transaction/trace_node.rb +110 -0
  53. data/lib/ting_yun/agent/transaction/traced_method_stack.rb +80 -0
  54. data/lib/ting_yun/agent/transaction/transaction_metrics.rb +51 -0
  55. data/lib/ting_yun/agent/transaction/transaction_sample_builder.rb +63 -0
  56. data/lib/ting_yun/agent/transaction/transaction_state.rb +112 -0
  57. data/lib/ting_yun/agent/transaction.rb +522 -0
  58. data/lib/ting_yun/agent.rb +207 -0
  59. data/lib/ting_yun/configuration/default_source.rb +638 -0
  60. data/lib/ting_yun/configuration/dotted_hash.rb +46 -0
  61. data/lib/ting_yun/configuration/environment_source.rb +116 -0
  62. data/lib/ting_yun/configuration/manager.rb +232 -0
  63. data/lib/ting_yun/configuration/manual_source.rb +14 -0
  64. data/lib/ting_yun/configuration/server_source.rb +88 -0
  65. data/lib/ting_yun/configuration/yaml_source.rb +136 -0
  66. data/lib/ting_yun/configuration.rb +9 -0
  67. data/lib/ting_yun/environment_report.rb +123 -0
  68. data/lib/ting_yun/frameworks/class_methods.rb +47 -0
  69. data/lib/ting_yun/frameworks/external.rb +15 -0
  70. data/lib/ting_yun/frameworks/instance_methods.rb +120 -0
  71. data/lib/ting_yun/frameworks/instrumentation.rb +67 -0
  72. data/lib/ting_yun/frameworks/rails.rb +63 -0
  73. data/lib/ting_yun/frameworks/rails3.rb +26 -0
  74. data/lib/ting_yun/frameworks/rails4.rb +14 -0
  75. data/lib/ting_yun/frameworks/ruby.rb +17 -0
  76. data/lib/ting_yun/frameworks/sinatra.rb +10 -0
  77. data/lib/ting_yun/frameworks.rb +34 -0
  78. data/lib/ting_yun/http/generic_request.rb +8 -0
  79. data/lib/ting_yun/http/net_http_request.rb +46 -0
  80. data/lib/ting_yun/instrumentation/active_record.rb +103 -0
  81. data/lib/ting_yun/instrumentation/middleware_proxy.rb +77 -0
  82. data/lib/ting_yun/instrumentation/middleware_tracing.rb +84 -0
  83. data/lib/ting_yun/instrumentation/mongo.rb +103 -0
  84. data/lib/ting_yun/instrumentation/mongo2.rb +37 -0
  85. data/lib/ting_yun/instrumentation/mongo_command_log_subscriber.rb +97 -0
  86. data/lib/ting_yun/instrumentation/moped.rb +95 -0
  87. data/lib/ting_yun/instrumentation/net.rb +59 -0
  88. data/lib/ting_yun/instrumentation/rack.rb +109 -0
  89. data/lib/ting_yun/instrumentation/rails3/action_controller.rb +63 -0
  90. data/lib/ting_yun/instrumentation/rails3/action_view.rb +115 -0
  91. data/lib/ting_yun/instrumentation/rails4/action_controller_subscriber.rb +124 -0
  92. data/lib/ting_yun/instrumentation/rails4/action_view_subscriber.rb +118 -0
  93. data/lib/ting_yun/instrumentation/rails4/active_record_subscriber.rb +124 -0
  94. data/lib/ting_yun/instrumentation/rails_middleware.rb +38 -0
  95. data/lib/ting_yun/instrumentation/redis.rb +70 -0
  96. data/lib/ting_yun/instrumentation/support/active_record_helper.rb +178 -0
  97. data/lib/ting_yun/instrumentation/support/controller_instrumentation.rb +54 -0
  98. data/lib/ting_yun/instrumentation/support/database.rb +38 -0
  99. data/lib/ting_yun/instrumentation/support/event_formatter.rb +19 -0
  100. data/lib/ting_yun/instrumentation/support/evented_subscriber.rb +97 -0
  101. data/lib/ting_yun/instrumentation/support/external_error.rb +52 -0
  102. data/lib/ting_yun/instrumentation/support/metric_translator.rb +84 -0
  103. data/lib/ting_yun/instrumentation/support/mongo_formatter.rb +49 -0
  104. data/lib/ting_yun/instrumentation/support/parameter_filtering.rb +21 -0
  105. data/lib/ting_yun/instrumentation/support/queue_time.rb +76 -0
  106. data/lib/ting_yun/instrumentation/support/transaction_namer.rb +68 -0
  107. data/lib/ting_yun/instrumentation/thrift.rb +329 -0
  108. data/lib/ting_yun/logger/agent_logger.rb +196 -0
  109. data/lib/ting_yun/logger/log_once.rb +38 -0
  110. data/lib/ting_yun/logger/memory_logger.rb +56 -0
  111. data/lib/ting_yun/logger/null_logger.rb +31 -0
  112. data/lib/ting_yun/logger/startup_logger.rb +13 -0
  113. data/lib/ting_yun/logger.rb +8 -0
  114. data/lib/ting_yun/metrics/metric_data.rb +86 -0
  115. data/lib/ting_yun/metrics/metric_spec.rb +89 -0
  116. data/lib/ting_yun/metrics/stats.rb +158 -0
  117. data/lib/ting_yun/metrics.rb +12 -0
  118. data/lib/ting_yun/support/coerce.rb +86 -0
  119. data/lib/ting_yun/support/collector.rb +29 -0
  120. data/lib/ting_yun/support/exception.rb +79 -0
  121. data/lib/ting_yun/support/hash_extensions.rb +25 -0
  122. data/lib/ting_yun/support/helper.rb +54 -0
  123. data/lib/ting_yun/support/hostname.rb +13 -0
  124. data/lib/ting_yun/support/http_clients/uri_util.rb +49 -0
  125. data/lib/ting_yun/support/language_support.rb +155 -0
  126. data/lib/ting_yun/support/library_detection.rb +129 -0
  127. data/lib/ting_yun/support/local_environment.rb +185 -0
  128. data/lib/ting_yun/support/path.rb +13 -0
  129. data/lib/ting_yun/support/serialize/encodes.rb +61 -0
  130. data/lib/ting_yun/support/serialize/encoding_normalizer.rb +84 -0
  131. data/lib/ting_yun/support/serialize/json_marshaller.rb +73 -0
  132. data/lib/ting_yun/support/serialize/json_wrapper.rb +78 -0
  133. data/lib/ting_yun/support/serialize/marshaller.rb +69 -0
  134. data/lib/ting_yun/support/serialize/ok_json.rb +651 -0
  135. data/lib/ting_yun/support/system_info.rb +206 -0
  136. data/lib/ting_yun/support/timer_lib.rb +29 -0
  137. data/lib/ting_yun/support/version_number.rb +70 -0
  138. data/lib/ting_yun/ting_yun_service/connection.rb +118 -0
  139. data/lib/ting_yun/ting_yun_service/http.rb +41 -0
  140. data/lib/ting_yun/ting_yun_service/request.rb +90 -0
  141. data/lib/ting_yun/ting_yun_service/ssl.rb +45 -0
  142. data/lib/ting_yun/ting_yun_service/upload_service.rb +149 -0
  143. data/lib/ting_yun/ting_yun_service.rb +124 -0
  144. data/lib/ting_yun/version.rb +17 -0
  145. data/lib/tingyun_rpm.rb +47 -0
  146. data/tingyun_rpm.gemspec +60 -0
  147. metadata +415 -0
@@ -0,0 +1,68 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ting_yun/agent'
4
+ require 'ting_yun/agent/collector/middle_ware_collector/sampler'
5
+ require 'ting_yun/support/system_info'
6
+
7
+ module TingYun
8
+ module Agent
9
+ module Collector
10
+ class CpuSampler < ::TingYun::Agent::Collector::Sampler
11
+ attr_reader :last_time
12
+
13
+ named :cpu
14
+
15
+ def initialize
16
+ @last_time = nil
17
+ @processor_count = TingYun::Support::SystemInfo.num_logical_processors
18
+ if @processor_count.nil?
19
+ TingYun::Agent.logger.warn("Failed to determine processor count, assuming 1")
20
+ @processor_count = 1
21
+ end
22
+ poll
23
+ end
24
+
25
+ def record_user_util(value)
26
+ TingYun::Agent.record_metric("CPU/NULL/UserUtilization", value)
27
+ end
28
+
29
+ def record_system_util(value)
30
+ TingYun::Agent.record_metric("CPU/System/Utilization", value)
31
+ end
32
+
33
+ def record_usertime(value)
34
+ TingYun::Agent.record_metric("CPU/NULL/UserTime", value)
35
+ end
36
+
37
+ def record_systemtime(value)
38
+ TingYun::Agent.record_metric("CPU/System Time", value)
39
+ end
40
+
41
+ def poll
42
+ now = Time.now
43
+ t = Process.times
44
+ if @last_time
45
+ elapsed = now - @last_time
46
+ return if elapsed < 1 # Causing some kind of math underflow
47
+
48
+ usertime = t.utime - @last_utime
49
+ # systemtime = t.stime - @last_stime
50
+
51
+ # record_systemtime(systemtime) if systemtime >= 0
52
+ record_usertime(usertime) if usertime >= 0
53
+
54
+ # Calculate the true utilization by taking cpu times and dividing by
55
+ # elapsed time X processor_count.
56
+
57
+ record_user_util(usertime / (elapsed * @processor_count))
58
+ # record_system_util(systemtime / (elapsed * @processor_count))
59
+ end
60
+ @last_utime = t.utime
61
+ @last_stime = t.stime
62
+ @last_time = now
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+
@@ -0,0 +1,139 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ting_yun/agent'
4
+ require 'ting_yun/agent/collector/middle_ware_collector/sampler'
5
+ require 'ting_yun/support/system_info'
6
+
7
+ module TingYun
8
+ module Agent
9
+ module Collector
10
+ class MemorySampler < ::TingYun::Agent::Collector::Sampler
11
+ named :memory
12
+
13
+ attr_accessor :sampler
14
+
15
+ def initialize
16
+ # macos, linux, solaris
17
+ if platform =~ /linux/
18
+ @sampler = ProcStatus.new
19
+ if !@sampler.can_run?
20
+ ::TingYun::Agent.logger.debug "Error attempting to use /proc/#{$$}/status file for reading memory. Using ps command instead."
21
+ @sampler = ShellPS.new("ps -o rsz")
22
+ else
23
+ ::TingYun::Agent.logger.debug "Using /proc/#{$$}/status for reading process memory."
24
+ end
25
+ elsif platform =~ /darwin9/ # 10.5
26
+ @sampler = ShellPS.new("ps -o rsz")
27
+ elsif platform =~ /darwin1\d+/ # >= 10.6
28
+ @sampler = ShellPS.new("ps -o rss")
29
+ elsif platform =~ /freebsd/
30
+ @sampler = ShellPS.new("ps -o rss")
31
+ elsif platform =~ /solaris/
32
+ @sampler = ShellPS.new("/usr/bin/ps -o rss -p")
33
+ end
34
+
35
+ raise Unsupported, "Unsupported platform for getting memory: #{platform}" if @sampler.nil?
36
+ raise Unsupported, "Unable to run #{@sampler}" unless @sampler.can_run?
37
+ end
38
+
39
+ def self.supported_on_this_platform?
40
+ platform =~ /linux|darwin|freebsd|solaris/
41
+ end
42
+
43
+ def platform
44
+ TingYun::Support::SystemInfo.ruby_os_identifier.downcase
45
+ end
46
+
47
+ def self.platform
48
+ TingYun::Support::SystemInfo.ruby_os_identifier.downcase
49
+ end
50
+
51
+ def poll
52
+ sample = @sampler.get_sample
53
+ if sample
54
+ TingYun::Agent.record_metric("Memory/NULL/PhysicalUsed", sample)
55
+ end
56
+ end
57
+
58
+ class Base
59
+ def initialize
60
+ @broken = false
61
+ end
62
+
63
+ def can_run?
64
+ return false if @broken
65
+ m = get_memory rescue nil
66
+ m && m > 0
67
+ end
68
+
69
+ def get_memory
70
+ raise 'Implement in the subclass'
71
+ end
72
+
73
+ def get_sample
74
+ return nil if @broken
75
+ begin
76
+ m = get_memory
77
+ if m.nil?
78
+ ::TingYun::Agent.logger.warn "Unable to get the resident memory for process #{$$}. Disabling memory sampler."
79
+ @broken = true
80
+ end
81
+ return m
82
+ rescue => e
83
+ ::TingYun::Agent.logger.warn "Unable to get the resident memory for process #{$$}. Disabling memory sampler.", e
84
+ @broken = true
85
+ return nil
86
+ end
87
+ end
88
+ end
89
+
90
+ class ShellPS < Base
91
+ def initialize(command)
92
+ super()
93
+ @command = command
94
+ end
95
+
96
+ # Returns the amount of resident memory this process is using in MB
97
+ #
98
+ def get_memory
99
+ process = $$
100
+ memory = `#{@command} #{process}`.split("\n")[1].to_f / 1024.0 rescue nil
101
+ # if for some reason the ps command doesn't work on the resident os,
102
+ # then don't execute it any more.
103
+ raise "Faulty command: `#{@command} #{process}`" if memory.nil? || memory <= 0
104
+ memory
105
+ end
106
+
107
+ def to_s
108
+ "shell command sampler: #{@command}"
109
+ end
110
+ end
111
+
112
+ # ProcStatus
113
+ #
114
+ # A class that samples memory by reading the file /proc/$$/status, which is specific to linux
115
+ #
116
+ class ProcStatus < Base
117
+ # Returns the amount of resident memory this process is using in MB
118
+ def get_memory
119
+ proc_status = File.open(proc_status_file, "r") {|f| f.read_nonblock(4096).strip }
120
+ if proc_status =~ /RSS:\s*(\d+) kB/i
121
+ return $1.to_f / 1024.0
122
+ end
123
+ raise "Unable to find RSS in #{proc_status_file}"
124
+ end
125
+
126
+ def proc_status_file
127
+ "/proc/#{$$}/status"
128
+ end
129
+
130
+ def to_s
131
+ "proc status file sampler: #{proc_status_file}"
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ module TingYun
4
+ module Agent
5
+ module Collector
6
+ module MiddleWare
7
+ require 'ting_yun/agent/collector/middle_ware_collector/cpu_sampler'
8
+ require 'ting_yun/agent/collector/middle_ware_collector/memory_sampler'
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ting_yun/agent'
4
+
5
+ module TingYun
6
+ module Agent
7
+ module Collector
8
+ class Sampler
9
+
10
+ class Unsupported < StandardError; end
11
+
12
+ attr_reader :id
13
+ @sampler_classes = []
14
+
15
+ def self.named(new_name)
16
+ @name = new_name
17
+ end
18
+
19
+ def self.name
20
+ @name
21
+ end
22
+
23
+ def self.inherited(subclass)
24
+ @sampler_classes << subclass
25
+ end
26
+
27
+ # Override with check. Called before instantiating.
28
+ def self.supported_on_this_platform?
29
+ true
30
+ end
31
+
32
+ def self.enabled?
33
+ if @name
34
+ config_key = "disable_#{@name}_sampler"
35
+ !(::TingYun::Agent.config[config_key])
36
+ else
37
+ true
38
+ end
39
+ end
40
+
41
+ def self.sampler_classes
42
+ @sampler_classes
43
+ end
44
+
45
+ # The ID passed in here is unused by our code, but is preserved in case
46
+ # we have clients who are defining their own subclasses of this class, and
47
+ # expecting to be able to call super with an ID.
48
+ def initialize(id=nil)
49
+ @id = id || self.class.name
50
+ end
51
+
52
+ def poll
53
+ raise "Implement in the subclass"
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ting_yun/agent'
4
+ require 'ting_yun/agent/transaction/transaction_state'
5
+ require 'ting_yun/agent/collector/middle_ware_collector/sampler'
6
+
7
+
8
+ module TingYun
9
+ module Agent
10
+ module Collector
11
+ class MiddleWareCollector
12
+
13
+ include Enumerable
14
+
15
+ def initialize(event_listener)
16
+ @samplers = []
17
+ @event_listener = event_listener
18
+ @event_listener.subscribe(:middleware_harvest) { samplers_poll }
19
+ end
20
+
21
+ def samplers_poll
22
+ @samplers.delete_if do |sampler|
23
+ begin
24
+ sampler.poll
25
+ false # it's okay. don't delete it.
26
+ rescue => e
27
+ ::TingYun::Agent.logger.warn("Removing #{sampler} from list", e)
28
+ true # remove the sampler
29
+ end
30
+ end
31
+ end
32
+
33
+ def each(&blk)
34
+ @samplers.each(&blk)
35
+ end
36
+
37
+ def clear()
38
+ @samplers.clear
39
+ end
40
+
41
+ def sampler_class_registered?(sampler_class)
42
+ self.any? { |s| s.class == sampler_class }
43
+ end
44
+
45
+ def register_sampler(sampler_class)
46
+ supported = sampler_class.supported_on_this_platform?
47
+ enabled = sampler_class.enabled?
48
+ if supported && enabled
49
+ if !sampler_class_registered?(sampler_class)
50
+ sampler = sampler_class.new
51
+
52
+ @samplers << sampler
53
+ ::TingYun::Agent.logger.debug("Registered #{sampler_class.name} for harvest time sampling.")
54
+ else
55
+ ::TingYun::Agent.logger.warn("Ignoring addition of #{sampler_class.name} because it is already registered.")
56
+ end
57
+ else
58
+ ::TingYun::Agent.logger.debug("#{sampler_class.name} not supported on this platform.")
59
+ end
60
+ rescue TingYun::Agent::Collector::Sampler::Unsupported => e
61
+ ::TingYun::Agent.logger.info("#{sampler_class.name} not available: #{e}")
62
+ rescue => e
63
+ ::TingYun::Agent.logger.error("Error registering sampler:", e)
64
+ end
65
+
66
+
67
+ # adds samplers to the sampler collection so that they run every
68
+ # minute. This is dynamically recognized by any class that
69
+ def load_samplers
70
+ TingYun::Agent::Collector::Sampler.sampler_classes.each do |subclass|
71
+ register_sampler(subclass)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+
80
+
@@ -0,0 +1,299 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ting_yun/agent/database'
4
+ require 'ting_yun/agent/transaction/transaction_state'
5
+ require 'ting_yun/support/coerce'
6
+ require 'ting_yun/metrics/stats'
7
+ require 'ting_yun/support/helper'
8
+ module TingYun
9
+ module Agent
10
+ module Collector
11
+ # This class contains the logic of recording slow SQL traces, which may
12
+ # represent multiple aggregated SQL queries.
13
+ #
14
+ # A slow SQL trace consists of a collection of SQL instrumented SQL queries
15
+ # that all normalize to the same text. For example, the following two
16
+ # queries would be aggregated together into a single slow SQL trace:
17
+ #
18
+ # SELECT * FROM table WHERE id=42
19
+ # SELECT * FROM table WHERE id=1234
20
+ #
21
+ # Each slow SQL trace keeps track of the number of times the same normalized
22
+ # query was seen, the min, max, and total time spent executing those
23
+ # queries, and an example backtrace from one of the aggregated queries.
24
+ #
25
+ class SqlSampler
26
+
27
+ MAX_SAMPLES = 10
28
+
29
+ attr_reader :sql_traces
30
+
31
+ def initialize
32
+ @sql_traces = {}
33
+ @samples_lock = Mutex.new
34
+ end
35
+
36
+ def enabled?
37
+ Agent.config[:'nbs.action_tracer.enabled'] &&
38
+ Agent.config[:'nbs.action_tracer.slow_sql'] &&
39
+ TingYun::Agent::Database.should_record_sql?('nbs.action_tracer.record_sql')
40
+ end
41
+
42
+ def on_start_transaction(state, uri=nil)
43
+ return unless enabled?
44
+
45
+ state.sql_sampler_transaction_data = TransactionSqlData.new
46
+
47
+ if Agent.config[:'nbs.action_tracer.slow_sql'] && state.sql_sampler_transaction_data
48
+ state.sql_sampler_transaction_data.set_transaction_info(uri)
49
+ end
50
+ end
51
+
52
+ # duration{:type => sec}
53
+ def notice_sql(sql, metric_name, config, duration, state=nil, explainer=nil) #THREAD_LOCAL_ACCESS sometimes
54
+ start_time = Time.now.to_f
55
+ state ||= TingYun::Agent::TransactionState.tl_get
56
+ data = state.sql_sampler_transaction_data
57
+ return unless data
58
+
59
+ if state.sql_recorded? && !metric_name.nil?
60
+ if duration*1000 > TingYun::Agent.config[:'nbs.action_tracer.slow_sql_threshold']
61
+ if duration*1000 > TingYun::Agent.config[:'nbs.action_tracer.stack_trace_threshold']
62
+ backtrace = (caller.reject! { |t| t.include?('tingyun_rpm') }).join("\n")
63
+ else
64
+ backtrace = ''
65
+ end
66
+ statement = TingYun::Agent::Database::Statement.new(sql, config, explainer)
67
+ data.sql_data << SlowSql.new(statement, metric_name, duration, start_time, backtrace)
68
+ end
69
+ end
70
+ end
71
+
72
+
73
+ def on_finishing_transaction(state, name)
74
+ return unless enabled?
75
+
76
+ transaction_sql_data = state.sql_sampler_transaction_data
77
+ return unless transaction_sql_data
78
+
79
+ transaction_sql_data.set_transaction_name(name)
80
+
81
+ save_slow_sql(transaction_sql_data)
82
+ end
83
+
84
+ def save_slow_sql(data)
85
+ if data.sql_data.size > 0
86
+ @samples_lock.synchronize do
87
+ ::TingYun::Agent.logger.debug "Examining #{data.sql_data.size} slow transaction sql statement(s)"
88
+ save data
89
+ end
90
+ end
91
+ end
92
+
93
+ def save (transaction_sql_data)
94
+ action_metric_name = transaction_sql_data.action_metric_name
95
+ uri = transaction_sql_data.uri
96
+
97
+ transaction_sql_data.sql_data.each do |sql_item|
98
+ normalized_sql = sql_item.normalize
99
+ sql_trace = @sql_traces[normalized_sql]
100
+ if sql_trace
101
+ sql_trace.aggregate(sql_item, action_metric_name, uri)
102
+ else
103
+ if has_room?
104
+ sql_trace = SqlTrace.new(normalized_sql, sql_item, action_metric_name, uri)
105
+ elsif should_add_trace?(sql_item)
106
+ remove_shortest_trace
107
+ sql_trace = SqlTrace.new(normalized_sql, sql_item, action_metric_name, uri)
108
+ end
109
+
110
+ if sql_trace
111
+ @sql_traces[normalized_sql] = sql_trace
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+
118
+ # this should always be called under the @samples_lock
119
+ def has_room?
120
+ @sql_traces.size < MAX_SAMPLES
121
+ end
122
+
123
+ # this should always be called under the @samples_lock
124
+ def should_add_trace?(sql_item)
125
+ @sql_traces.any? do |(_, existing_trace)|
126
+ existing_trace.max_call_time < sql_item.duration
127
+ end
128
+ end
129
+
130
+ # this should always be called under the @samples_lock
131
+ def remove_shortest_trace
132
+ shortest_key, _ = @sql_traces.min_by { |(_, trace)| trace.max_call_time }
133
+ @sql_traces.delete(shortest_key)
134
+ end
135
+
136
+
137
+ def harvest!
138
+ return [] unless enabled?
139
+
140
+ slowest = []
141
+ @samples_lock.synchronize do
142
+ slowest = @sql_traces.values
143
+ @sql_traces = {}
144
+ end
145
+ slowest.each {|trace| trace.prepare_to_send }
146
+ slowest
147
+ end
148
+
149
+ def reset!
150
+ @samples_lock.synchronize do
151
+ @sql_traces = {}
152
+ end
153
+ end
154
+
155
+ def merge!
156
+ @samples_lock.synchronize do
157
+ sql_traces.each do |trace|
158
+ existing_trace = @sql_traces[trace.sql]
159
+ if existing_trace
160
+ existing_trace.aggregate_trace(trace)
161
+ else
162
+ @sql_traces[trace.sql] = trace
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+
169
+ end
170
+
171
+ class TransactionSqlData
172
+ attr_reader :action_metric_name
173
+ attr_reader :uri
174
+ attr_reader :sql_data
175
+
176
+ def initialize
177
+ @sql_data = []
178
+ end
179
+
180
+ def set_transaction_info(uri)
181
+ @uri = uri
182
+ end
183
+
184
+ def set_transaction_name(name)
185
+ @action_metric_name = name
186
+ end
187
+ end
188
+
189
+ class SlowSql
190
+ attr_reader :statement
191
+ attr_reader :metric_name
192
+ attr_reader :duration
193
+ attr_reader :backtrace
194
+ attr_reader :start_time
195
+
196
+
197
+ def initialize(statement, metric_name, duration, t0, backtrace=nil)
198
+ @start_time = t0
199
+ @statement = statement
200
+ @metric_name = metric_name
201
+ @duration = duration
202
+ @backtrace = backtrace
203
+ end
204
+
205
+ def sql
206
+ statement.sql
207
+ end
208
+
209
+ def obfuscate
210
+ TingYun::Agent::Database.obfuscate_sql(statement)
211
+ end
212
+
213
+
214
+ def normalize
215
+ TingYun::Agent::Database::Obfuscator.instance.default_sql_obfuscator(statement)
216
+ end
217
+
218
+ def explain
219
+ if statement.config && statement.explainer
220
+ TingYun::Agent::Database.explain_sql(statement.sql, statement.config, statement.explainer)
221
+ end
222
+ end
223
+
224
+ # We can't serialize the explainer, so clear it before we transmit
225
+ def prepare_to_send
226
+ statement.explainer = nil
227
+ end
228
+ end
229
+
230
+
231
+
232
+ class SqlTrace < TingYun::Metrics::Stats
233
+
234
+ attr_reader :action_metric_name
235
+ attr_reader :uri
236
+ attr_reader :sql
237
+ attr_reader :slow_sql
238
+ attr_reader :params
239
+
240
+ def initialize(normalized_query, slow_sql, action_name, uri)
241
+ super()
242
+ @params = {}
243
+
244
+ @action_metric_name = action_name
245
+ @slow_sql = slow_sql
246
+ @sql = normalized_query
247
+ @uri = uri
248
+ @params[:stacktrace] = slow_sql.backtrace
249
+ record_data_point(float(slow_sql.duration))
250
+ end
251
+
252
+ def aggregate(slow_sql, action_name, uri)
253
+ if slow_sql.duration > max_call_time
254
+ @action_metric_name = action_name
255
+ @slow_sql = slow_sql
256
+ @uri = uri
257
+ @params[:stacktrace] = slow_sql.backtrace
258
+ end
259
+ record_data_point(float(slow_sql.duration))
260
+ end
261
+
262
+ def aggregate_trace(trace)
263
+ aggregate(trace.slow_sql, trace.path, trace.url)
264
+ end
265
+
266
+ def prepare_to_send
267
+ @sql = @slow_sql.sql unless need_to_obfuscate?
268
+ @params[:explainPlan] = @slow_sql.explain if need_to_explain?
269
+ end
270
+
271
+ def need_to_obfuscate?
272
+ Agent.config[:'nbs.action_tracer.record_sql'].to_s == 'obfuscated'
273
+ end
274
+
275
+ def need_to_explain?
276
+ Agent.config[:'nbs.action_tracer.explain_enabled'] && @slow_sql.duration * 1000 > TingYun::Agent.config[:'nbs.action_tracer.explain_threshold']
277
+ end
278
+
279
+
280
+ include TingYun::Support::Coerce
281
+
282
+ def to_collector_array(encoder)
283
+ [
284
+ @slow_sql.start_time,
285
+ string(@action_metric_name),
286
+ string(@slow_sql.metric_name),
287
+ string(@uri),
288
+ string(@sql),
289
+ int(@call_count),
290
+ TingYun::Helper.time_to_millis(@total_call_time),
291
+ TingYun::Helper.time_to_millis(@max_call_time),
292
+ TingYun::Helper.time_to_millis(@min_call_time),
293
+ encoder.encode(@params)
294
+ ]
295
+ end
296
+ end
297
+ end
298
+ end
299
+ end