d13n 0.5.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 (95) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.rspec +6 -0
  4. data/.rubocop.yml +5 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +22 -0
  7. data/Gemfile.lock +151 -0
  8. data/Guardfile +63 -0
  9. data/README.md +200 -0
  10. data/bin/d13n +11 -0
  11. data/d13n.gemspec +34 -0
  12. data/lib/d13n/application/class_methods.rb +56 -0
  13. data/lib/d13n/application.rb +3 -0
  14. data/lib/d13n/cli/command.rb +76 -0
  15. data/lib/d13n/cli/commands/scaffold.rb +345 -0
  16. data/lib/d13n/configuration/default_source.rb +200 -0
  17. data/lib/d13n/configuration/dotted_hash.rb +39 -0
  18. data/lib/d13n/configuration/environment_source.rb +89 -0
  19. data/lib/d13n/configuration/manager.rb +239 -0
  20. data/lib/d13n/configuration/manual_source.rb +4 -0
  21. data/lib/d13n/configuration/mask_defaults.rb +6 -0
  22. data/lib/d13n/configuration/server_source.rb +83 -0
  23. data/lib/d13n/configuration/yaml_source.rb +66 -0
  24. data/lib/d13n/configuration.rb +6 -0
  25. data/lib/d13n/ext/string.rb +17 -0
  26. data/lib/d13n/logger/log_once.rb +24 -0
  27. data/lib/d13n/logger/memory_logger.rb +48 -0
  28. data/lib/d13n/logger/null_logger.rb +16 -0
  29. data/lib/d13n/logger.rb +213 -0
  30. data/lib/d13n/metric/conductor.rb +123 -0
  31. data/lib/d13n/metric/helper.rb +62 -0
  32. data/lib/d13n/metric/http_clients/http_helper.rb +15 -0
  33. data/lib/d13n/metric/http_clients/net_http_wrappers.rb +54 -0
  34. data/lib/d13n/metric/http_clients.rb +4 -0
  35. data/lib/d13n/metric/instrumentation/app_exception.rb +70 -0
  36. data/lib/d13n/metric/instrumentation/controller_instrumentation.rb +91 -0
  37. data/lib/d13n/metric/instrumentation/em-websocket.rb +71 -0
  38. data/lib/d13n/metric/instrumentation/exception.rb +65 -0
  39. data/lib/d13n/metric/instrumentation/middleware_tracing.rb +82 -0
  40. data/lib/d13n/metric/instrumentation/net.rb +36 -0
  41. data/lib/d13n/metric/instrumentation/sinatra/stream_namer.rb +35 -0
  42. data/lib/d13n/metric/instrumentation/sinatra.rb +165 -0
  43. data/lib/d13n/metric/instrumentation/websocket_instrumentation.rb +42 -0
  44. data/lib/d13n/metric/instrumentation.rb +41 -0
  45. data/lib/d13n/metric/manager.rb +106 -0
  46. data/lib/d13n/metric/metrics/app_database_metric.rb +4 -0
  47. data/lib/d13n/metric/metrics/app_http_metric.rb +229 -0
  48. data/lib/d13n/metric/metrics/app_state_metric.rb +103 -0
  49. data/lib/d13n/metric/metrics/base.rb +14 -0
  50. data/lib/d13n/metric/metrics/biz_state_metric.rb +4 -0
  51. data/lib/d13n/metric/metrics.rb +6 -0
  52. data/lib/d13n/metric/stream/span_tracer_helpers.rb +72 -0
  53. data/lib/d13n/metric/stream/stream_tracer_helpers.rb +141 -0
  54. data/lib/d13n/metric/stream/traced_span_stack.rb +73 -0
  55. data/lib/d13n/metric/stream.rb +322 -0
  56. data/lib/d13n/metric/stream_state.rb +68 -0
  57. data/lib/d13n/metric.rb +11 -0
  58. data/lib/d13n/rack/d13n_middleware.rb +21 -0
  59. data/lib/d13n/rack/metric_middleware.rb +18 -0
  60. data/lib/d13n/service/background_job/sinatra.rb +24 -0
  61. data/lib/d13n/service/background_job.rb +1 -0
  62. data/lib/d13n/service/start.rb +75 -0
  63. data/lib/d13n/service.rb +91 -0
  64. data/lib/d13n/support/request_id.rb +29 -0
  65. data/lib/d13n/version.rb +14 -0
  66. data/lib/d13n.rb +92 -0
  67. data/templates/.rspec.template +6 -0
  68. data/templates/.ruby-version.template +1 -0
  69. data/templates/Gemfile.template +16 -0
  70. data/templates/Guardfile.template +64 -0
  71. data/templates/Jenkinsfile.template +85 -0
  72. data/templates/Makefile.template +178 -0
  73. data/templates/README.md.template +1 -0
  74. data/templates/Rakefile.template +6 -0
  75. data/templates/application.yml.template +14 -0
  76. data/templates/config.ru.template +4 -0
  77. data/templates/docker/.dockerignore.template +5 -0
  78. data/templates/docker/Dockerfile.application.development +15 -0
  79. data/templates/docker/Dockerfile.cache.development +18 -0
  80. data/templates/docker/Dockerfile.development +27 -0
  81. data/templates/docker/Dockerfile.release +16 -0
  82. data/templates/docker/docker-compose.yml.development +53 -0
  83. data/templates/docker/docker-compose.yml.release +37 -0
  84. data/templates/lib/api/service.rb.template +10 -0
  85. data/templates/lib/api/support.rb.template +38 -0
  86. data/templates/lib/api/version.rb.template +3 -0
  87. data/templates/lib/api.rb.template +4 -0
  88. data/templates/lib/application.rb.template +49 -0
  89. data/templates/lib/service.rb.template +4 -0
  90. data/templates/lib/version.rb.template +3 -0
  91. data/templates/scripts/test.sh.template +7 -0
  92. data/templates/spec/spec_helper.rb.template +56 -0
  93. data/templates/tasks/migration.rake.template +11 -0
  94. data/templates/tasks/spec.rake.template +21 -0
  95. metadata +199 -0
@@ -0,0 +1,229 @@
1
+ require 'd13n/ext/string'
2
+ module D13n::Metric
3
+ class HTTPMetricError < MetricError; end
4
+ class ServiceNotFound < HTTPMetricError; end
5
+ class EndpointNotFound < HTTPMetricError; end
6
+ class AppHttpMetric < Base
7
+
8
+ module Namespace
9
+ def get_service_endpoint(request)
10
+ service = Helper.service_for(request.uri)
11
+ endpoint = Helper.endpoint_for(request.uri)
12
+ raise ServiceNotFound.new "No matched service for #{request.uri.to_s}" if service.nil?
13
+ raise EndpointNotFound.new "No matched endpoint for #{request.uri.to_s}" if endpoint.nil?
14
+ [service, endpoint]
15
+ end
16
+
17
+ def service_metric(request,type)
18
+ service, endpoint = get_service_endpoint(request)
19
+ items = []
20
+ items << prefix
21
+ items << 'service'
22
+ items << service
23
+ items << type.to_s
24
+ items.join('.')
25
+ end
26
+
27
+ def endpoint_metric(request, type)
28
+ service, endpoint = get_service_endpoint(request)
29
+ items = []
30
+ items << prefix
31
+ items << 'endpoint'
32
+ items << service
33
+ items << endpoint
34
+ items << type.to_s
35
+ items.join('.')
36
+ end
37
+
38
+ def http_basic_tags(request)
39
+ service, endpoint = get_service_endpoint(request)
40
+ tags = []
41
+ tags << "idc:#{D13n.idc_name}"
42
+ tags << "env:#{D13n.idc_env}"
43
+ tags << "app:#{D13n.app_name}"
44
+ tags << "srv:#{service}"
45
+ tags << "endpoint:#{endpoint}"
46
+ tags
47
+ end
48
+
49
+ def http_status_tags(request, response)
50
+ tags = http_basic_tags(request)
51
+ tags << "status:#{response.code}"
52
+ tags
53
+ end
54
+
55
+ def http_error_tags(response)
56
+ tags = http_basic_tags
57
+ tags << "error:#{response.class.name.underscore}"
58
+ tags
59
+ end
60
+
61
+ def endpoint_status_metric(request,response)
62
+ "#{service_metric(request,:statue)}.#{response.code}"
63
+ end
64
+
65
+ def service_error_metric(request,response)
66
+ "#{service_metric(request,:error)}.#{response.class.name.underscore}"
67
+ end
68
+ end
69
+
70
+ module In
71
+ include Namespace
72
+ def process(*args, &block)
73
+ return yield unless Helper.http_in_tracable?
74
+
75
+ state = D13n::Metric::StreamState.st_get
76
+ state.request = metric_request(args)
77
+
78
+ trace_options = args.last.is_a?(Hash) ? args.last : {}
79
+ category = trace_options[:category] || :action
80
+ stream_options = create_stream_options(trace_options, category, state)
81
+
82
+ t0 = Time.now
83
+ begin
84
+ node = start(state, t0, request)
85
+ response = yield
86
+ ensure
87
+ finish(state, t0, node, request, response)
88
+ end
89
+ return response
90
+ end
91
+
92
+ def start(state, t0, request)
93
+ 'start'
94
+ end
95
+
96
+ def finish(state, t0, node, request, response)
97
+ 'finish'
98
+ end
99
+ end
100
+
101
+ module Out
102
+ include Namespace
103
+
104
+ def process(request, collectable=true)
105
+ state = D13n::Metric::StreamState.st_get
106
+
107
+ return yield unless collectable && Helper.http_out_tracable?
108
+
109
+ t0 = Time.now
110
+ begin
111
+ node = start(state, t0, request)
112
+ response = yield
113
+ rescue ServiceNotFound => err
114
+ D13n.logger.error err.message
115
+ rescue Exception => err
116
+ D13n.logger.debug 'Unexpected exception raise while processing HTTP request metric', err
117
+ ensure
118
+ finish(state, t0, node, request, response || err)
119
+ end
120
+ return response
121
+ end
122
+
123
+ def start(state, t0, request)
124
+ inject_request_headers(state, request)
125
+ stack = state.traced_span_stack
126
+ node = stack.push_frame(state, prefix , t0)
127
+ return node
128
+ rescue => e
129
+ D13n.logger.error 'Uncaught exception while start processing HTTP request metric', e
130
+ return nil
131
+ end
132
+
133
+ def inject_request_headers(state, request)
134
+ stream = state.current_stream
135
+
136
+ state.is_cross_app_caller = true
137
+ if stream
138
+ request[StreamState::D13N_STREAM_HEADER] = state.referring_stream_id || stream.uuid
139
+ end
140
+ request[StreamState::D13N_APP_HEADER] = D13n.app_name
141
+ end
142
+
143
+ def finish(state, t0, node, request, response)
144
+ unless t0
145
+ D13n.logger.error("HTTP request process finished metric without start time. This is probably a bug.")
146
+ return
147
+ end
148
+
149
+ unless request
150
+ D13n.logger.error("HTTP request process finished metric without request. This is probably a bug.")
151
+ end
152
+
153
+ t1 = Time.now
154
+ duration = t1.to_f - t0.to_f
155
+
156
+ begin
157
+ scoped_metric = get_service_endpoint(request).join('.')
158
+ node.name = scoped_metric if node
159
+ if response
160
+ collect_error_count(request, response) if response.is_a? Exception
161
+ if response.is_a? Net::HTTPResponse
162
+ collect_status_count(request, response)
163
+ collect_request_timing(request, duration)
164
+ end
165
+ else
166
+ collect_request_count(request)
167
+ end
168
+ ensure
169
+ if node
170
+ stack = state.traced_span_stack
171
+ stack.pop_frame(state, node, scoped_metric, t1)
172
+ end
173
+ end
174
+ rescue ServiceNotFound, EndpointNotFound => e
175
+ D13n.logger.debug "service or endpoind not found while collect HTTP metric:", e.message
176
+ rescue HTTPMetricError => e
177
+ D13n.logger.debug "while collect HTTP metric", e.message
178
+ rescue => e
179
+ D13n.logger.error "Uncaught exception while finishing an HTTP request metric", e
180
+ end
181
+
182
+ def collect_request_count(request, count=1, rate=1.0)
183
+ @collector.increment(metric_name('count'), count, sample_rate: rate, tags: http_basic_tags(request))
184
+ end
185
+
186
+ def collect_status_count(request, response, count=1, rate=1.0)
187
+ @collector.increment(metric_name('count'), count, sample_rate: rate, tags: http_status_tags(request, response))
188
+ end
189
+
190
+ def collect_error_count(request, response, count=1, rate=1.0)
191
+ @collector.increment(metric_name('count'), count, sample_rate: rate, tags: http_error_tags(request, response))
192
+ end
193
+
194
+ def collect_request_timing(request, timing, rate=1.0)
195
+ @collector.measure(metric_name('timing'), timing, sample_rate: rate, tags: http_basic_tags(request))
196
+ end
197
+ end
198
+
199
+ def self.instance(collector, opts)
200
+ direction = opts[:direction]
201
+ check_direction(direction)
202
+ @instance = new(collector, opts)
203
+ @instance.extend PROCESSOR[direction]
204
+ end
205
+
206
+ PROCESSOR = {
207
+ 'in' => In,
208
+ 'out' => Out
209
+ }
210
+
211
+ def initialize(collector, opts)
212
+ @collector = collector
213
+ @opts = opts
214
+ @direction = @opts[:direction]
215
+ end
216
+
217
+ def prefix
218
+ "app.http.#{@direction[0]}"
219
+ end
220
+
221
+ def metric_name(type)
222
+ "#{prefix}.#{type}"
223
+ end
224
+
225
+ def self.check_direction(direction)
226
+ raise InstrumentNameError.new "Wrong request direction #{direction}!" unless PROCESSOR.keys.include?(direction)
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,103 @@
1
+ module D13n::Metric
2
+ class AppStateMetric < Base
3
+ module Namespace
4
+ def gem_metric_tags(gem_info)
5
+ tags = []
6
+ tags << "idc:#{Helper.idc_name}"
7
+ tags << "env:#{Helper.idc_env}"
8
+ tags << "app:#{Helper.app_name}"
9
+ tags << "gem:#{gem_info[0]}"
10
+ tags << "version:#{gem_info[1]}"
11
+ tags
12
+ end
13
+
14
+ def exception_metric_tags(exception, opts)
15
+ tags = []
16
+ tags << "idc:#{Helper.idc_name}"
17
+ tags << "env:#{Helper.idc_env}"
18
+ tags << "app:#{Helper.app_name}"
19
+ tags << "name:#{exception.class.name}"
20
+ tags << "at:#{opts.fetch(:at, 'runtime')}"
21
+ tags << "src:#{opts.fetch(:src, 'app')}"
22
+ tags << "uuid:#{opts.fetch(:uuid, 'unknown')}"
23
+ tags << "stream_id:#{opts[:referring_stream_id] || opts.fetch(:uuid, 'unknown')}"
24
+ tags
25
+ end
26
+ end
27
+
28
+ module GemProcessor
29
+ include Namespace
30
+
31
+ def process
32
+ load_gem_spec.each do |g|
33
+ collect_gem_gauge(g)
34
+ end
35
+ end
36
+
37
+ def load_gem_spec
38
+ Gem.loaded_specs.inject({}) {
39
+ |m, (n,s)| m.merge(n => s.version)
40
+ }
41
+ end
42
+
43
+ def collect_gem_gauge(named_gem,gauge=1, rate=1.0)
44
+ @collector.gauge(metric_name('gauge'), gauge, sample_rate:rate, tags: gem_metric_tags(named_gem))
45
+ end
46
+ end
47
+
48
+ module ExceptionProcessor
49
+ include Namespace
50
+
51
+ def process
52
+ return yield unless Helper.exception_tracable?
53
+
54
+ begin
55
+ exception = yield
56
+ ensure
57
+ finish(exception)
58
+ end
59
+ return exception
60
+ end
61
+
62
+ def finish(exception)
63
+ metric_data = D13n::Metric::StreamState.default_metric_data
64
+ @opts.merge!(metric_data)
65
+ collect_exception_count(exception, @opts)
66
+ end
67
+
68
+ def collect_exception_count(exception, opts, count=1, rate=1.0)
69
+ @collector.increment(metric_name('count'), count, sample_rate: rate, tags: exception_metric_tags(exception,opts))
70
+ end
71
+ end
72
+
73
+ PROCESSOR = {
74
+ 'gem' => GemProcessor,
75
+ 'exception' => ExceptionProcessor
76
+ }
77
+
78
+ def self.check_type(type)
79
+ raise InstrumentNameError.new "Wrong request process type #{type}!" unless PROCESSOR.has_key?(type)
80
+ end
81
+
82
+ def self.instance(collector, opts)
83
+ type = opts[:type]
84
+ check_type(type)
85
+ @instance = new(collector, opts)
86
+ @instance.extend PROCESSOR[type]
87
+ end
88
+
89
+ def initialize(collector, opts)
90
+ @collector = collector
91
+ @opts = opts
92
+ @type = @opts[:type]
93
+ end
94
+
95
+ def metric_name(type)
96
+ "#{prefix}.#{type}"
97
+ end
98
+
99
+ def prefix
100
+ "app.state.#{@type}"
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,14 @@
1
+ module D13n::Metric
2
+ class InstrumentNameError < MetricError; end
3
+ class Base
4
+ attr_reader :prefix
5
+
6
+ def prefix
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def process(&block)
11
+ raise NotImplementedError
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ module D13n::Metric
2
+ class BizStateMetric < Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ require 'd13n/metric/helper'
2
+ require 'd13n/metric/metrics/base'
3
+ require 'd13n/metric/metrics/app_database_metric'
4
+ require 'd13n/metric/metrics/app_http_metric'
5
+ require 'd13n/metric/metrics/app_state_metric'
6
+ require 'd13n/metric/metrics/biz_state_metric'
@@ -0,0 +1,72 @@
1
+ require 'd13n/metric/stream/stream_tracer_helpers'
2
+ module D13n::Metric
3
+ class Stream
4
+ module SpanTracerHelpers
5
+ MAX_ALLOWED_METRIC_DURATION = 1_000_000_000
6
+ include StreamTracerHelpers::Namer
7
+
8
+
9
+ extend self
10
+
11
+ def collect_span_duration_timing(collector, state, name, timing, metric_data, options, rate=1.0)
12
+ collector.measure(metric_name("timing"), timing, sample_rate: rate, tags: stream_duration_tags(metric_data))
13
+ end
14
+
15
+ def collect_span_exclusive_timing(collector, state, name, timing, metric_data, options, rate=1.0)
16
+ collector.measure(metric_name("timing"), timing, sample_rate: rate, tags: stream_exclusive_tags(metric_data))
17
+ end
18
+
19
+ def collect_span_request_count(collector, state, name, metric_data, options, count = 1, rate = 1.0)
20
+ collector.increment(metric_name("count"), count, sample_rate: rate, tags: stream_request_tags(metric_data))
21
+ end
22
+
23
+ def collect_span_metrics(state, first_name, duration, exclusive, metric_data, options)
24
+ collector = D13n::Metric::Manager.instance
25
+ collect_span_duration_timing(collector, state, first_name, duration, metric_data, options)
26
+ collect_span_exclusive_timing(collector, state, first_name, exclusive, metric_data, options)
27
+ collect_span_request_count(collector, state, first_name, metric_data, options)
28
+ end
29
+
30
+ def trace_header(state, t0)
31
+ stack = state.traced_span_stack
32
+ stack.push_frame(state, :span_tracer, t0)
33
+ end
34
+
35
+ def trace_footer(state, t0, first_name, expected_frame, options, t1=Time.now.to_f)
36
+ if expected_frame
37
+ stack = state.traced_span_stack
38
+ frame = stack.pop_frame(state, expected_frame, first_name, t1)
39
+ duration, exclusive = get_timings(t0, t1, frame)
40
+ metric_data = {:exclusive => exclusive}
41
+ get_metric_data(state, t0, t1, metric_data)
42
+
43
+ if duration < MAX_ALLOWED_METRIC_DURATION
44
+ if duration < 0
45
+ D13n.logger.warn("metric_duration_negative:#{first_name} Metric #{first_name} has negative duration: #{duration} s")
46
+ end
47
+
48
+ if exclusive < 0
49
+ D13n.logger.warn("metric_exclusive_negative: #{first_name} Metric #{first_name} has negative exclusive time: duration = #{duration} s, child_time = #{frame.children_time}")
50
+ end
51
+
52
+ collect_span_metrics(state, first_name, duration, exclusive, metric_data, options)
53
+ else
54
+ D13n.logger.warn("too_huge_metric:#{first_name}, Ignoring metric #{first_name} with unacceptably large duration: #{duration} s")
55
+ end
56
+
57
+ end
58
+ end
59
+
60
+ def get_timings(t0, t1, frame)
61
+ duration = t1 - t0
62
+ exclusive = duration - frame.children_time
63
+ [duration, exclusive]
64
+ end
65
+
66
+ def get_metric_data(state, t0, t1, metric_data)
67
+ stream = state.current_stream
68
+ stream.generate_default_metric_data(state, t0, t1, metric_data)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,141 @@
1
+ module D13n::Metric
2
+ class Stream
3
+ module StreamTracerHelpers
4
+ module Namer
5
+ def prefix
6
+ "app.http.i"
7
+ end
8
+
9
+ def metric_name(type)
10
+ "#{prefix}.#{type}"
11
+ end
12
+
13
+ def stream_basic_tags(metric_data)
14
+ tags = []
15
+ tags << "idc:#{D13n.idc_name}"
16
+ tags << "env:#{D13n.idc_env}"
17
+ tags << "app:#{D13n.app_name}"
18
+ tags << "name:#{metric_data[:name]}"
19
+ tags << "uuid:#{metric_data[:uuid]}"
20
+ tags << "stream_id:#{metric_data[:referring_stream_id] || metric_data[:uuid]}"
21
+ tags << "type:#{metric_data[:referring_stream_id].nil? ? 'stream' : 'span'}"
22
+ tags
23
+ end
24
+
25
+ def stream_duration_tags(metric_data)
26
+ tags = stream_basic_tags(metric_data)
27
+ tags << "time:duration"
28
+ tags
29
+ end
30
+
31
+ def stream_exclusive_tags(metric_data)
32
+ tags = stream_basic_tags(metric_data)
33
+ tags << "time:exclusive"
34
+ tags
35
+ end
36
+
37
+ def stream_request_tags(metric_data)
38
+ tags = stream_basic_tags(metric_data)
39
+ tags
40
+ end
41
+
42
+ def stream_http_response_code_tags(metric_data)
43
+ tags = stream_basic_tags(metric_data)
44
+ tags << "response:code"
45
+ tags << "code:#{metric_data[:http_response_code]}"
46
+ tags
47
+ end
48
+
49
+ def stream_http_response_content_type_tags(metric_data)
50
+ tags = stream_basic_tags(metric_data)
51
+ tags << "response:type"
52
+ tags << "type:#{metric_data[:http_response_content_type]}"
53
+ tags
54
+ end
55
+
56
+ def stream_http_request_content_length_tags(metric_data)
57
+ tags = stream_basic_tags(metric_data)
58
+ tags << "request:length"
59
+ tags
60
+ end
61
+
62
+ def stream_http_response_content_length_tags(metric_data)
63
+ tags = stream_basic_tags(metric_data)
64
+ tags << "response:length"
65
+ tags
66
+ end
67
+
68
+ def stream_error_tags(metric_data, error)
69
+ tags = stream_basic_tags(metric_data)
70
+ tags << "error:#{error.is_a?(Class) ? error.name : error.class.name}"
71
+ tags
72
+ end
73
+
74
+ def stream_apdex_tags(metric_data)
75
+ tags = stream_basic_tags(metric_data)
76
+ tags << "apdex_zone:#{metric_data[:apdex_perf_zone]}"
77
+ tags
78
+ end
79
+ end
80
+
81
+ include Namer
82
+ extend self
83
+
84
+ def collect_duration_metric(collector, state, timing, metric_data, rate=1.0)
85
+ collector.measure(metric_name("timing"), timing, sample_rate: rate, tags: stream_duration_tags(metric_data))
86
+ end
87
+
88
+ def collect_exclusive_metric(collector, state, timing, metric_data, rate=1.0)
89
+ collector.measure(metric_name("timing"), timing, sample_rate: rate, tags: stream_exclusive_tags(metric_data))
90
+ end
91
+
92
+ def collect_apdex_metric(collector, state, metric_data, count=1,rate=1.0)
93
+ collector.increment(metric_name("count"), count, sample_rate: rate, tags: stream_apdex_tags(metric_data))
94
+ end
95
+
96
+ def collect_repsonse_code_metric(collector, state, metric_data, count=1, rate=1.0)
97
+ collector.increment(metric_name("count"), count, sample_rate: rate, tags: stream_http_response_code_tags(metric_data))
98
+ end
99
+
100
+ def collect_response_content_type_metric(collector, state, metric_data, count=1, rate=1.0)
101
+ collector.increment(metric_name("count"), count, sample_rate: rate, tags: stream_http_response_content_type_tags(metric_data))
102
+ end
103
+
104
+ def collect_response_content_length_metric(collector, state, gauge, metric_data, rate=1.0)
105
+ collector.gauge(metric_name("gauge"), gauge.to_i, sample_rate: rate, tags: stream_http_response_content_length_tags(metric_data))
106
+ end
107
+
108
+ def collect_request_content_length_metric(collector, state, gauge, metric_data, rate=1.0)
109
+ collector.gauge(metric_name("gauge"), gauge.to_i, sample_rate: rate, tags: stream_http_request_content_length_tags(metric_data))
110
+ end
111
+
112
+ def collect_response_metric(collector, state, metric_data)
113
+ collect_repsonse_code_metric(collector, state, metric_data)
114
+ collect_response_content_type_metric(collector, state, metric_data)
115
+ collect_response_content_length_metric(collector, state, metric_data[:http_response_content_length], metric_data)
116
+ collect_request_content_length_metric(collector, state, metric_data[:http_request_content_length], metric_data) unless metric_data[:http_request_content_length].to_i == 0
117
+ end
118
+
119
+ def collect_error_metric(collector, state, error, metric_data, count=1, rate=1.0 )
120
+ collector.increment(metric_name('count'), count, sample_rate: rate, tags: stream_error_tags(metric_data, error))
121
+ end
122
+
123
+ def collect_errors_metric(collector, state, metric_data)
124
+ errors = metric_data[:errors]
125
+
126
+ errors.each do |error|
127
+ collect_error_metric(collector, state, error, metric_data)
128
+ end
129
+ end
130
+
131
+ def collect_metrics(state, metric_data)
132
+ collector = D13n::Metric::Manager.instance
133
+ collect_duration_metric(collector, state, metric_data[:duration], metric_data)
134
+ collect_exclusive_metric(collector, state, metric_data[:exclusive], metric_data)
135
+ collect_apdex_metric(collector, state, metric_data)
136
+ collect_response_metric(collector, state, metric_data)
137
+ collect_errors_metric(collector, state, metric_data) if metric_data[:error]
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,73 @@
1
+ module D13n::Metric
2
+ class Stream
3
+ class UnexpectedStackError < D13n::Error;end
4
+ class StackFrame
5
+ attr_reader :tag
6
+ attr_accessor :name, :start_time, :children_time, :end_time
7
+ def initialize(tag, start_time)
8
+ @tag = tag
9
+ @start_time = start_time
10
+ @children_time = 0
11
+ end
12
+ end
13
+
14
+ class TracedSpanStack
15
+ def initialize
16
+ @stack = []
17
+ end
18
+
19
+ def push_frame(state, tag, time = Time.now.to_f)
20
+ frame = StackFrame.new(tag, time)
21
+ @stack.push frame
22
+ frame
23
+ end
24
+
25
+ def pop_frame(state, expected_frame, name, time, deduct_call_time_from_parent=true)
26
+ frame = fetch_matching_frame(expected_frame)
27
+ frame.end_time = time
28
+ frame.name = name
29
+
30
+ note_children_time(frame, time, deduct_call_time_from_parent)
31
+
32
+ frame
33
+ end
34
+
35
+ def fetch_matching_frame(expected_frame)
36
+ while frame = @stack.pop
37
+ if frame == expected_frame
38
+ return frame
39
+ else
40
+ D13n.logger.info("Unexpected frame in traced method stack: #{frame.inspect} expected to be #{expected_frame.inspect}")
41
+ end
42
+ end
43
+ raise UnexpectedStackError.new "Frame not found in stack: #{expected_frame.inspect}"
44
+ end
45
+
46
+ def note_children_time(frame, time, deduct_call_time_from_parent)
47
+ if !@stack.empty?
48
+ if deduct_call_time_from_parent
49
+ @stack.last.children_time += (time - frame.start_time)
50
+ else
51
+ @stack.last.children_time += frame.children_time
52
+ end
53
+ end
54
+ end
55
+
56
+ def empty?
57
+ @stack.empty?
58
+ end
59
+
60
+ def clear
61
+ @stack.clear
62
+ end
63
+
64
+ def last
65
+ @stack.last
66
+ end
67
+
68
+ def size
69
+ @stack.size
70
+ end
71
+ end
72
+ end
73
+ end