instana 1.11.6 → 1.193.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of instana might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.circleci/config.yml +168 -0
- data/Rakefile +26 -37
- data/gemfiles/libraries.gemfile +2 -0
- data/lib/instana/agent.rb +6 -0
- data/lib/instana/base.rb +2 -0
- data/lib/instana/config.rb +11 -0
- data/lib/instana/frameworks/cuba.rb +33 -0
- data/lib/instana/frameworks/instrumentation/action_controller.rb +11 -0
- data/lib/instana/frameworks/instrumentation/mysql2_adapter.rb +7 -1
- data/lib/instana/frameworks/instrumentation/mysql_adapter.rb +7 -1
- data/lib/instana/frameworks/instrumentation/postgresql_adapter.rb +23 -5
- data/lib/instana/frameworks/roda.rb +41 -0
- data/lib/instana/frameworks/sinatra.rb +17 -0
- data/lib/instana/instrumentation/excon.rb +1 -1
- data/lib/instana/instrumentation/graphql.rb +77 -0
- data/lib/instana/instrumentation/net-http.rb +2 -0
- data/lib/instana/instrumentation/rack.rb +23 -3
- data/lib/instana/secrets.rb +42 -0
- data/lib/instana/setup.rb +1 -0
- data/lib/instana/test.rb +4 -3
- data/lib/instana/tracing/span.rb +23 -10
- data/lib/instana/version.rb +1 -1
- data/test/apps/cuba.rb +4 -0
- data/test/apps/roda.rb +3 -0
- data/test/apps/sinatra.rb +4 -0
- data/test/config_test.rb +1 -17
- data/test/frameworks/cuba_test.rb +14 -1
- data/test/frameworks/rack_test.rb +121 -69
- data/test/frameworks/rails/actioncontroller_test.rb +12 -0
- data/test/frameworks/rails/activerecord_test.rb +80 -28
- data/test/frameworks/roda_test.rb +14 -0
- data/test/frameworks/sinatra_test.rb +37 -15
- data/test/instrumentation/excon_test.rb +0 -2
- data/test/instrumentation/graphql_test.rb +116 -0
- data/test/instrumentation/grpc_test.rb +1 -1
- data/test/instrumentation/redis_test.rb +10 -0
- data/test/secrets_test.rb +73 -0
- data/test/tracing/tracer_test.rb +31 -1
- metadata +10 -6
- data/.travis.yml +0 -43
- data/test/tracing/trace_test.rb +0 -67
@@ -1,6 +1,47 @@
|
|
1
1
|
require "instana/rack"
|
2
2
|
|
3
|
+
module Instana
|
4
|
+
module RodaPathTemplateExtractor
|
5
|
+
module RequestMethods
|
6
|
+
TERM = defined?(::Roda) ? ::Roda::RodaPlugins::Base::RequestMethods::TERM : Object
|
7
|
+
|
8
|
+
def if_match(args, &blk)
|
9
|
+
path = @remaining_path
|
10
|
+
captures = @captures.clear
|
11
|
+
|
12
|
+
if match_all(args)
|
13
|
+
(env['INSTANA_PATH_TEMPLATE_FRAGMENTS'] ||= []).concat(named_args(args, blk))
|
14
|
+
block_result(blk.(*captures))
|
15
|
+
env['INSTANA_HTTP_PATH_TEMPLATE'] = env['INSTANA_PATH_TEMPLATE_FRAGMENTS']
|
16
|
+
.join('/')
|
17
|
+
.prepend('/')
|
18
|
+
throw :halt, response.finish
|
19
|
+
else
|
20
|
+
@remaining_path = path
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def named_args(args, blk)
|
26
|
+
parameters = blk.parameters
|
27
|
+
args.map do |a|
|
28
|
+
case a
|
29
|
+
when String
|
30
|
+
a
|
31
|
+
when TERM
|
32
|
+
nil
|
33
|
+
else
|
34
|
+
_, name = parameters.pop
|
35
|
+
"{#{name}}"
|
36
|
+
end
|
37
|
+
end.compact
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
3
43
|
if defined?(::Roda)
|
4
44
|
::Instana.logger.debug "Instrumenting Roda"
|
5
45
|
Roda.use ::Instana::Rack
|
46
|
+
Roda.plugin ::Instana::RodaPathTemplateExtractor
|
6
47
|
end
|
@@ -3,7 +3,24 @@ require "instana/rack"
|
|
3
3
|
# This instrumentation will insert Rack into Sinatra _and_ Padrino since
|
4
4
|
# the latter is based on Sinatra
|
5
5
|
|
6
|
+
module Instana
|
7
|
+
module SinatraPathTemplateExtractor
|
8
|
+
def self.extended(base)
|
9
|
+
::Instana.logger.debug "#{base} extended #{self}"
|
10
|
+
base.store_path_template
|
11
|
+
end
|
12
|
+
|
13
|
+
def store_path_template
|
14
|
+
after do
|
15
|
+
@env["INSTANA_HTTP_PATH_TEMPLATE"] = @env["sinatra.route"]
|
16
|
+
.sub("#{@request.request_method} ", '')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
6
22
|
if defined?(::Sinatra)
|
7
23
|
::Instana.logger.debug "Instrumenting Sinatra"
|
8
24
|
::Sinatra::Base.use ::Instana::Rack
|
25
|
+
::Sinatra::Base.register ::Instana::SinatraPathTemplateExtractor
|
9
26
|
end
|
@@ -7,7 +7,7 @@ if defined?(::Excon) && ::Instana.config[:excon][:enabled]
|
|
7
7
|
|
8
8
|
payload = { :http => {} }
|
9
9
|
path = datum[:path].split('?').first
|
10
|
-
payload[:http][:url] = "#{datum[:connection].instance_variable_get(:@socket_key)}#{path}"
|
10
|
+
payload[:http][:url] = ::Instana.secrets.remove_from_query("#{datum[:connection].instance_variable_get(:@socket_key)}#{path}")
|
11
11
|
payload[:http][:method] = datum[:method] if datum.key?(:method)
|
12
12
|
|
13
13
|
if datum[:pipeline] == true
|
@@ -0,0 +1,77 @@
|
|
1
|
+
if defined?(GraphQL::Schema) && defined?(GraphQL::Tracing::PlatformTracing) && ::Instana.config[:graphql][:enabled]
|
2
|
+
module Instana
|
3
|
+
class GraphqlTracing < GraphQL::Tracing::PlatformTracing
|
4
|
+
self.platform_keys = {
|
5
|
+
'lex' => 'lex.graphql',
|
6
|
+
'parse' => 'parse.graphql',
|
7
|
+
'validate' => 'validate.graphql',
|
8
|
+
'analyze_query' => 'analyze.graphql',
|
9
|
+
'analyze_multiplex' => 'analyze.graphql',
|
10
|
+
'execute_multiplex' => 'execute.graphql',
|
11
|
+
'execute_query' => 'execute.graphql',
|
12
|
+
'execute_query_lazy' => 'execute.graphql',
|
13
|
+
}
|
14
|
+
|
15
|
+
def platform_trace(platform_key, key, data)
|
16
|
+
return yield unless key == 'execute_query'
|
17
|
+
operation = data[:query].selected_operation
|
18
|
+
|
19
|
+
arguments = []
|
20
|
+
fields = []
|
21
|
+
|
22
|
+
operation.selections.each do |field|
|
23
|
+
arguments.concat(walk_fields(field, :arguments))
|
24
|
+
fields.concat(walk_fields(field, :selections))
|
25
|
+
end
|
26
|
+
|
27
|
+
payload = {
|
28
|
+
operationName: data[:query].operation_name || 'anonymous',
|
29
|
+
operationType: operation.operation_type,
|
30
|
+
arguments: grouped_fields(arguments),
|
31
|
+
fields: grouped_fields(fields),
|
32
|
+
}
|
33
|
+
|
34
|
+
begin
|
35
|
+
::Instana.tracer.log_entry(:'graphql.server')
|
36
|
+
yield
|
37
|
+
rescue Exception => e
|
38
|
+
::Instana.tracer.log_error(e)
|
39
|
+
raise e
|
40
|
+
ensure
|
41
|
+
::Instana.tracer.log_exit(:'graphql.server', {graphql: payload})
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def platform_field_key(type, field)
|
46
|
+
"#{type.graphql_name}.#{field.graphql_name}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def platform_authorized_key(type)
|
50
|
+
"#{type.graphql_name}.authorized.graphql"
|
51
|
+
end
|
52
|
+
|
53
|
+
def platform_resolve_type_key(type)
|
54
|
+
"#{type.graphql_name}.resolve_type.graphql"
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def walk_fields(parent, method)
|
60
|
+
return [] unless parent.respond_to?(method)
|
61
|
+
|
62
|
+
parent.send(method).map do |field|
|
63
|
+
[{object: parent.name, field: field.name}] + walk_fields(field, method)
|
64
|
+
end.flatten
|
65
|
+
end
|
66
|
+
|
67
|
+
def grouped_fields(fields)
|
68
|
+
fields
|
69
|
+
.group_by { |p| p[:object] }
|
70
|
+
.map { |name, p| [name, p.map { |f| f[:field] }] }
|
71
|
+
.to_h
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
::GraphQL::Schema.use(::Instana::GraphqlTracing)
|
77
|
+
end
|
@@ -32,6 +32,8 @@ if defined?(::Net::HTTP) && ::Instana.config[:nethttp][:enabled]
|
|
32
32
|
kv_payload[:http][:url] = "http://#{@address}:#{@port}#{request.path}"
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
kv_payload[:http][:url] = ::Instana.secrets.remove_from_query(kv_payload[:http][:url])
|
35
37
|
|
36
38
|
# The core call
|
37
39
|
response = request_without_instana(*args, &block)
|
@@ -8,8 +8,9 @@ module Instana
|
|
8
8
|
@app = app
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
kvs = {
|
11
|
+
def collect_kvs(env)
|
12
|
+
kvs = {}
|
13
|
+
kvs[:http] = {}
|
13
14
|
kvs[:http][:method] = env['REQUEST_METHOD']
|
14
15
|
kvs[:http][:url] = ::CGI.unescape(env['PATH_INFO'])
|
15
16
|
|
@@ -37,15 +38,27 @@ module Instana
|
|
37
38
|
end
|
38
39
|
}
|
39
40
|
end
|
41
|
+
return kvs
|
42
|
+
end
|
40
43
|
|
44
|
+
def call(env)
|
41
45
|
# Check incoming context
|
42
46
|
incoming_context = {}
|
43
47
|
if env.key?('HTTP_X_INSTANA_T')
|
44
48
|
incoming_context[:trace_id] = ::Instana::Util.header_to_id(env['HTTP_X_INSTANA_T'])
|
45
49
|
incoming_context[:span_id] = ::Instana::Util.header_to_id(env['HTTP_X_INSTANA_S']) if env.key?('HTTP_X_INSTANA_S')
|
46
50
|
incoming_context[:level] = env['HTTP_X_INSTANA_L'] if env.key?('HTTP_X_INSTANA_L')
|
51
|
+
|
52
|
+
# Honor X-Instana-L
|
53
|
+
if incoming_context[:level] and incoming_context[:level].length > 0
|
54
|
+
if incoming_context[:level][0] == "0"
|
55
|
+
return @app.call(env)
|
56
|
+
end
|
57
|
+
end
|
47
58
|
end
|
48
59
|
|
60
|
+
kvs = collect_kvs(env)
|
61
|
+
|
49
62
|
::Instana.tracer.log_start_or_continue(:rack, {}, incoming_context)
|
50
63
|
|
51
64
|
status, headers, response = @app.call(env)
|
@@ -63,6 +76,11 @@ module Instana
|
|
63
76
|
::Instana.tracer.log_error(nil)
|
64
77
|
end
|
65
78
|
|
79
|
+
# If the framework instrumentation provides a path template,
|
80
|
+
# pass it into the span here.
|
81
|
+
# See: https://www.instana.com/docs/tracing/custom-best-practices/#path-templates-visual-grouping-of-http-endpoints
|
82
|
+
kvs[:http][:path_tpl] = env['INSTANA_HTTP_PATH_TEMPLATE'] if env['INSTANA_HTTP_PATH_TEMPLATE']
|
83
|
+
|
66
84
|
# Save the IDs before the trace ends so we can place
|
67
85
|
# them in the response headers in the ensure block
|
68
86
|
trace_id = ::Instana.tracer.current_span.trace_id
|
@@ -78,8 +96,10 @@ module Instana
|
|
78
96
|
# Set reponse headers; encode as hex string
|
79
97
|
headers['X-Instana-T'] = ::Instana::Util.id_to_header(trace_id)
|
80
98
|
headers['X-Instana-S'] = ::Instana::Util.id_to_header(span_id)
|
99
|
+
headers['X-Instana-L'] = '1'
|
100
|
+
headers['Server-Timing'] = "intid;desc=#{::Instana::Util.id_to_header(trace_id)}"
|
101
|
+
::Instana.tracer.log_end(:rack, kvs)
|
81
102
|
end
|
82
|
-
::Instana.tracer.log_end(:rack, kvs)
|
83
103
|
end
|
84
104
|
end
|
85
105
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module Instana
|
5
|
+
class Secrets
|
6
|
+
def remove_from_query(str, secret_values = Instana.agent.secret_values)
|
7
|
+
return str unless secret_values
|
8
|
+
|
9
|
+
url = URI(str)
|
10
|
+
params = CGI.parse(url.query)
|
11
|
+
|
12
|
+
redacted = params.map do |k, v|
|
13
|
+
needs_redaction = secret_values['list']
|
14
|
+
.any? { |t| matcher(secret_values['matcher']).(t,k) }
|
15
|
+
[k, needs_redaction ? '<redacted>' : v]
|
16
|
+
end
|
17
|
+
|
18
|
+
url.query = URI.encode_www_form(redacted)
|
19
|
+
CGI.unescape(url.to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def matcher(name)
|
25
|
+
case name
|
26
|
+
when 'equals-ignore-case'
|
27
|
+
->(expected, actual) { expected.casecmp(actual) == 0 }
|
28
|
+
when 'equals'
|
29
|
+
->(expected, actual) { (expected <=> actual) == 0 }
|
30
|
+
when 'contains-ignore-case'
|
31
|
+
->(expected, actual) { actual.downcase.include?(expected) }
|
32
|
+
when 'contains'
|
33
|
+
->(expected, actual) { actual.include?(expected) }
|
34
|
+
when 'regex'
|
35
|
+
->(expected, actual) { !Regexp.new(expected).match(actual).nil? }
|
36
|
+
else
|
37
|
+
::Instana.logger.warn("Matcher #{name} is not supported.")
|
38
|
+
lambda { false }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/instana/setup.rb
CHANGED
data/lib/instana/test.rb
CHANGED
@@ -7,14 +7,15 @@ module Instana
|
|
7
7
|
def setup_environment
|
8
8
|
# Set defaults if not set
|
9
9
|
ENV['MEMCACHED_HOST'] ||= '127.0.0.1:11211'
|
10
|
-
ENV['
|
11
|
-
ENV['
|
10
|
+
ENV['POSTGRES_HOST'] ||= "127.0.0.1"
|
11
|
+
ENV['POSTGRES_USER'] ||= "stan"
|
12
|
+
ENV['POSTGRES_PASSWORD'] ||= "stanlikesdata"
|
12
13
|
ENV['TRAVIS_MYSQL_HOST'] ||= "127.0.0.1"
|
13
14
|
ENV['TRAVIS_MYSQL_USER'] ||= "root"
|
14
15
|
|
15
16
|
if !ENV.key?('DATABASE_URL')
|
16
17
|
if ENV['DB_FLAVOR'] == 'postgresql'
|
17
|
-
ENV['DATABASE_URL'] = "postgresql://#{ENV['
|
18
|
+
ENV['DATABASE_URL'] = "postgresql://#{ENV['POSTGRES_USER']}:#{ENV['POSTGRES_PASSWORD']}@#{ENV['POSTGRES_HOST']}:5432/#{ENV['POSTGRES_USER']}"
|
18
19
|
elsif ENV['DB_FLAVOR'] == 'mysql'
|
19
20
|
ENV['DATABASE_URL'] = "mysql://#{ENV['TRAVIS_MYSQL_USER']}:#{ENV['TRAVIS_MYSQL_PASS']}@#{ENV['TRAVIS_MYSQL_HOST']}:3306/travis_ci_test"
|
20
21
|
else
|
data/lib/instana/tracing/span.rb
CHANGED
@@ -3,8 +3,8 @@ module Instana
|
|
3
3
|
REGISTERED_SPANS = [ :actioncontroller, :actionview, :activerecord, :excon,
|
4
4
|
:memcache, :'net-http', :rack, :render, :'rpc-client',
|
5
5
|
:'rpc-server', :'sidekiq-client', :'sidekiq-worker',
|
6
|
-
:redis, :'resque-client', :'resque-worker' ].freeze
|
7
|
-
ENTRY_SPANS = [ :rack, :'resque-worker', :'rpc-server', :'sidekiq-worker' ].freeze
|
6
|
+
:redis, :'resque-client', :'resque-worker', :'graphql.server' ].freeze
|
7
|
+
ENTRY_SPANS = [ :rack, :'resque-worker', :'rpc-server', :'sidekiq-worker', :'graphql.server' ].freeze
|
8
8
|
EXIT_SPANS = [ :activerecord, :excon, :'net-http', :'resque-client',
|
9
9
|
:'rpc-client', :'sidekiq-client', :redis ].freeze
|
10
10
|
HTTP_SPANS = [ :rack, :excon, :'net-http' ].freeze
|
@@ -57,9 +57,6 @@ module Instana
|
|
57
57
|
end
|
58
58
|
|
59
59
|
if ::Instana.config[:collect_backtraces]
|
60
|
-
# For entry spans, add a backtrace fingerprint
|
61
|
-
add_stack(limit: 2) if ENTRY_SPANS.include?(name)
|
62
|
-
|
63
60
|
# Attach a backtrace to all exit spans
|
64
61
|
add_stack if EXIT_SPANS.include?(name)
|
65
62
|
end
|
@@ -76,9 +73,11 @@ module Instana
|
|
76
73
|
#
|
77
74
|
# @param limit [Integer] Limit the backtrace to the top <limit> frames
|
78
75
|
#
|
79
|
-
def add_stack(limit:
|
76
|
+
def add_stack(limit: 30, stack: Kernel.caller)
|
80
77
|
frame_count = 0
|
78
|
+
sanitized_stack = []
|
81
79
|
@data[:stack] = []
|
80
|
+
limit = 40 if limit > 40
|
82
81
|
|
83
82
|
stack.each do |i|
|
84
83
|
# If the stack has the full instana gem version in it's path
|
@@ -86,18 +85,23 @@ module Instana
|
|
86
85
|
if !i.match(/instana\/instrumentation\/rack.rb/).nil? ||
|
87
86
|
(i.match(::Instana::VERSION_FULL).nil? && i.match('lib/instana/').nil?)
|
88
87
|
|
89
|
-
break if limit && frame_count >= limit
|
90
|
-
|
91
88
|
x = i.split(':')
|
92
89
|
|
93
|
-
|
90
|
+
sanitized_stack << {
|
94
91
|
:c => x[0],
|
95
92
|
:n => x[1],
|
96
93
|
:m => x[2]
|
97
94
|
}
|
98
|
-
frame_count = frame_count + 1 if limit
|
99
95
|
end
|
100
96
|
end
|
97
|
+
|
98
|
+
if sanitized_stack.length > limit
|
99
|
+
# (limit * -1) gives us negative form of <limit> used for
|
100
|
+
# slicing from the end of the list. e.g. stack[-30, 30]
|
101
|
+
@data[:stack] = sanitized_stack[limit*-1, limit]
|
102
|
+
else
|
103
|
+
@data[:stack] = sanitized_stack
|
104
|
+
end
|
101
105
|
end
|
102
106
|
|
103
107
|
# Log an error into the span
|
@@ -303,6 +307,15 @@ module Instana
|
|
303
307
|
# a String, Numeric, or Boolean it will be encoded with to_s
|
304
308
|
#
|
305
309
|
def set_tag(key, value)
|
310
|
+
if ![Symbol, String].include?(key.class)
|
311
|
+
key = key.to_s
|
312
|
+
end
|
313
|
+
|
314
|
+
# If <value> is not a Symbol, String, Array, Hash or Numeric - convert to string
|
315
|
+
if ![Symbol, String, Array, TrueClass, FalseClass, Hash].include?(value.class) && !value.is_a?(Numeric)
|
316
|
+
value = value.to_s
|
317
|
+
end
|
318
|
+
|
306
319
|
if custom?
|
307
320
|
@data[:data][:sdk][:custom] ||= {}
|
308
321
|
@data[:data][:sdk][:custom][:tags] ||= {}
|
data/lib/instana/version.rb
CHANGED
data/test/apps/cuba.rb
CHANGED
data/test/apps/roda.rb
CHANGED
data/test/apps/sinatra.rb
CHANGED
data/test/config_test.rb
CHANGED
@@ -10,28 +10,12 @@ class ConfigTest < Minitest::Test
|
|
10
10
|
assert_equal '127.0.0.1', ::Instana.config[:agent_host]
|
11
11
|
assert_equal 42699, ::Instana.config[:agent_port]
|
12
12
|
|
13
|
-
assert ::Instana.config[:enabled]
|
14
13
|
assert ::Instana.config[:tracing][:enabled]
|
15
14
|
assert ::Instana.config[:metrics][:enabled]
|
16
15
|
|
17
16
|
::Instana.config[:metrics].each do |k, v|
|
17
|
+
next unless v.is_a? Hash
|
18
18
|
assert_equal true, ::Instana.config[:metrics][k].key?(:enabled)
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
22
|
-
def test_that_global_affects_children
|
23
|
-
# Disabling the gem should explicitly disable
|
24
|
-
# metrics and tracing flags
|
25
|
-
::Instana.config[:enabled] = false
|
26
|
-
|
27
|
-
assert_equal false, ::Instana.config[:tracing][:enabled]
|
28
|
-
assert_equal false, ::Instana.config[:metrics][:enabled]
|
29
|
-
|
30
|
-
# Enabling the gem should explicitly enable
|
31
|
-
# metrics and tracing flags
|
32
|
-
::Instana.config[:enabled] = true
|
33
|
-
|
34
|
-
assert_equal ::Instana.config[:tracing][:enabled]
|
35
|
-
assert_equal ::Instana.config[:metrics][:enabled]
|
36
|
-
end
|
37
21
|
end
|