oneapm_rpm 1.3.6 → 1.3.7.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/lib/one_apm/agent/agent/start_worker_thread.rb +8 -8
- data/lib/one_apm/agent/cross_app/cross_app_monitor.rb +2 -2
- data/lib/one_apm/agent/cross_app/cross_app_tracing.rb +2 -2
- data/lib/one_apm/agent/database.rb +17 -17
- data/lib/one_apm/agent/database/obfuscation_helpers.rb +13 -13
- data/lib/one_apm/agent/database/obfuscator.rb +5 -5
- data/lib/one_apm/agent/database/postgres_explain_obfuscator.rb +4 -4
- data/lib/one_apm/agent/datastore/metric_helper.rb +12 -12
- data/lib/one_apm/agent/datastore/mongo/command_formatter.rb +6 -6
- data/lib/one_apm/agent/datastore/mongo/metric_translator.rb +6 -6
- data/lib/one_apm/agent/datastore/mongo/obfuscator.rb +2 -2
- data/lib/one_apm/agent/datastore/mongo/statement_formatter.rb +4 -4
- data/lib/one_apm/agent/javascript_instrumentor.rb +22 -22
- data/lib/one_apm/agent/synthetics_monitor.rb +7 -7
- data/lib/one_apm/agent/threading/thread_profile.rb +2 -2
- data/lib/one_apm/collector/collector/http_connection.rb +4 -4
- data/lib/one_apm/collector/collector_service.rb +1 -1
- data/lib/one_apm/collector/commands/thread_profiler_session.rb +4 -4
- data/lib/one_apm/collector/containers/agent_command_router.rb +4 -4
- data/lib/one_apm/collector/containers/custom_event_aggregator.rb +10 -10
- data/lib/one_apm/collector/containers/error_collector.rb +4 -4
- data/lib/one_apm/collector/containers/sql_sampler.rb +2 -2
- data/lib/one_apm/collector/containers/transaction_event_aggregator.rb +36 -36
- data/lib/one_apm/collector/containers/transaction_sampler.rb +4 -4
- data/lib/one_apm/collector/containers/utilization_data.rb +1 -1
- data/lib/one_apm/collector/samplers/delayed_job_sampler.rb +7 -7
- data/lib/one_apm/collector/samplers/vm_sampler.rb +18 -18
- data/lib/one_apm/collector/stats_engine/gc_profiler.rb +6 -6
- data/lib/one_apm/collector/stats_engine/metric_stats.rb +1 -1
- data/lib/one_apm/collector/support/proc_poller.rb +2 -2
- data/lib/one_apm/configuration.rb +2 -2
- data/lib/one_apm/configuration/default_source.rb +2 -2
- data/lib/one_apm/configuration/environment_source.rb +5 -5
- data/lib/one_apm/configuration/high_security_source.rb +7 -7
- data/lib/one_apm/errors/noticed_error.rb +2 -2
- data/lib/one_apm/inst/dispatcher/puma.rb +7 -4
- data/lib/one_apm/inst/framework/grape.rb +9 -9
- data/lib/one_apm/inst/framework/sinatra/transaction_namer.rb +2 -2
- data/lib/one_apm/inst/http_clients/curb.rb +2 -2
- data/lib/one_apm/inst/http_clients/excon.rb +7 -7
- data/lib/one_apm/inst/http_clients/httpclient.rb +2 -2
- data/lib/one_apm/inst/http_clients/typhoeus.rb +2 -2
- data/lib/one_apm/inst/nosql/memcache.rb +5 -5
- data/lib/one_apm/inst/nosql/mongo2.rb +8 -8
- data/lib/one_apm/inst/orm/active_record.rb +2 -2
- data/lib/one_apm/inst/rails3/action_controller.rb +1 -1
- data/lib/one_apm/inst/rails4/action_view_subscriber.rb +2 -2
- data/lib/one_apm/inst/rails4/active_record_subscriber.rb +3 -3
- data/lib/one_apm/inst/support/queue_time.rb +14 -14
- data/lib/one_apm/logger/agent_logger.rb +4 -4
- data/lib/one_apm/metrics/metric_spec.rb +10 -10
- data/lib/one_apm/rack/browser_monitoring.rb +135 -144
- data/lib/one_apm/rack/developer_mode.rb +22 -33
- data/lib/one_apm/rack/developer_mode/helper.rb +1 -1
- data/lib/one_apm/rack/middleware_base.rb +2 -0
- data/lib/one_apm/rack/middleware_helper.rb +21 -0
- data/lib/one_apm/rack/middleware_hooks.rb +10 -8
- data/lib/one_apm/rack/middleware_tracing.rb +47 -44
- data/lib/one_apm/rack/middleware_wrapper.rb +63 -57
- data/lib/one_apm/support/backtrace/backtrace_node.rb +4 -4
- data/lib/one_apm/support/backtrace/backtrace_service.rb +7 -7
- data/lib/one_apm/support/collection_helper.rb +4 -4
- data/lib/one_apm/support/event_buffer/synthetics_event_buffer.rb +2 -2
- data/lib/one_apm/support/json_marshaller.rb +2 -2
- data/lib/one_apm/support/method_tracer.rb +2 -2
- data/lib/one_apm/support/method_tracer/helpers.rb +2 -2
- data/lib/one_apm/support/obfuscator.rb +5 -5
- data/lib/one_apm/support/rules_engine.rb +2 -2
- data/lib/one_apm/support/rules_engine/replacement_rule.rb +4 -4
- data/lib/one_apm/support/rules_engine/segment_terms_rule.rb +8 -8
- data/lib/one_apm/support/supported_versions.rb +9 -13
- data/lib/one_apm/support/vm/rubinius_vm.rb +1 -1
- data/lib/one_apm/transaction.rb +8 -8
- data/lib/one_apm/transaction/class_methods.rb +8 -8
- data/lib/one_apm/transaction/instance_helpers.rb +1 -1
- data/lib/one_apm/transaction/metric_constants.rb +27 -27
- data/lib/one_apm/transaction/sample_buffer/developer_mode_sample_buffer.rb +3 -3
- data/lib/one_apm/transaction/sample_buffer/force_persist_sample_buffer.rb +2 -2
- data/lib/one_apm/transaction/sample_buffer/slowest_sample_buffer.rb +2 -2
- data/lib/one_apm/transaction/sample_buffer/transaction_sample_buffer.rb +3 -3
- data/lib/one_apm/transaction/segment.rb +2 -2
- data/lib/one_apm/transaction/transaction_finish_append.rb +3 -3
- data/lib/one_apm/transaction/transaction_jruby_functions.rb +3 -3
- data/lib/one_apm/transaction/transaction_name.rb +3 -3
- data/lib/one_apm/transaction/transaction_namer.rb +7 -7
- data/lib/one_apm/transaction/transaction_sample_builder.rb +3 -3
- data/lib/one_apm/transaction/transaction_summary.rb +4 -4
- data/lib/one_apm/transaction/transaction_timings.rb +1 -1
- data/lib/one_apm/version.rb +3 -2
- metadata +3 -2
@@ -3,14 +3,14 @@
|
|
3
3
|
LibraryDetection.defer do
|
4
4
|
named :httpclient
|
5
5
|
|
6
|
-
|
6
|
+
OA_HTTPCLIENT_MINIMUM_VERSION = '2.2.0'
|
7
7
|
|
8
8
|
depends_on do
|
9
9
|
defined?(HTTPClient) && defined?(HTTPClient::VERSION)
|
10
10
|
end
|
11
11
|
|
12
12
|
depends_on do
|
13
|
-
minimum_supported_version = OneApm::VersionNumber.new(
|
13
|
+
minimum_supported_version = OneApm::VersionNumber.new(OA_HTTPCLIENT_MINIMUM_VERSION)
|
14
14
|
current_version = OneApm::VersionNumber.new(HTTPClient::VERSION)
|
15
15
|
|
16
16
|
current_version >= minimum_supported_version
|
@@ -48,10 +48,10 @@ end
|
|
48
48
|
|
49
49
|
module OneApm::Agent::Instrumentation::TyphoeusTracing
|
50
50
|
|
51
|
-
|
51
|
+
OA_EARLIEST_VERSION = OneApm::VersionNumber.new("0.5.3")
|
52
52
|
|
53
53
|
def self.is_supported_version?
|
54
|
-
OneApm::VersionNumber.new(Typhoeus::VERSION) >= OneApm::Agent::Instrumentation::TyphoeusTracing::
|
54
|
+
OneApm::VersionNumber.new(Typhoeus::VERSION) >= OneApm::Agent::Instrumentation::TyphoeusTracing::OA_EARLIEST_VERSION
|
55
55
|
end
|
56
56
|
|
57
57
|
def self.request_is_hydra_enabled?(request)
|
@@ -12,7 +12,7 @@ module OneApm
|
|
12
12
|
!OneApm::Manager.config[:disable_memcache]
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
OA_METHODS = [:get, :get_multi, :set, :add, :incr, :decr, :delete, :replace, :append,
|
16
16
|
:prepend, :cas, :single_get, :multi_get, :single_cas, :multi_cas]
|
17
17
|
|
18
18
|
def supported_methods_for(client_class, methods)
|
@@ -21,7 +21,7 @@ module OneApm
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def instrument_methods(client_class, requested_methods =
|
24
|
+
def instrument_methods(client_class, requested_methods = OA_METHODS)
|
25
25
|
supported_methods_for(client_class, requested_methods).each do |method_name|
|
26
26
|
|
27
27
|
visibility = OneApm::Helper.instance_method_visibility client_class, method_name
|
@@ -120,15 +120,15 @@ LibraryDetection.defer do
|
|
120
120
|
# re-evaluate after they've done that require.
|
121
121
|
defined?(::Dalli::Client) &&
|
122
122
|
::OneApm::Agent::Instrumentation::Memcache.supported_methods_for(::Dalli::Client,
|
123
|
-
|
123
|
+
OA_CAS_CLIENT_METHODS).any?
|
124
124
|
end
|
125
125
|
|
126
|
-
|
126
|
+
OA_CAS_CLIENT_METHODS = [:get_cas, :get_multi_cas, :set_cas, :replace_cas,
|
127
127
|
:delete_cas]
|
128
128
|
|
129
129
|
executes do
|
130
130
|
OneApm::Manager.logger.info 'Installing Dalli CAS Client Memcache instrumentation'
|
131
131
|
::OneApm::Agent::Instrumentation::Memcache.instrument_methods(::Dalli::Client,
|
132
|
-
|
132
|
+
OA_CAS_CLIENT_METHODS)
|
133
133
|
end
|
134
134
|
end
|
@@ -5,26 +5,26 @@ module OneApm
|
|
5
5
|
module Instrumentation
|
6
6
|
class MongoCommandSubscriber
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
OA_MONGODB = 'MongoDB'.freeze
|
9
|
+
OA_STARTED = 'STARTED'.freeze
|
10
|
+
OA_SUCCEEDED = 'SUCCEEDED'.freeze
|
11
|
+
OA_FAILED = 'FAILED'.freeze
|
12
12
|
|
13
13
|
def started(event)
|
14
14
|
begin
|
15
15
|
return unless OneApm::Manager.tl_is_execution_traced?
|
16
16
|
operations[event.operation_id] = event
|
17
17
|
rescue Exception => e
|
18
|
-
log_operations_error(
|
18
|
+
log_operations_error(OA_STARTED, e)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
def succeeded(event)
|
23
|
-
operator
|
23
|
+
operator OA_SUCCEEDED, event
|
24
24
|
end
|
25
25
|
|
26
26
|
def failed(event)
|
27
|
-
operator
|
27
|
+
operator OA_FAILED, event
|
28
28
|
end
|
29
29
|
|
30
30
|
private
|
@@ -35,7 +35,7 @@ module OneApm
|
|
35
35
|
|
36
36
|
def mertircs(event)
|
37
37
|
OneApm::Agent::Datastore::MetricHelper.metrics_for(
|
38
|
-
|
38
|
+
OA_MONGODB, event.command_name, event.command.values.first
|
39
39
|
)
|
40
40
|
end
|
41
41
|
|
@@ -7,7 +7,7 @@ module OneApm
|
|
7
7
|
module Instrumentation
|
8
8
|
module ActiveRecord
|
9
9
|
|
10
|
-
|
10
|
+
OA_EXPLAINER = lambda do |config, query|
|
11
11
|
connection = OneApm::Agent::Database.get_connection(config) do
|
12
12
|
::ActiveRecord::Base.send("#{config[:adapter]}_connection", config)
|
13
13
|
end
|
@@ -54,7 +54,7 @@ module OneApm
|
|
54
54
|
log_without_oneapm_instrumentation(*args, &block)
|
55
55
|
ensure
|
56
56
|
elapsed_time = (Time.now - t0).to_f
|
57
|
-
OneApm::Manager.notice_sql(sql, metric, @config, elapsed_time, state, &
|
57
|
+
OneApm::Manager.notice_sql(sql, metric, @config, elapsed_time, state, &OA_EXPLAINER)
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
@@ -49,7 +49,7 @@ module OneApm
|
|
49
49
|
def recordable?
|
50
50
|
name[0] == '!' ||
|
51
51
|
metric_name == 'View/text template/Rendering' ||
|
52
|
-
metric_name == "View/#{OneApm::Transaction::
|
52
|
+
metric_name == "View/#{OneApm::Transaction::OA_UNKNOWN_METRIC}/Partial"
|
53
53
|
end
|
54
54
|
|
55
55
|
def metric_name
|
@@ -75,7 +75,7 @@ module OneApm
|
|
75
75
|
elsif (parts = identifier.split('/')).size > 1
|
76
76
|
parts[-2..-1].join('/')
|
77
77
|
else
|
78
|
-
OneApm::Transaction::
|
78
|
+
OneApm::Transaction::OA_UNKNOWN_METRIC
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -9,10 +9,10 @@ module OneApm
|
|
9
9
|
module Agent
|
10
10
|
module Instrumentation
|
11
11
|
class ActiveRecordSubscriber < EventedSubscriber
|
12
|
-
|
12
|
+
OA_CACHED_QUERY_NAME = 'CACHE'.freeze unless defined? OA_CACHED_QUERY_NAME
|
13
13
|
|
14
14
|
def start(name, id, payload)
|
15
|
-
return if payload[:name] ==
|
15
|
+
return if payload[:name] == OA_CACHED_QUERY_NAME
|
16
16
|
return unless OneApm::Manager.tl_is_execution_traced?
|
17
17
|
super
|
18
18
|
rescue => e
|
@@ -20,7 +20,7 @@ module OneApm
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def finish(name, id, payload)
|
23
|
-
return if payload[:name] ==
|
23
|
+
return if payload[:name] == OA_CACHED_QUERY_NAME
|
24
24
|
state = OneApm::TransactionState.tl_get
|
25
25
|
return unless state.is_execution_traced?
|
26
26
|
event = pop_event(id)
|
@@ -4,22 +4,22 @@ module OneApm
|
|
4
4
|
module Agent
|
5
5
|
module Instrumentation
|
6
6
|
module QueueTime
|
7
|
-
unless defined?(
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
unless defined?(OA_REQUEST_START_HEADER)
|
8
|
+
OA_REQUEST_START_HEADER = 'HTTP_X_REQUEST_START'.freeze
|
9
|
+
OA_QUEUE_START_HEADER = 'HTTP_X_QUEUE_START'.freeze
|
10
|
+
OA_MIDDLEWARE_START_HEADER = 'HTTP_X_MIDDLEWARE_START'.freeze
|
11
|
+
OA_ALL_QUEUE_METRIC = 'WebFrontend/QueueTime'.freeze
|
12
12
|
# any timestamps before this are thrown out and the parser
|
13
13
|
# will try again with a larger unit (2000/1/1 UTC)
|
14
|
-
|
14
|
+
OA_EARLIEST_ACCEPTABLE_TIME = Time.at(946684800)
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
OA_CANDIDATE_HEADERS = [
|
17
|
+
OA_REQUEST_START_HEADER,
|
18
|
+
OA_QUEUE_START_HEADER,
|
19
|
+
OA_MIDDLEWARE_START_HEADER
|
20
20
|
].freeze
|
21
21
|
|
22
|
-
|
22
|
+
OA_DIVISORS = [1_000_000, 1_000, 1]
|
23
23
|
end
|
24
24
|
|
25
25
|
module_function
|
@@ -27,7 +27,7 @@ module OneApm
|
|
27
27
|
def parse_frontend_timestamp(headers, now=Time.now)
|
28
28
|
earliest = nil
|
29
29
|
|
30
|
-
|
30
|
+
OA_CANDIDATE_HEADERS.each do |header|
|
31
31
|
if headers[header]
|
32
32
|
parsed = parse_timestamp(timestamp_string_from_header_value(headers[header]))
|
33
33
|
if parsed && (!earliest || parsed < earliest)
|
@@ -54,10 +54,10 @@ module OneApm
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def parse_timestamp(string)
|
57
|
-
|
57
|
+
OA_DIVISORS.each do |divisor|
|
58
58
|
begin
|
59
59
|
t = Time.at(string.to_f / divisor)
|
60
|
-
return t if t >
|
60
|
+
return t if t > OA_EARLIEST_ACCEPTABLE_TIME
|
61
61
|
rescue RangeError
|
62
62
|
# On Ruby versions built with a 32-bit time_t, attempting to
|
63
63
|
# instantiate a Time object in the far future raises a RangeError,
|
@@ -39,7 +39,7 @@ module OneApm
|
|
39
39
|
format_and_send(:debug, msgs, &blk)
|
40
40
|
end
|
41
41
|
|
42
|
-
|
42
|
+
OA_NUM_LOG_ONCE_KEYS = 1000
|
43
43
|
|
44
44
|
def log_once(level, key, *msgs)
|
45
45
|
# Since `already_logged` might change between calls, just grab it once
|
@@ -48,7 +48,7 @@ module OneApm
|
|
48
48
|
|
49
49
|
return if logged.include?(key)
|
50
50
|
|
51
|
-
if logged.size >=
|
51
|
+
if logged.size >= OA_NUM_LOG_ONCE_KEYS && key.kind_of?(String)
|
52
52
|
# The reason for preventing too many keys in `logged` is for
|
53
53
|
# memory concerns.
|
54
54
|
# The reason for checking the type of the key is that we always want
|
@@ -168,7 +168,7 @@ module OneApm
|
|
168
168
|
@log.level = AgentLogger.log_level_for(OneApm::Manager.config[:log_level])
|
169
169
|
end
|
170
170
|
|
171
|
-
|
171
|
+
OA_LOG_LEVELS = {
|
172
172
|
"debug" => ::Logger::DEBUG,
|
173
173
|
"info" => ::Logger::INFO,
|
174
174
|
"warn" => ::Logger::WARN,
|
@@ -177,7 +177,7 @@ module OneApm
|
|
177
177
|
}
|
178
178
|
|
179
179
|
def self.log_level_for(level)
|
180
|
-
|
180
|
+
OA_LOG_LEVELS.fetch(level.to_s.downcase, ::Logger::INFO)
|
181
181
|
end
|
182
182
|
|
183
183
|
def set_log_format!
|
@@ -5,25 +5,25 @@ module OneApm
|
|
5
5
|
attr_reader :name, :scope
|
6
6
|
|
7
7
|
# the maximum length of a metric name or metric scope
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
OA_MAX_LENGTH = 255
|
9
|
+
OA_LENGTH_RANGE = (0...OA_MAX_LENGTH)
|
10
|
+
OA_EMPTY_SCOPE = ''.freeze
|
11
11
|
|
12
12
|
def initialize(metric_name='', metric_scope=nil)
|
13
|
-
if metric_name.to_s.length >
|
14
|
-
@name = metric_name.to_s[
|
13
|
+
if metric_name.to_s.length > OA_MAX_LENGTH
|
14
|
+
@name = metric_name.to_s[OA_LENGTH_RANGE]
|
15
15
|
else
|
16
16
|
@name = metric_name.to_s
|
17
17
|
end
|
18
18
|
|
19
19
|
if metric_scope
|
20
|
-
if metric_scope.to_s.length >
|
21
|
-
@scope = metric_scope.to_s[
|
20
|
+
if metric_scope.to_s.length > OA_MAX_LENGTH
|
21
|
+
@scope = metric_scope.to_s[OA_LENGTH_RANGE]
|
22
22
|
else
|
23
23
|
@scope = metric_scope.to_s
|
24
24
|
end
|
25
25
|
else
|
26
|
-
@scope =
|
26
|
+
@scope = OA_EMPTY_SCOPE
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -45,10 +45,10 @@ module OneApm
|
|
45
45
|
OneApm::Manager.logger.warn("The sub method on metric specs is deprecated") rescue nil
|
46
46
|
return nil if name !~ pattern &&
|
47
47
|
(!apply_to_scope || scope.nil? || scope !~ pattern)
|
48
|
-
new_name = name.sub(pattern, replacement)[
|
48
|
+
new_name = name.sub(pattern, replacement)[OA_LENGTH_RANGE]
|
49
49
|
|
50
50
|
if apply_to_scope
|
51
|
-
new_scope = (scope && scope.sub(pattern, replacement)[
|
51
|
+
new_scope = (scope && scope.sub(pattern, replacement)[OA_LENGTH_RANGE])
|
52
52
|
else
|
53
53
|
new_scope = scope
|
54
54
|
end
|
@@ -5,185 +5,176 @@ require 'ipaddr'
|
|
5
5
|
require 'one_apm/rack/middleware_base'
|
6
6
|
require 'one_apm/rack/middleware_wrapper'
|
7
7
|
|
8
|
-
module OneApm::Rack
|
9
|
-
class BrowserMonitoring < MiddlewareBase
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
module OneApm
|
10
|
+
module Rack
|
11
|
+
class BrowserMonitoring < MiddlewareBase
|
14
12
|
|
15
|
-
|
16
|
-
|
13
|
+
OA_SCAN_LIMIT = 50_000
|
14
|
+
OA_ALREADY_INSTRUMENTED_KEY = "oneapm.browser_monitoring_already_instrumented"
|
15
|
+
OA_REMOTE_IP = "action_dispatch.remote_ip"
|
17
16
|
|
18
|
-
|
19
|
-
|
17
|
+
OA_CHARSET_RE = /<\s*meta[^>]+charset\s*=[^>]*>/im.freeze
|
18
|
+
OA_X_UA_COMPATIBLE_RE = /<\s*meta[^>]+http-equiv\s*=\s*['"]x-ua-compatible['"][^>]*>/im.freeze
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
response_string = autoinstrument_source(result[2], result[1], js_to_inject)
|
20
|
+
def traced_call(env)
|
21
|
+
result = @app.call(env) # [status, headers, response]
|
24
22
|
|
25
|
-
|
26
|
-
if
|
27
|
-
|
28
|
-
|
23
|
+
js_to_inject = OneApm::Manager.browser_timing_header
|
24
|
+
if (js_to_inject != "") && should_instrument?(env, result[0], result[1])
|
25
|
+
response_string = autoinstrument_source(result[2], result[1], js_to_inject)
|
26
|
+
|
27
|
+
env[OA_ALREADY_INSTRUMENTED_KEY] = true
|
28
|
+
if response_string
|
29
|
+
response = ::Rack::Response.new(response_string, result[0], result[1])
|
30
|
+
response.finish
|
31
|
+
else
|
32
|
+
result
|
33
|
+
end
|
29
34
|
else
|
30
35
|
result
|
31
36
|
end
|
32
|
-
else
|
33
|
-
result
|
34
37
|
end
|
35
|
-
end
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
def should_instrument?(env, status, headers)
|
40
|
+
OneApm::Manager.config[:'browser_monitoring.auto_instrument'] &&
|
41
|
+
status == 200 &&
|
42
|
+
!env[OA_ALREADY_INSTRUMENTED_KEY] &&
|
43
|
+
is_html?(headers) &&
|
44
|
+
!is_attachment?(headers) &&
|
45
|
+
!is_streaming?(env) &&
|
46
|
+
ip_valid?(env)
|
47
|
+
end
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
def is_html?(headers)
|
50
|
+
headers["Content-Type"] && headers["Content-Type"].include?("text/html")
|
51
|
+
end
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
-
|
53
|
+
def is_attachment?(headers)
|
54
|
+
headers['Content-Disposition'].to_s.include?('attachment')
|
55
|
+
end
|
54
56
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
57
|
+
def is_streaming?(env)
|
58
|
+
return false unless defined?(ActionController::Live)
|
59
|
+
env['action_controller.instance'].class.included_modules.include?(ActionController::Live)
|
60
|
+
end
|
59
61
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
62
|
+
def ip_valid?(env)
|
63
|
+
whitelist_ips = OneApm::Manager.config[:'browser_monitoring.whitelist_ips']
|
64
|
+
return true if whitelist_ips.empty? || env[OA_REMOTE_IP].to_s.empty?
|
65
|
+
OneApm::Manager.logger.debug "Remote IP: #{env[OA_REMOTE_IP]}"
|
66
|
+
begin
|
67
|
+
client = IPAddr.new(env[OA_REMOTE_IP].to_s).to_i
|
68
|
+
whitelist_ips.split(",").any? do |ip|
|
69
|
+
low_ip, high_ip = ip.split("-").map(&:strip)
|
70
|
+
low = IPAddr.new(low_ip).to_i
|
71
|
+
if high_ip
|
72
|
+
high = IPAddr.new(high_ip).to_i
|
73
|
+
(low..high) === client
|
74
|
+
else
|
75
|
+
low == client
|
76
|
+
end
|
74
77
|
end
|
78
|
+
rescue => e
|
79
|
+
OneApm::Manager.logger.error "Configuration for browser_monitoring.whitelist_ips has problems:#{whitelist_ips}"
|
80
|
+
true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def autoinstrument_source(response, headers, js_to_inject)
|
85
|
+
begin
|
86
|
+
source = gather_source(response)
|
87
|
+
close_old_response(response)
|
88
|
+
return nil unless source
|
89
|
+
inject_js(source, headers, js_to_inject)
|
90
|
+
rescue => e
|
91
|
+
OneApm::Manager.logger.debug "Skipping RUM instrumentation on exception.", e
|
92
|
+
nil
|
75
93
|
end
|
76
|
-
rescue => e
|
77
|
-
OneApm::Manager.logger.error "Configuration for browser_monitoring.whitelist_ips has problems:#{whitelist_ips}"
|
78
|
-
true
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def autoinstrument_source(response, headers, js_to_inject)
|
83
|
-
begin
|
84
|
-
source = gather_source(response)
|
85
|
-
close_old_response(response)
|
86
|
-
return nil unless source
|
87
|
-
inject_js(source, headers, js_to_inject)
|
88
|
-
rescue => e
|
89
|
-
OneApm::Manager.logger.debug "Skipping RUM instrumentation on exception.", e
|
90
|
-
nil
|
91
94
|
end
|
92
|
-
end
|
93
95
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
96
|
+
def inject_js source, headers, js_to_inject
|
97
|
+
position = OneApm::Manager.config[:'browser_monitoring.position']
|
98
|
+
insertion_index = (position.empty? || position.to_sym != :footer) ? header_index(source) : footer_index(source)
|
99
|
+
if insertion_index
|
100
|
+
source = source[0...insertion_index] \
|
101
|
+
<< js_to_inject \
|
102
|
+
<< source[insertion_index..-1]
|
103
|
+
if headers['Content-Length']
|
104
|
+
headers['Content-Length'] = calculate_content_length(source).to_s
|
105
|
+
end
|
106
|
+
else
|
107
|
+
OneApm::Manager.logger.debug "Skipping RUM instrumentation. Could not properly determine location to inject script."
|
103
108
|
end
|
104
|
-
|
105
|
-
OneApm::Manager.logger.debug "Skipping RUM instrumentation. Could not properly determine location to inject script."
|
109
|
+
source
|
106
110
|
end
|
107
|
-
source
|
108
|
-
end
|
109
111
|
|
110
|
-
def header_index source
|
111
|
-
beginning_of_source = source[0..SCAN_LIMIT]
|
112
|
-
if body_start = find_body_start(beginning_of_source)
|
113
|
-
meta_tag_positions = [
|
114
|
-
find_x_ua_compatible_position(beginning_of_source),
|
115
|
-
find_charset_position(beginning_of_source)
|
116
|
-
].compact
|
117
112
|
|
118
|
-
|
119
|
-
|
113
|
+
def header_index source
|
114
|
+
beginning_of_source = source[0..OA_SCAN_LIMIT]
|
115
|
+
if body_start = find_body_start(beginning_of_source)
|
116
|
+
meta_tag_positions = [
|
117
|
+
find_x_ua_compatible_position(beginning_of_source),
|
118
|
+
find_charset_position(beginning_of_source)
|
119
|
+
].compact
|
120
|
+
|
121
|
+
if !meta_tag_positions.empty?
|
122
|
+
insertion_index = meta_tag_positions.max
|
123
|
+
else
|
124
|
+
insertion_index = find_end_of_head_open(beginning_of_source) || body_start
|
125
|
+
end
|
126
|
+
insertion_index
|
120
127
|
else
|
121
|
-
|
128
|
+
msg = "Skipping RUM instrumentation. Unable to find <body> tag in first #{OA_SCAN_LIMIT} bytes of document."
|
129
|
+
OneApm::Manager.logger.log_once(:warn, :rum_insertion_failure, msg)
|
130
|
+
OneApm::Manager.logger.debug(msg)
|
131
|
+
nil
|
122
132
|
end
|
123
|
-
insertion_index
|
124
|
-
else
|
125
|
-
msg = "Skipping RUM instrumentation. Unable to find <body> tag in first #{SCAN_LIMIT} bytes of document."
|
126
|
-
OneApm::Manager.logger.log_once(:warn, :rum_insertion_failure, msg)
|
127
|
-
OneApm::Manager.logger.debug(msg)
|
128
|
-
nil
|
129
133
|
end
|
130
|
-
end
|
131
134
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
135
|
+
def footer_index source
|
136
|
+
if body_end = find_body_end(source)
|
137
|
+
body_end
|
138
|
+
else
|
139
|
+
msg = "Skipping RUM instrumentation. Unable to find </body> tag from document."
|
140
|
+
OneApm::Manager.logger.log_once(:warn, :rum_insertion_failure, msg)
|
141
|
+
OneApm::Manager.logger.debug(msg)
|
142
|
+
nil
|
143
|
+
end
|
140
144
|
end
|
141
|
-
end
|
142
145
|
|
143
|
-
|
144
|
-
|
145
|
-
response.each {|fragment| source ? (source << fragment.to_s) : (source = fragment.to_s)}
|
146
|
-
source
|
147
|
-
end
|
148
|
-
|
149
|
-
# Per "The Response > The Body" section of Rack spec, we should close
|
150
|
-
# if our response is able. http://rack.rubyforge.org/doc/SPEC.html
|
151
|
-
def close_old_response(response)
|
152
|
-
if response.respond_to?(:close)
|
153
|
-
response.close
|
146
|
+
def find_body_start(beginning_of_source)
|
147
|
+
beginning_of_source.index("<body")
|
154
148
|
end
|
155
|
-
end
|
156
149
|
|
157
|
-
|
158
|
-
|
159
|
-
|
150
|
+
def find_body_end source
|
151
|
+
source.rindex("</body>")
|
152
|
+
end
|
160
153
|
|
161
|
-
def find_body_end source
|
162
|
-
source.rindex("</body>")
|
163
|
-
end
|
164
154
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
155
|
+
def find_x_ua_compatible_position(beginning_of_source)
|
156
|
+
match = OA_X_UA_COMPATIBLE_RE.match(beginning_of_source)
|
157
|
+
match.end(0) if match
|
158
|
+
end
|
169
159
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
160
|
+
def find_charset_position(beginning_of_source)
|
161
|
+
match = OA_CHARSET_RE.match(beginning_of_source)
|
162
|
+
match.end(0) if match
|
163
|
+
end
|
174
164
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
165
|
+
def find_end_of_head_open(beginning_of_source)
|
166
|
+
head_open = beginning_of_source.index("<head")
|
167
|
+
beginning_of_source.index(">", head_open) + 1 if head_open
|
168
|
+
end
|
179
169
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
170
|
+
# String does not respond to 'bytesize' in 1.8.6. Fortunately String#length
|
171
|
+
# returns bytes rather than characters in 1.8.6 so we can use that instead.
|
172
|
+
def calculate_content_length(source)
|
173
|
+
if source.respond_to?(:bytesize)
|
174
|
+
source.bytesize
|
175
|
+
else
|
176
|
+
source.length
|
177
|
+
end
|
187
178
|
end
|
188
179
|
end
|
189
180
|
end
|