elastic-apm 2.6.1 → 2.7.0

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.

Potentially problematic release.


This version of elastic-apm might be problematic. Click here for more details.

@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'http'
4
+ require 'concurrent'
5
+ require 'zlib'
6
+
7
+ require 'elastic_apm/transport/connection/proxy_pipe'
8
+
9
+ module ElasticAPM
10
+ module Transport
11
+ class Connection
12
+ # @api private
13
+ class Http
14
+ include Logging
15
+
16
+ def initialize(config)
17
+ @config = config
18
+ @closed = Concurrent::AtomicBoolean.new
19
+
20
+ @rd, @wr = ProxyPipe.pipe(compress: @config.http_compression?)
21
+ end
22
+
23
+ def open(url, headers: {}, ssl_context: nil)
24
+ @request = open_request_in_thread(url, headers, ssl_context)
25
+ end
26
+
27
+ def self.open(config, url, headers: {}, ssl_context: nil)
28
+ new(config).tap do |http|
29
+ http.open(url, headers: headers, ssl_context: ssl_context)
30
+ end
31
+ end
32
+
33
+ def write(str)
34
+ @wr.write(str)
35
+ @wr.bytes_sent
36
+ end
37
+
38
+ def close(reason)
39
+ return if closed?
40
+
41
+ debug '%s: Closing request with reason %s', thread_str, reason
42
+ @closed.make_true
43
+
44
+ @wr&.close(reason)
45
+ return if @request.nil? || @request&.join(5)
46
+
47
+ error(
48
+ '%s: APM Server not responding in time, terminating request',
49
+ thread_str
50
+ )
51
+ @request.kill
52
+ end
53
+
54
+ def closed?
55
+ @closed.true?
56
+ end
57
+
58
+ def inspect
59
+ format(
60
+ '%s closed: %s>',
61
+ super.split.first,
62
+ closed?
63
+ )
64
+ end
65
+
66
+ private
67
+
68
+ def thread_str
69
+ format('[THREAD:%s]', Thread.current.object_id)
70
+ end
71
+
72
+ # rubocop:disable Metrics/LineLength
73
+ def open_request_in_thread(url, headers, ssl_context)
74
+ client = build_client(headers)
75
+
76
+ debug '%s: Opening new request', thread_str
77
+ Thread.new do
78
+ begin
79
+ post(client, url, ssl_context)
80
+ rescue Exception => e
81
+ error "Couldn't establish connection to APM Server:\n%p", e.inspect
82
+ end
83
+ end
84
+ end
85
+ # rubocop:enable Metrics/LineLength
86
+
87
+ def build_client(headers)
88
+ client = HTTP.headers(headers)
89
+ return client unless @config.proxy_address && @config.proxy_port
90
+
91
+ client.via(
92
+ @config.proxy_address,
93
+ @config.proxy_port,
94
+ @config.proxy_username,
95
+ @config.proxy_password,
96
+ @config.proxy_headers
97
+ )
98
+ end
99
+
100
+ def post(client, url, ssl_context)
101
+ resp = client.post(
102
+ url,
103
+ body: @rd,
104
+ ssl_context: ssl_context
105
+ ).flush
106
+
107
+ if resp&.status == 202
108
+ debug 'APM Server responded with status 202'
109
+ elsif resp
110
+ error "APM Server responded with an error:\n%p", resp.body.to_s
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'concurrent'
4
+ require 'zlib'
5
+
6
+ module ElasticAPM
7
+ module Transport
8
+ class Connection
9
+ # @api private
10
+ class ProxyPipe
11
+ def initialize(enc = nil, compress: true)
12
+ @read, wr = IO.pipe(enc)
13
+ @write = Write.new(wr, compress: compress)
14
+ end
15
+
16
+ attr_reader :read, :write
17
+
18
+ # @api private
19
+ class Write
20
+ include Logging
21
+
22
+ def initialize(io, compress: true)
23
+ @io = io
24
+ @compress = compress
25
+ @bytes_sent = Concurrent::AtomicFixnum.new(0)
26
+ @config = ElasticAPM.agent&.config # this is silly, fix Logging
27
+
28
+ return unless compress
29
+ enable_compression!
30
+ end
31
+
32
+ attr_reader :io
33
+
34
+ def enable_compression!
35
+ io.binmode
36
+ @io = Zlib::GzipWriter.new(io)
37
+ end
38
+
39
+ def close(reason = nil)
40
+ debug("Closing writer with reason #{reason}")
41
+ io.close
42
+ end
43
+
44
+ def closed?
45
+ io.closed?
46
+ end
47
+
48
+ def write(str)
49
+ io.puts(str).tap do
50
+ @bytes_sent.update do |curr|
51
+ @compress ? io.tell : curr + str.bytesize
52
+ end
53
+ end
54
+ end
55
+
56
+ def bytes_sent
57
+ @bytes_sent.value
58
+ end
59
+ end
60
+
61
+ def self.pipe(*args)
62
+ pipe = new(*args)
63
+ [pipe.read, pipe.write]
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -29,6 +29,7 @@ module ElasticAPM
29
29
 
30
30
  def call(payload)
31
31
  strip_from! payload.dig(:transaction, :context, :request, :headers)
32
+ strip_from! payload.dig(:transaction, :context, :request, :env)
32
33
  strip_from! payload.dig(:transaction, :context, :response, :headers)
33
34
  strip_from! payload.dig(:error, :context, :request, :headers)
34
35
  strip_from! payload.dig(:error, :context, :response, :headers)
@@ -55,7 +55,8 @@ module ElasticAPM
55
55
  {
56
56
  hostname: keyword_field(system.hostname),
57
57
  architecture: keyword_field(system.architecture),
58
- platform: keyword_field(system.platform)
58
+ platform: keyword_field(system.platform),
59
+ kubernetes: keyword_object(system.kubernetes)
59
60
  }
60
61
  end
61
62
  end
@@ -22,8 +22,6 @@ module ElasticAPM
22
22
  @config = config
23
23
  @queue = queue
24
24
 
25
- @stopping = false
26
-
27
25
  @serializers = serializers
28
26
  @filters = filters
29
27
 
@@ -33,29 +31,17 @@ module ElasticAPM
33
31
 
34
32
  attr_reader :queue, :filters, :name, :connection, :serializers
35
33
 
36
- def stop
37
- @stopping = true
38
- end
39
-
40
- def stopping?
41
- @stopping
42
- end
43
-
44
34
  # rubocop:disable Metrics/MethodLength
45
35
  def work_forever
46
36
  while (msg = queue.pop)
47
37
  case msg
48
38
  when StopMessage
49
- stop
39
+ debug 'Stopping worker -- %s', self
40
+ connection.flush(:halt)
41
+ break
50
42
  else
51
43
  process msg
52
44
  end
53
-
54
- next unless stopping?
55
-
56
- debug 'Stopping worker -- %s', self
57
- @connection.flush
58
- break
59
45
  end
60
46
  rescue Exception => e
61
47
  warn 'Worker died with exception: %s', e.inspect
@@ -63,12 +49,21 @@ module ElasticAPM
63
49
  end
64
50
  # rubocop:enable Metrics/MethodLength
65
51
 
52
+ def process(resource)
53
+ return unless (json = serialize_and_filter(resource))
54
+ connection.write(json)
55
+ end
56
+
66
57
  private
67
58
 
68
- def process(resource)
59
+ def serialize_and_filter(resource)
69
60
  serialized = serializers.serialize(resource)
70
61
  @filters.apply!(serialized)
71
- @connection.write(serialized.to_json)
62
+ JSON.fast_generate(serialized)
63
+ rescue Exception
64
+ error format('Failed converting event to JSON: %s', resource.inspect)
65
+ error serialized.inspect
66
+ nil
72
67
  end
73
68
  end
74
69
  end
@@ -21,8 +21,10 @@ module ElasticAPM
21
21
  str.hex.to_s(2).rjust(str.size * 4, '0')
22
22
  end
23
23
 
24
- def self.reverse_merge!(first, second)
25
- first.merge!(second) { |_, old, _| old }
24
+ def self.reverse_merge!(first, *others)
25
+ others.reduce(first) do |curr, other|
26
+ curr.merge!(other) { |_, _, new| new }
27
+ end
26
28
  end
27
29
 
28
30
  def self.truncate(value, max_length: 1024)
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticAPM
4
+ module Util
5
+ # @api private
6
+
7
+ # Usage example:
8
+ # Throttle.new(5) { thing to only do once per 5 secs }
9
+ class Throttle
10
+ def initialize(buffer_secs, &block)
11
+ @buffer_secs = buffer_secs
12
+ @block = block
13
+ end
14
+
15
+ def call
16
+ if @last_call && seconds_since_last_call < @buffer_secs
17
+ return @last_result
18
+ end
19
+
20
+ @last_call = now
21
+ @last_result = @block.call
22
+ end
23
+
24
+ private
25
+
26
+ def now
27
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
28
+ end
29
+
30
+ def seconds_since_last_call
31
+ now - @last_call
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElasticAPM
4
- VERSION = '2.6.1'
4
+ VERSION = '2.7.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elastic-apm
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.1
4
+ version: 2.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikkel Malmberg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-28 00:00:00.000000000 Z
11
+ date: 2019-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -79,6 +79,7 @@ files:
79
79
  - docs/introduction.asciidoc
80
80
  - docs/metrics.asciidoc
81
81
  - docs/opentracing.asciidoc
82
+ - docs/release-notes.asciidoc
82
83
  - docs/supported-technologies.asciidoc
83
84
  - elastic-apm.gemspec
84
85
  - lib/elastic-apm.rb
@@ -146,6 +147,8 @@ files:
146
147
  - lib/elastic_apm/transaction.rb
147
148
  - lib/elastic_apm/transport/base.rb
148
149
  - lib/elastic_apm/transport/connection.rb
150
+ - lib/elastic_apm/transport/connection/http.rb
151
+ - lib/elastic_apm/transport/connection/proxy_pipe.rb
149
152
  - lib/elastic_apm/transport/filters.rb
150
153
  - lib/elastic_apm/transport/filters/secrets_filter.rb
151
154
  - lib/elastic_apm/transport/serializers.rb
@@ -160,6 +163,7 @@ files:
160
163
  - lib/elastic_apm/util/inflector.rb
161
164
  - lib/elastic_apm/util/lru_cache.rb
162
165
  - lib/elastic_apm/util/prefixed_logger.rb
166
+ - lib/elastic_apm/util/throttle.rb
163
167
  - lib/elastic_apm/version.rb
164
168
  - vendor/.gitkeep
165
169
  homepage: https://github.com/elastic/apm-agent-ruby