baseline_red_rpm 1.0.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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/lib/baseline_red_rpm.rb +164 -0
  3. data/lib/baseline_red_rpm/backtrace.rb +117 -0
  4. data/lib/baseline_red_rpm/configuration.rb +63 -0
  5. data/lib/baseline_red_rpm/instrumentation.rb +23 -0
  6. data/lib/baseline_red_rpm/instruments/action_controller.rb +70 -0
  7. data/lib/baseline_red_rpm/instruments/action_view.rb +222 -0
  8. data/lib/baseline_red_rpm/instruments/active_model_serializer.rb +37 -0
  9. data/lib/baseline_red_rpm/instruments/active_record.rb +66 -0
  10. data/lib/baseline_red_rpm/instruments/active_record/adapters/mysql2.rb +55 -0
  11. data/lib/baseline_red_rpm/instruments/active_record/adapters/postgresql.rb +143 -0
  12. data/lib/baseline_red_rpm/instruments/active_record/adapters/sqlite3.rb +142 -0
  13. data/lib/baseline_red_rpm/instruments/activerecord_import.rb +57 -0
  14. data/lib/baseline_red_rpm/instruments/emque_consuming.rb +41 -0
  15. data/lib/baseline_red_rpm/instruments/faraday.rb +48 -0
  16. data/lib/baseline_red_rpm/instruments/grape.rb +63 -0
  17. data/lib/baseline_red_rpm/instruments/net_http.rb +43 -0
  18. data/lib/baseline_red_rpm/instruments/rack.rb +129 -0
  19. data/lib/baseline_red_rpm/instruments/redis.rb +75 -0
  20. data/lib/baseline_red_rpm/instruments/roda.rb +48 -0
  21. data/lib/baseline_red_rpm/instruments/sequel.rb +100 -0
  22. data/lib/baseline_red_rpm/instruments/sidekiq.rb +100 -0
  23. data/lib/baseline_red_rpm/instruments/sinatra.rb +82 -0
  24. data/lib/baseline_red_rpm/instruments/typhoeus.rb +74 -0
  25. data/lib/baseline_red_rpm/introspector.rb +53 -0
  26. data/lib/baseline_red_rpm/logger.rb +34 -0
  27. data/lib/baseline_red_rpm/rails.rb +15 -0
  28. data/lib/baseline_red_rpm/railtie.rb +19 -0
  29. data/lib/baseline_red_rpm/reporters/json_client.rb +69 -0
  30. data/lib/baseline_red_rpm/reporters/null_client.rb +16 -0
  31. data/lib/baseline_red_rpm/tracer.rb +75 -0
  32. data/lib/baseline_red_rpm/tracing/buffer.rb +27 -0
  33. data/lib/baseline_red_rpm/tracing/carrier.rb +25 -0
  34. data/lib/baseline_red_rpm/tracing/collector.rb +33 -0
  35. data/lib/baseline_red_rpm/tracing/endpoint.rb +21 -0
  36. data/lib/baseline_red_rpm/tracing/managed_span.rb +40 -0
  37. data/lib/baseline_red_rpm/tracing/managed_tracer.rb +36 -0
  38. data/lib/baseline_red_rpm/tracing/span.rb +72 -0
  39. data/lib/baseline_red_rpm/tracing/span_context.rb +43 -0
  40. data/lib/baseline_red_rpm/tracing/thread_span_stack.rb +34 -0
  41. data/lib/baseline_red_rpm/tracing/trace_id.rb +13 -0
  42. data/lib/baseline_red_rpm/tracing/tracer.rb +100 -0
  43. data/lib/baseline_red_rpm/utils.rb +45 -0
  44. data/lib/tasks/install.rake +6 -0
  45. metadata +212 -0
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BaselineRedRpm
4
+ class Introspector
5
+
6
+ VALID_RUNNERS = [
7
+ :PhusionPassenger,
8
+ :Puma,
9
+ :Rainbows,
10
+ :Resque,
11
+ :Sidekiq,
12
+ :Sinatra,
13
+ :Unicorn,
14
+ :Webrick
15
+ ]
16
+ class << self
17
+
18
+ def agentable?
19
+ if raking? || rspecing?
20
+ BaselineRedRpm.logger.info("Detected rake, not initializing agent")
21
+ return false
22
+ end
23
+ BaselineRedRpm.logger.info("Detecting runner...")
24
+ VALID_RUNNERS.each do |runner|
25
+ if const_defined?(runner.to_s)
26
+ BaselineRedRpm.logger.info("#{runner} detected. You're valid")
27
+ return true
28
+ end
29
+ end
30
+ BaselineRedRpm.logger.info("No valid runner detected!")
31
+ false
32
+ end
33
+
34
+ def rspecing?
35
+ (File.basename($0) =~ /\Arspec/) == 0
36
+ end
37
+
38
+ def raking?
39
+ (File.basename($0) =~ /\Arake/) == 0
40
+ end
41
+
42
+ def const_defined?(string_const)
43
+ begin
44
+ Object.const_get(string_const)
45
+ true
46
+ rescue NameError
47
+ false
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module BaselineRedRpm
6
+ class << self
7
+ attr_accessor :logger
8
+ end
9
+
10
+ class Logger
11
+ def info(msg)
12
+ BaselineRedRpm.info(msg)
13
+ end
14
+
15
+ def debug(msg)
16
+ BaselineRedRpm.info(msg)
17
+ end
18
+
19
+ def warn(msg)
20
+ BaselineRedRpm.info(msg)
21
+ end
22
+
23
+ def error(msg)
24
+ BaselineRedRpm.info(msg)
25
+ end
26
+
27
+ def fatal(msg)
28
+ BaselineRedRpm.info(msg)
29
+ end
30
+ end
31
+ end
32
+
33
+ BaselineRedRpm.logger = Logger.new(STDERR)
34
+ BaselineRedRpm.logger.level = Logger::INFO
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(::Rails)
4
+ if ::Rails::VERSION::MAJOR > 2
5
+ require 'baseline_red_rpm/railtie'
6
+ else
7
+ Rails.configuration.after_initialize do
8
+ unless BaselineRedRpm.disable_agent?
9
+ BaselineRedRpm.load
10
+ BaselineRedRpm.logger.info "Initializing rack middleware tracer."
11
+ Rails.configuration.middleware.insert 0, BaselineRedRpm::Instruments::Rack
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BaselineRedRpm
4
+ class Railtie < ::Rails::Railtie
5
+ initializer "baseline_red.initialize" do |app|
6
+ unless BaselineRedRpm.disable_agent?
7
+ require 'baseline_red_rpm/instruments/rack'
8
+ BaselineRedRpm.logger.info "Initializing rack middleware tracer."
9
+ app.middleware.insert 0, BaselineRedRpm::Instruments::Rack
10
+ end
11
+
12
+ config.after_initialize do
13
+ BaselineRedRpm.config.app_root = Rails.root
14
+ BaselineRedRpm.config.reload
15
+ BaselineRedRpm.load
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'base64'
6
+
7
+ module BaselineRedRpm
8
+ module Reporters
9
+ class JsonClient
10
+ def initialize(opts = { :url => nil, :collector => nil, :flush_interval => nil })
11
+ @collector = opts[:collector]
12
+ @flush_interval = opts[:flush_interval]
13
+ @spans_uri = URI.parse(opts[:url])
14
+ end
15
+
16
+ def start
17
+ @thread = Thread.new do
18
+ loop do
19
+ emit_batch(@collector.retrieve)
20
+ sleep @flush_interval
21
+ end
22
+ end
23
+ end
24
+
25
+ def stop
26
+ @thread.terminate if @thread
27
+ emit_batch(@collector.retrieve)
28
+ end
29
+
30
+ private
31
+
32
+ def compress_body(data)
33
+ body = MessagePack.pack({
34
+ "name" => BaselineRedRpm.config.application_name,
35
+ "host" => BaselineRedRpm.host,
36
+ "data" => data
37
+ })
38
+
39
+ compressed_body = Zlib::Deflate.deflate(body, Zlib::DEFAULT_COMPRESSION)
40
+ Base64.encode64(compressed_body)
41
+ end
42
+
43
+ def emit_batch(spans)
44
+ return if spans.empty?
45
+
46
+ sock = Net::HTTP.new(@spans_uri.host, @spans_uri.port)
47
+ sock.use_ssl = ::BaselineRedRpm.config.ssl
48
+
49
+ request = Net::HTTP::Post.new(@spans_uri.request_uri, {
50
+ "Accept-Encoding" => "gzip",
51
+ "User-Agent" => "gzip"
52
+ })
53
+ request.body = compress_body(spans)
54
+ request.content_type = "application/octet-stream"
55
+
56
+ response = sock.start do |http|
57
+ http.read_timeout = 30
58
+ http.request(request)
59
+ end
60
+
61
+ if response.code != 202
62
+ STDERR.puts(response.body)
63
+ end
64
+ rescue => e
65
+ STDERR.puts("Error emitting spans batch: #{e.message}\n#{e.backtrace.join("\n")}")
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BaselineRedRpm
4
+ module Reporters
5
+ class NullClient
6
+ def initialize
7
+ end
8
+
9
+ def start
10
+ end
11
+
12
+ def stop
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BaselineRedRpm
4
+ class Tracer
5
+ class << self
6
+ # This method should be called by any components that are
7
+ # capable of starting the tracing process.
8
+ # ie. rack, sidekiq worker, etc
9
+ def sample!(incoming_trace = nil, force = false)
10
+ # Since we keep track of the active span, meaning we have entered into
11
+ # tracing at some point, and we no longer have an active span,
12
+ # reset tracing.
13
+ sample_off! if !BaselineRedRpm.tracer.active_span
14
+
15
+ # Now determine if we want to trace, either by an incoming
16
+ # trace or meeting the sample rate.
17
+ Thread.current[:sample] = force || !!incoming_trace || should_sample?
18
+ Thread.current[:sample]
19
+ end
20
+
21
+ def sample_off!
22
+ Thread.current[:sample] = false
23
+ end
24
+
25
+ def sampled?
26
+ !!Thread.current[:sample]
27
+ end
28
+
29
+ def tracing?
30
+ BaselineRedRpm.tracing? && sampled?
31
+ end
32
+
33
+ def random_percentage
34
+ rand * 100
35
+ end
36
+
37
+ def should_sample?
38
+ random_percentage <= ::BaselineRedRpm.config.sample_rate.to_i
39
+ end
40
+
41
+ def profile(layer, opts = {})
42
+ if defined?(TracePoint)
43
+ @times = {}
44
+ traces = []
45
+ tracer = TracePoint.new(:call, :return) do |tp|
46
+ backtrace = caller(0)
47
+ key = "#{tp.defined_class}_#{tp.method_id}_#{backtrace.size}"
48
+ if tp.event == :call
49
+ @times[key] = Time.now.to_f
50
+ else
51
+ if @times[key]
52
+ @times[key] = Time.now.to_f - @times[key].to_f
53
+ traces << {
54
+ "duration "=> @times[key].to_f,
55
+ "class" => tp.defined_class,
56
+ "method" => tp.method_id,
57
+ "backtrace" => backtrace,
58
+ "line" => ::BaselineRedRpm::Backtrace.send(:clean_line, tp.path),
59
+ "line_number" => tp.lineno
60
+ }
61
+ end
62
+ end
63
+ end
64
+
65
+ result = tracer.enable { yield }
66
+ @times = {}
67
+
68
+ return traces, result
69
+ else
70
+ return [], yield
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BaselineRedRpm
4
+ module Tracing
5
+ class Buffer
6
+ def initialize
7
+ @buffer = []
8
+ @mutex = Mutex.new
9
+ end
10
+
11
+ def <<(element)
12
+ @mutex.synchronize do
13
+ @buffer << element
14
+ true
15
+ end
16
+ end
17
+
18
+ def retrieve
19
+ @mutex.synchronize do
20
+ elements = @buffer.dup
21
+ @buffer.clear
22
+ elements
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BaselineRedRpm
4
+ module Tracing
5
+ class Carrier
6
+ def initialize
7
+ @data = {}
8
+ end
9
+
10
+ def [](key)
11
+ @data[key]
12
+ end
13
+
14
+ def []=(key, value)
15
+ @data[key] = value
16
+ end
17
+
18
+ def each(&block)
19
+ @data.each do |datum|
20
+ yield(datum)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BaselineRedRpm
4
+ module Tracing
5
+ class Collector
6
+ attr_reader :buffer
7
+
8
+ def initialize(local_endpoint)
9
+ @buffer = Buffer.new
10
+ @local_endpoint = local_endpoint
11
+ end
12
+
13
+ def retrieve
14
+ @buffer.retrieve
15
+ end
16
+
17
+ def send_span(span, end_time)
18
+ duration = end_time - span.start_time
19
+
20
+ @buffer << {
21
+ "traceId" => span.context.trace_id,
22
+ "id" => span.context.span_id,
23
+ "parentId" => span.context.parent_id,
24
+ "name" => span.operation_name,
25
+ "timestamp" => span.start_time,
26
+ "duration" => duration * 1_000,
27
+ "logEntries" => span.log_entries,
28
+ "tags" => span.tags
29
+ }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+
5
+ module BaselineRedRpm
6
+ module Tracing
7
+ class Endpoint
8
+ LOCAL_IP = (
9
+ Socket.ip_address_list.detect(&:ipv4_private?) ||
10
+ Socket.ip_address_list.reverse.detect(&:ipv4?)
11
+ ).ip_address
12
+
13
+ def self.local_endpoint(service_name)
14
+ {
15
+ "serviceName" => service_name,
16
+ "ipv4" => LOCAL_IP
17
+ }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BaselineRedRpm
4
+ module Tracing
5
+ class ManagedSpan < Span
6
+ extend Forwardable
7
+
8
+ def_delegators :@span, :context, :operation_name=, :set_tag, :set_baggage_item, :get_baggage_item, :log, :finish
9
+
10
+ def initialize(span, deactivate)
11
+ @span = span
12
+ @deactivate = deactivate.respond_to?(:call) ? deactivate : nil
13
+ @active = true
14
+ end
15
+
16
+ def wrapped
17
+ @span
18
+ end
19
+
20
+ def active?
21
+ @active
22
+ end
23
+
24
+ def deactivate
25
+ if @active && @deactivate
26
+ deactivated_span = @deactivate.call
27
+ warn "ActiveSpan::SpanSource inconsistency found during deactivation" unless deactivated_span == self
28
+ @active = false
29
+ end
30
+ end
31
+
32
+ def finish(opts = {})
33
+ opts[:end_time] ||= BaselineRedRpm.now
34
+
35
+ deactivate
36
+ @span.finish(end_time: opts[:end_time])
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BaselineRedRpm
4
+ module Tracing
5
+ class ManagedTracer
6
+ extend Forwardable
7
+ def_delegators :@tracer, :inject, :extract
8
+
9
+ attr_reader :thread_span_stack
10
+
11
+ def initialize(tracer, thread_span_stack = ThreadSpanStack.new)
12
+ @tracer = tracer
13
+ @thread_span_stack = thread_span_stack
14
+ end
15
+
16
+ def wrapped
17
+ @tracer
18
+ end
19
+
20
+ def collector
21
+ @tracer.collector
22
+ end
23
+
24
+ def active_span
25
+ thread_span_stack.active_span
26
+ end
27
+
28
+ def start_span(operation_name, opts = {}, *args)
29
+ opts[:child_of] ||= active_span
30
+
31
+ span = @tracer.start_span(operation_name, opts, *args)
32
+ @thread_span_stack.set_active_span(span)
33
+ end
34
+ end
35
+ end
36
+ end