instana 1.195.0 → 1.197.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +16 -2
  3. data/.rubocop.yml +6 -2
  4. data/Appraisals +13 -1
  5. data/Rakefile +1 -1
  6. data/docker-compose.yml +20 -0
  7. data/gemfiles/aws_30.gemfile +3 -0
  8. data/gemfiles/excon_021.gemfile +18 -0
  9. data/gemfiles/excon_079.gemfile +18 -0
  10. data/gemfiles/shoryuken_50.gemfile +19 -0
  11. data/instana.gemspec +3 -7
  12. data/lib/instana.rb +3 -0
  13. data/lib/instana/activator.rb +2 -0
  14. data/lib/instana/activators/aws_sdk_s3.rb +20 -0
  15. data/lib/instana/activators/aws_sdk_sns.rb +20 -0
  16. data/lib/instana/activators/aws_sdk_sqs.rb +20 -0
  17. data/lib/instana/activators/excon.rb +1 -1
  18. data/lib/instana/activators/shoryuken.rb +24 -0
  19. data/lib/instana/backend/agent.rb +60 -0
  20. data/lib/instana/backend/gc_snapshot.rb +41 -0
  21. data/lib/instana/backend/host_agent.rb +57 -0
  22. data/lib/instana/backend/host_agent_activation_observer.rb +87 -0
  23. data/lib/instana/backend/host_agent_lookup.rb +57 -0
  24. data/lib/instana/backend/host_agent_reporting_observer.rb +106 -0
  25. data/lib/instana/backend/process_info.rb +64 -0
  26. data/lib/instana/backend/request_client.rb +84 -0
  27. data/lib/instana/backend/serverless_agent.rb +118 -0
  28. data/lib/instana/base.rb +8 -27
  29. data/lib/instana/config.rb +7 -21
  30. data/lib/instana/instrumentation/aws_sdk_dynamodb.rb +21 -2
  31. data/lib/instana/instrumentation/aws_sdk_s3.rb +55 -0
  32. data/lib/instana/instrumentation/aws_sdk_sns.rb +29 -0
  33. data/lib/instana/instrumentation/aws_sdk_sqs.rb +98 -0
  34. data/lib/instana/instrumentation/excon.rb +12 -4
  35. data/lib/instana/instrumentation/instrumented_request.rb +7 -2
  36. data/lib/instana/instrumentation/net-http.rb +4 -2
  37. data/lib/instana/instrumentation/shoryuken.rb +44 -0
  38. data/lib/instana/logger_delegator.rb +31 -0
  39. data/lib/instana/{opentracing → open_tracing}/carrier.rb +0 -0
  40. data/lib/instana/open_tracing/instana_tracer.rb +99 -0
  41. data/lib/instana/secrets.rb +8 -4
  42. data/lib/instana/setup.rb +20 -11
  43. data/lib/instana/snapshot/deltable.rb +25 -0
  44. data/lib/instana/snapshot/docker_container.rb +151 -0
  45. data/lib/instana/snapshot/fargate_container.rb +88 -0
  46. data/lib/instana/snapshot/fargate_process.rb +67 -0
  47. data/lib/instana/snapshot/fargate_task.rb +72 -0
  48. data/lib/instana/snapshot/ruby_process.rb +48 -0
  49. data/lib/instana/tracer.rb +29 -147
  50. data/lib/instana/tracing/processor.rb +4 -22
  51. data/lib/instana/tracing/span.rb +29 -38
  52. data/lib/instana/tracing/span_context.rb +3 -2
  53. data/lib/instana/util.rb +4 -67
  54. data/lib/instana/version.rb +1 -1
  55. data/lib/opentracing.rb +26 -3
  56. data/test/backend/agent_test.rb +54 -0
  57. data/test/backend/gc_snapshot_test.rb +11 -0
  58. data/test/backend/host_agent_activation_observer_test.rb +65 -0
  59. data/test/backend/host_agent_lookup_test.rb +78 -0
  60. data/test/backend/host_agent_reporting_observer_test.rb +192 -0
  61. data/test/backend/host_agent_test.rb +32 -0
  62. data/test/backend/process_info_test.rb +63 -0
  63. data/test/backend/request_client_test.rb +61 -0
  64. data/test/backend/serverless_agent_test.rb +73 -0
  65. data/test/config_test.rb +10 -0
  66. data/test/instana_test.rb +11 -4
  67. data/test/instrumentation/aws_test.rb +130 -2
  68. data/test/instrumentation/excon_test.rb +16 -1
  69. data/test/instrumentation/net_http_test.rb +18 -0
  70. data/test/instrumentation/rack_instrumented_request_test.rb +7 -4
  71. data/test/instrumentation/rack_test.rb +2 -14
  72. data/test/instrumentation/shoryuken_test.rb +47 -0
  73. data/test/secrets_test.rb +41 -22
  74. data/test/snapshot/deltable_test.rb +17 -0
  75. data/test/snapshot/docker_container_test.rb +82 -0
  76. data/test/snapshot/fargate_container_test.rb +82 -0
  77. data/test/snapshot/fargate_process_test.rb +35 -0
  78. data/test/snapshot/fargate_task_test.rb +49 -0
  79. data/test/snapshot/ruby_process_test.rb +14 -0
  80. data/test/support/mock_timer.rb +20 -0
  81. data/test/test_helper.rb +15 -3
  82. data/test/tracing/custom_test.rb +1 -3
  83. data/test/tracing/id_management_test.rb +4 -0
  84. data/test/tracing/opentracing_test.rb +15 -2
  85. data/test/tracing/processor_test.rb +58 -0
  86. data/test/tracing/span_context_test.rb +22 -0
  87. data/test/tracing/span_test.rb +127 -0
  88. data/test/tracing/tracer_async_test.rb +29 -0
  89. data/test/tracing/tracer_test.rb +82 -16
  90. data/test/util_test.rb +10 -0
  91. metadata +86 -46
  92. data/Dockerfile +0 -16
  93. data/lib/instana/agent.rb +0 -508
  94. data/lib/instana/agent/helpers.rb +0 -87
  95. data/lib/instana/agent/hooks.rb +0 -44
  96. data/lib/instana/agent/tasks.rb +0 -51
  97. data/lib/instana/collector.rb +0 -119
  98. data/lib/instana/collectors/gc.rb +0 -60
  99. data/lib/instana/collectors/memory.rb +0 -37
  100. data/lib/instana/collectors/thread.rb +0 -33
  101. data/lib/instana/eum/eum-test.js.erb +0 -17
  102. data/lib/instana/eum/eum.js.erb +0 -17
  103. data/lib/instana/helpers.rb +0 -47
  104. data/lib/instana/opentracing/tracer.rb +0 -21
  105. data/lib/instana/thread_local.rb +0 -18
  106. data/lib/oj_check.rb +0 -19
  107. data/test/agent/agent_test.rb +0 -151
@@ -5,12 +5,13 @@ module Instana
5
5
  module Instrumentation
6
6
  class Excon < ::Excon::Middleware::Base
7
7
  def request_call(datum)
8
- return @stack.request_call(datum) unless ::Instana.tracer.tracing? || !Instana.tracer.current_span.exit_span?
8
+ return @stack.request_call(datum) unless traceable?
9
9
 
10
10
  payload = { :http => {} }
11
- path = datum[:path].split('?').first
11
+ path, query = datum[:path].split('?', 2)
12
12
  payload[:http][:url] = ::Instana.secrets.remove_from_query("#{datum[:connection].instance_variable_get(:@socket_key)}#{path}")
13
13
  payload[:http][:method] = datum[:method] if datum.key?(:method)
14
+ payload[:http][:params] = ::Instana.secrets.remove_from_query(query || '')
14
15
 
15
16
  if datum[:pipeline] == true
16
17
  # Pass the context along in the datum so we get back on response
@@ -35,7 +36,7 @@ module Instana
35
36
  end
36
37
 
37
38
  def error_call(datum)
38
- return @stack.error_call(datum) unless ::Instana.tracer.tracing? || !Instana.tracer.current_span.exit_span?
39
+ return @stack.error_call(datum) unless traceable?
39
40
 
40
41
  if datum[:pipeline] == true
41
42
  ::Instana.tracer.log_async_error(datum[:error], datum[:instana_span])
@@ -48,7 +49,7 @@ module Instana
48
49
  def response_call(datum)
49
50
  # FIXME: Will connect exceptions call a response?
50
51
  #
51
- return @stack.response_call(datum) unless ::Instana.tracer.tracing? || !Instana.tracer.current_span.exit_span?
52
+ return @stack.response_call(datum) unless traceable?
52
53
 
53
54
  result = @stack.response_call(datum)
54
55
 
@@ -71,6 +72,13 @@ module Instana
71
72
  end
72
73
  result
73
74
  end
75
+
76
+ private
77
+
78
+ def traceable?
79
+ ::Instana.tracer.tracing? &&
80
+ (!Instana.tracer.current_span.exit_span? || Instana.tracer.current_span.name == :excon)
81
+ end
74
82
  end
75
83
  end
76
84
  end
@@ -46,12 +46,17 @@ module Instana
46
46
  headers
47
47
  end
48
48
 
49
+ def request_params
50
+ ::Instana.secrets.remove_from_query(@env['QUERY_STRING'])
51
+ end
52
+
49
53
  def request_tags
50
54
  {
51
55
  method: request_method,
52
56
  url: CGI.unescape(path_info),
53
57
  host: host_with_port,
54
- header: extra_header_tags
58
+ header: extra_header_tags,
59
+ params: request_params
55
60
  }.compact
56
61
  end
57
62
 
@@ -88,7 +93,7 @@ module Instana
88
93
  {
89
94
  external_trace_id: matches['trace'],
90
95
  external_state: @env['HTTP_TRACESTATE'],
91
- trace_id: ::Instana::Util.header_to_id(matches['trace'][16..-1]),
96
+ trace_id: ::Instana::Util.header_to_id(matches['trace'][16..-1]), # rubocop:disable Style/SlicingWithRange, Lint/RedundantCopDisableDirective
92
97
  span_id: ::Instana::Util.header_to_id(matches['parent'])
93
98
  }
94
99
  end
@@ -32,7 +32,9 @@ module Instana
32
32
  kv_payload[:http][:method] = request.method
33
33
 
34
34
  if request.uri
35
- kv_payload[:http][:url] = request.uri.to_s
35
+ uri_without_query = request.uri.dup.tap { |r| r.query = nil }
36
+ kv_payload[:http][:url] = uri_without_query.to_s.gsub(/\?\z/, '')
37
+ kv_payload[:http][:params] = ::Instana.secrets.remove_from_query(request.uri.query)
36
38
  else
37
39
  if use_ssl?
38
40
  kv_payload[:http][:url] = "https://#{@address}:#{@port}#{request.path}"
@@ -41,7 +43,7 @@ module Instana
41
43
  end
42
44
  end
43
45
 
44
- kv_payload[:http][:url] = ::Instana.secrets.remove_from_query(kv_payload[:http][:url])
46
+ kv_payload[:http][:url] = ::Instana.secrets.remove_from_query(kv_payload[:http][:url]).gsub(/\?\z/, '')
45
47
 
46
48
  # The core call
47
49
  response = super(*args, &block)
@@ -0,0 +1,44 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ module Instrumentation
6
+ class Shoryuken
7
+ def call(_worker_instance, _queue, sqs_message, _body, &block)
8
+ if sqs_message.is_a? Array
9
+ return yield
10
+ end
11
+
12
+ sqs_tags = {
13
+ sort: 'entry',
14
+ queue: sqs_message.queue_url
15
+ }
16
+
17
+ context = incomming_context_from(sqs_message.message_attributes)
18
+ ::Instana.tracer.start_or_continue_trace(:sqs, {sqs: sqs_tags}, context, &block)
19
+ end
20
+
21
+ private
22
+
23
+ def incomming_context_from(attributes)
24
+ trace_id = try(attributes, 'X_INSTANA_T', 'X_INSTANA_ST')
25
+ span_id = try(attributes, 'X_INSTANA_S', 'X_INSTANA_SS')
26
+ level = try(attributes, 'X_INSTANA_L', 'X_INSTANA_SL')
27
+
28
+ {
29
+ trace_id: trace_id,
30
+ span_id: span_id,
31
+ level: level
32
+ }.compact
33
+ end
34
+
35
+ def try(attributes, *args)
36
+ key = args.detect do |a|
37
+ attributes && attributes[a] && attributes[a].respond_to?(:string_value)
38
+ end
39
+
40
+ attributes[key].string_value if attributes && key
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,31 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ class LoggerDelegator < SimpleDelegator
6
+ def initialize(obj)
7
+ obj.level = level_from_environment
8
+ super(obj)
9
+ end
10
+
11
+ private
12
+
13
+ def level_from_environment
14
+ # :nocov:
15
+ return Logger::FATAL if ENV.key?('INSTANA_TEST') || ENV.key?('RACK_TEST')
16
+ return Logger::DEBUG if ENV.key?('INSTANA_DEBUG')
17
+
18
+ case ENV['INSTANA_LOG_LEVEL']
19
+ when 'debug'
20
+ Logger::DEBUG
21
+ when 'warn'
22
+ Logger::WARN
23
+ when 'error'
24
+ Logger::ERROR
25
+ else
26
+ Logger::INFO
27
+ end
28
+ # :nocov:
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,99 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ require 'delegate'
5
+ # :nocov:
6
+ module OpenTracing
7
+ class InstanaTracer < SimpleDelegator
8
+ Span = ::Instana::Span
9
+
10
+ # Start a new span
11
+ #
12
+ # @param operation_name [String] The name of the operation represented by the span
13
+ # @param child_of [Span] A span to be used as the ChildOf reference
14
+ # @param start_time [Time] the start time of the span
15
+ # @param tags [Hash] Starting tags for the span
16
+ #
17
+ # @return [Span]
18
+ #
19
+ def start_span(operation_name, child_of: nil, start_time: ::Instana::Util.now_in_ms, tags: nil)
20
+ new_span = if child_of && (child_of.is_a?(::Instana::Span) || child_of.is_a?(::Instana::SpanContext))
21
+ Span.new(operation_name, parent_ctx: child_of, start_time: start_time)
22
+ else
23
+ Span.new(operation_name, start_time: start_time)
24
+ end
25
+ new_span.set_tags(tags) if tags
26
+ new_span
27
+ end
28
+
29
+ # Start a new span which is the child of the current span
30
+ #
31
+ # @param operation_name [String] The name of the operation represented by the span
32
+ # @param child_of [Span] A span to be used as the ChildOf reference
33
+ # @param start_time [Time] the start time of the span
34
+ # @param tags [Hash] Starting tags for the span
35
+ #
36
+ # @return [Span]
37
+ #
38
+ def start_active_span(operation_name, child_of: active_span, start_time: ::Instana::Util.now_in_ms, tags: nil)
39
+ ::Instana.tracer.current_span = start_span(operation_name, child_of: child_of, start_time: start_time, tags: tags)
40
+ block_given? ? yield(::Instana.tracer.current_span) : ::Instana.tracer.current_span
41
+ end
42
+
43
+ # Returns the currently active span
44
+ #
45
+ # @return [Span]
46
+ #
47
+ def active_span
48
+ ::Instana.tracer.current_span
49
+ end
50
+
51
+ # Inject a span into the given carrier
52
+ #
53
+ # @param span_context [SpanContext]
54
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
55
+ # @param carrier [Carrier]
56
+ #
57
+ def inject(span_context, format, carrier)
58
+ case format
59
+ when OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY
60
+ ::Instana.logger.debug 'Unsupported inject format'
61
+ when OpenTracing::FORMAT_RACK
62
+ carrier['X-Instana-T'] = ::Instana::Util.id_to_header(span_context.trace_id)
63
+ carrier['X-Instana-S'] = ::Instana::Util.id_to_header(span_context.span_id)
64
+ else
65
+ ::Instana.logger.debug 'Unknown inject format'
66
+ end
67
+ end
68
+
69
+ # Extract a span from a carrier
70
+ #
71
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
72
+ # @param carrier [Carrier]
73
+ #
74
+ # @return [SpanContext]
75
+ #
76
+ def extract(format, carrier)
77
+ case format
78
+ when OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY
79
+ ::Instana.logger.debug 'Unsupported extract format'
80
+ when OpenTracing::FORMAT_RACK
81
+ ::Instana::SpanContext.new(::Instana::Util.header_to_id(carrier['HTTP_X_INSTANA_T']),
82
+ ::Instana::Util.header_to_id(carrier['HTTP_X_INSTANA_S']))
83
+ else
84
+ ::Instana.logger.debug 'Unknown inject format'
85
+ nil
86
+ end
87
+ end
88
+
89
+ def method_missing(method, *args, &block)
90
+ ::Instana.logger.warn { "You are invoking `#{m}` on Instana::Tracer via OpenTracing." }
91
+ super(method, *args, &block)
92
+ end
93
+
94
+ def respond_to_missing?(*)
95
+ super(method)
96
+ end
97
+ end
98
+ end
99
+ # :nocov:
@@ -6,11 +6,15 @@ require 'cgi'
6
6
 
7
7
  module Instana
8
8
  class Secrets
9
+ def initialize(logger: ::Instana.logger)
10
+ @logger = logger
11
+ end
12
+
9
13
  def remove_from_query(str, secret_values = Instana.agent.secret_values)
10
14
  return str unless secret_values
11
15
 
12
16
  url = URI(str)
13
- params = CGI.parse(url.query || '')
17
+ params = url.scheme ? CGI.parse(url.query || '') : CGI.parse(url.to_s)
14
18
 
15
19
  redacted = params.map do |k, v|
16
20
  needs_redaction = secret_values['list']
@@ -19,7 +23,7 @@ module Instana
19
23
  end
20
24
 
21
25
  url.query = URI.encode_www_form(redacted)
22
- CGI.unescape(url.to_s)
26
+ url.scheme ? CGI.unescape(url.to_s) : CGI.unescape(url.query)
23
27
  end
24
28
 
25
29
  private
@@ -37,8 +41,8 @@ module Instana
37
41
  when 'regex'
38
42
  ->(expected, actual) { !Regexp.new(expected).match(actual).nil? }
39
43
  else
40
- ::Instana.logger.warn("Matcher #{name} is not supported.")
41
- lambda { false }
44
+ @logger.warn("Matcher #{name} is not supported.")
45
+ ->(_e, _a) { false }
42
46
  end
43
47
  end
44
48
  end
data/lib/instana/setup.rb CHANGED
@@ -1,30 +1,39 @@
1
1
  # (c) Copyright IBM Corp. 2021
2
2
  # (c) Copyright Instana Inc. 2016
3
3
 
4
- require 'oj_check'
4
+ require 'instana/logger_delegator'
5
5
 
6
6
  require "instana/base"
7
7
  require "instana/config"
8
- require "instana/agent"
9
- require "instana/collector"
10
8
  require "instana/secrets"
11
9
  require "instana/tracer"
12
10
  require "instana/tracing/processor"
13
11
 
14
12
  require 'instana/activator'
15
13
 
14
+ require 'instana/backend/request_client'
15
+ require 'instana/backend/gc_snapshot'
16
+ require 'instana/backend/process_info'
17
+
18
+ require 'instana/snapshot/deltable'
19
+ require 'instana/snapshot/ruby_process'
20
+ require 'instana/snapshot/fargate_process'
21
+ require 'instana/snapshot/fargate_task'
22
+ require 'instana/snapshot/fargate_container'
23
+ require 'instana/snapshot/docker_container'
24
+
25
+ require 'instana/backend/host_agent_lookup'
26
+ require 'instana/backend/host_agent_activation_observer'
27
+ require 'instana/backend/host_agent_reporting_observer'
28
+
29
+ require 'instana/backend/host_agent'
30
+ require 'instana/backend/serverless_agent'
31
+ require 'instana/backend/agent'
32
+
16
33
  ::Instana.setup
17
34
  ::Instana.agent.setup
18
35
  ::Instana::Activator.start
19
36
 
20
- # Register the metric collectors
21
- unless RUBY_PLATFORM == 'java'.freeze
22
- require 'instana/collectors/gc'
23
- end
24
-
25
- require 'instana/collectors/memory'
26
- require 'instana/collectors/thread'
27
-
28
37
  # Require supported OpenTracing interfaces
29
38
  require "opentracing"
30
39
 
@@ -0,0 +1,25 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ module Snapshot
6
+ # @since 1.197.0
7
+ module Deltable
8
+ def delta(key, *rest, compute:, obj:, path: [key, *rest])
9
+ val = obj[key]
10
+ return val if val == nil
11
+
12
+ if rest.empty?
13
+ @__delta ||= Hash.new(0)
14
+ cache_key = path.join('.')
15
+ old = @__delta[cache_key]
16
+ @__delta[cache_key] = val
17
+
18
+ return compute.call(old, val)
19
+ end
20
+
21
+ delta(*rest, compute: compute, obj: val, path: path)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,151 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ module Snapshot
6
+ # Describes a Docker container visible to the current process
7
+ # @since 1.197.0
8
+ class DockerContainer
9
+ include Deltable
10
+ ID = 'com.instana.plugin.docker'.freeze
11
+
12
+ def initialize(container, metadata_uri: ENV['ECS_CONTAINER_METADATA_URI'])
13
+ @container = container
14
+ @metadata_uri = URI(metadata_uri)
15
+ @client = Backend::RequestClient.new(@metadata_uri.host, @metadata_uri.port, use_ssl: @metadata_uri.scheme == "https")
16
+ end
17
+
18
+ def entity_id
19
+ "#{@container['Labels']['com.amazonaws.ecs.task-arn']}::#{@container['Name']}"
20
+ end
21
+
22
+ def data
23
+ metrics = lookup('/task/stats').fetch(@container['DockerId'], {})
24
+
25
+ container_metrics(metrics)
26
+ .merge(container_metadata)
27
+ end
28
+
29
+ def snapshot
30
+ {
31
+ name: ID,
32
+ entityId: entity_id,
33
+ data: data
34
+ }
35
+ end
36
+
37
+ private
38
+
39
+ def container_metadata
40
+ {
41
+ Id: @container['DockerId'],
42
+ Created: @container['CreatedAt'],
43
+ Started: @container['StartedAt'],
44
+ Image: @container['Image'],
45
+ Labels: @container['Labels'],
46
+ Ports: @container['Ports'],
47
+ NetworkMode: @container['Networks'].first['NetworkMode']
48
+ }
49
+ end
50
+
51
+ def container_metrics(metrics)
52
+ return {} if metrics.empty?
53
+
54
+ {
55
+ memory: memory_stats(metrics),
56
+ blkio: blkio_stats(metrics),
57
+ cpu: cpu_stats(metrics),
58
+ network: network_stats(metrics)
59
+ }.compact
60
+ end
61
+
62
+ def memory_stats(metrics)
63
+ identity = ->(_old, new) { new }
64
+
65
+ {
66
+ active_anon: delta('memory_stats', 'stats', 'active_anon', compute: identity, obj: metrics),
67
+ active_file: delta('memory_stats', 'stats', 'active_file', compute: identity, obj: metrics),
68
+ inactive_anon: delta('memory_stats', 'stats', 'inactive_anon', compute: identity, obj: metrics),
69
+ inactive_file: delta('memory_stats', 'stats', 'inactive_file', compute: identity, obj: metrics),
70
+ total_cache: delta('memory_stats', 'stats', 'total_cache', compute: identity, obj: metrics),
71
+ total_rss: delta('memory_stats', 'stats', 'total_rss', compute: identity, obj: metrics),
72
+ usage: delta('memory_stats', 'usage', compute: identity, obj: metrics),
73
+ max_usage: delta('memory_stats', 'max_usage', compute: identity, obj: metrics),
74
+ limit: delta('memory_stats', 'limit', compute: identity, obj: metrics)
75
+ }
76
+ end
77
+
78
+ def blkio_stats(metrics)
79
+ delta = ->(old, new) { new - old }
80
+ bytes = {
81
+ 'block_bytes' => metrics['blkio_stats']['io_service_bytes_recursive'].map { |r| [r['op'], r['value']] }.to_h
82
+ }
83
+
84
+ {
85
+ blk_read: delta('block_bytes', 'Read', compute: delta, obj: bytes),
86
+ blk_write: delta('block_bytes', 'Write', compute: delta, obj: bytes)
87
+ }
88
+ end
89
+
90
+ def cpu_stats(metrics)
91
+ delta = ->(old, new) { new - old }
92
+ identity = ->(_old, new) { new }
93
+
94
+ cpu_system_delta = delta('cpu_stats', 'system_cpu_usage', compute: delta, obj: metrics).to_f
95
+ online_cpus = delta('cpu_stats', 'online_cpus', compute: identity, obj: metrics) || 1
96
+
97
+ {
98
+ total_usage: (delta('cpu_stats', 'cpu_usage', 'total_usage', compute: delta, obj: metrics) / cpu_system_delta) * online_cpus,
99
+ user_usage: (delta('cpu_stats', 'cpu_usage', 'usage_in_usermode', compute: delta, obj: metrics) / cpu_system_delta) * online_cpus,
100
+ system_usage: (delta('cpu_stats', 'cpu_usage', 'usage_in_kernelmode', compute: delta, obj: metrics) / cpu_system_delta) * online_cpus,
101
+ throttling_count: delta('cpu_stats', 'throttling_data', 'periods', compute: delta, obj: metrics),
102
+ throttling_time: delta('cpu_stats', 'throttling_data', 'throttled_time', compute: delta, obj: metrics)
103
+ }
104
+ end
105
+
106
+ def network_stats(metrics)
107
+ delta = ->(old, new) { new - old }
108
+ return nil unless metrics['networks']
109
+
110
+ interfaces = metrics['networks'].keys
111
+ payload = {
112
+ rx: {
113
+ bytes: 0,
114
+ dropped: 0,
115
+ errors: 0,
116
+ packet: 0
117
+ },
118
+ tx: {
119
+ bytes: 0,
120
+ dropped: 0,
121
+ errors: 0,
122
+ packet: 0
123
+ }
124
+ }
125
+
126
+ interfaces.each do |interface|
127
+ payload[:rx][:bytes] += delta('networks', interface, 'rx_bytes', compute: delta, obj: metrics)
128
+ payload[:rx][:dropped] += delta('networks', interface, 'rx_dropped', compute: delta, obj: metrics)
129
+ payload[:rx][:errors] += delta('networks', interface, 'rx_errors', compute: delta, obj: metrics)
130
+ payload[:rx][:packet] += delta('networks', interface, 'rx_packets', compute: delta, obj: metrics)
131
+
132
+ payload[:tx][:bytes] += delta('networks', interface, 'tx_bytes', compute: delta, obj: metrics)
133
+ payload[:tx][:dropped] += delta('networks', interface, 'tx_packets', compute: delta, obj: metrics)
134
+ payload[:tx][:errors] += delta('networks', interface, 'tx_errors', compute: delta, obj: metrics)
135
+ payload[:tx][:packet] += delta('networks', interface, 'tx_dropped', compute: delta, obj: metrics)
136
+ end
137
+
138
+ payload
139
+ end
140
+
141
+ def lookup(resource)
142
+ path = @metadata_uri.path + resource
143
+ response = @client.send_request('GET', path)
144
+
145
+ raise "Unable to get `#{path}`. Got `#{response.code}`." unless response.ok?
146
+
147
+ response.json
148
+ end
149
+ end
150
+ end
151
+ end