elastic-apm 2.6.1 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.

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