splunk-tracer 0.1.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.
@@ -0,0 +1,54 @@
1
+ require 'bundler/setup'
2
+ require 'splunktracing'
3
+
4
+ SplunkTracing.configure(component_name: 'splunktracing/ruby/example', access_token: '{your_access_token}')
5
+
6
+ puts 'Starting...'
7
+
8
+ mutex = Mutex.new
9
+ done = false
10
+ span_count = 0
11
+ percent_done = 0
12
+ total_time = 0
13
+
14
+ watchThread = Thread.new do
15
+ loop do
16
+ sleep(0.5)
17
+ mutex.lock
18
+ time_per_span = (1e6 * (total_time.to_f / span_count.to_f)).round(2)
19
+ puts "#{span_count} spans #{percent_done}% done #{total_time.round(2)} seconds (#{time_per_span} µs/span)"
20
+ is_done = done
21
+ mutex.unlock
22
+ Thread.exit if is_done
23
+ end
24
+ end
25
+
26
+ thread = Thread.new do
27
+ count = 0
28
+ total_time = 0
29
+ for j in 1..1000
30
+ start = Time.now
31
+ for i in 1..100
32
+ span = SplunkTracing.start_span('my_span')
33
+ span.log(event: 'hello world', count: i)
34
+ span.finish
35
+ count += 1
36
+ end
37
+ delta = Time.now - start
38
+
39
+ mutex.lock
40
+ percent_done = (100.0 * (count / 100_000.0)).ceil
41
+ span_count = count
42
+ total_time += delta
43
+ mutex.unlock
44
+ end
45
+ end
46
+
47
+ thread.join
48
+ mutex.lock
49
+ done = true
50
+ mutex.unlock
51
+ watchThread.join
52
+
53
+ puts 'Done!'
54
+ SplunkTracing.flush
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'splunktracing'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.2.3
@@ -0,0 +1,33 @@
1
+ require 'bundler/setup'
2
+ require 'simplecov'
3
+ SimpleCov.command_name 'example.rb'
4
+ SimpleCov.start
5
+ require 'splunktracing'
6
+
7
+ access_token = '08243c00-a31b-499d-9fae-776b41990997'
8
+
9
+ SplunkTracing.configure(component_name: 'splunktracing/ruby/example', access_token: access_token)
10
+
11
+ puts 'Starting operation...'
12
+ span = SplunkTracing.start_span('my_span')
13
+ thread1 = Thread.new do
14
+ (1..10).each do |i|
15
+ sleep(0.15)
16
+ puts "Logging event #{i}..."
17
+ span.log(event: 'hello world', count: i)
18
+ end
19
+ end
20
+ thread2 = Thread.new do
21
+ current = 1
22
+ (1..16).each do |i|
23
+ child = SplunkTracing.start_span('my_child', child_of: span.span_context)
24
+ sleep(0.1)
25
+ current *= 2
26
+ child.log(event: "2^#{i}", result: current)
27
+ child.finish
28
+ end
29
+ end
30
+ [thread1, thread2].each(&:join)
31
+ span.finish
32
+ SplunkTracing.flush
33
+ puts 'Done!'
@@ -0,0 +1,54 @@
1
+ # A simple, manual test ensuring that tracer instances still report after a
2
+ # Process.fork.
3
+
4
+ require 'bundler/setup'
5
+ require 'splunktracing'
6
+
7
+ SplunkTracing.configure(
8
+ component_name: 'splunktracing/ruby/examples/fork_children',
9
+ access_token: '{your_access_token}'
10
+ )
11
+
12
+ puts 'Starting...'
13
+ (1..20).each do |k|
14
+ puts "Explicit reset iteration #{k}..."
15
+
16
+ pid = Process.fork do
17
+ 10.times do
18
+ span = SplunkTracing.start_span("my_forked_span-#{Process.pid}")
19
+ sleep(0.0025 * rand(k))
20
+ span.finish
21
+ end
22
+ SplunkTracing.flush
23
+ end
24
+
25
+ 3.times do
26
+ span = SplunkTracing.start_span("my_process_span-#{Process.pid}")
27
+ sleep(0.0025 * rand(k))
28
+ span.set_tag(:empty, "")
29
+ span.set_tag(:full, "full")
30
+ span.finish
31
+ end
32
+
33
+ # Make sure redundant enable calls don't cause problems
34
+ # NOTE: disabling discards the buffer by default, so all spans
35
+ # get cleared here except the final toggle span
36
+ 10.times do
37
+ SplunkTracing.disable
38
+ SplunkTracing.enable
39
+ SplunkTracing.disable
40
+ SplunkTracing.disable
41
+ SplunkTracing.enable
42
+ SplunkTracing.enable
43
+ span = SplunkTracing.start_span("my_toggle_span-#{Process.pid}")
44
+ sleep(0.0025 * rand(k))
45
+ span.finish
46
+ end
47
+
48
+ puts "Parent, pid #{Process.pid}, waiting on child pid #{pid}"
49
+ Process.wait(pid)
50
+ end
51
+
52
+ puts 'Done!'
53
+
54
+ SplunkTracing.flush
@@ -0,0 +1,22 @@
1
+ require 'bundler/setup'
2
+ require 'splunktracing'
3
+
4
+ require 'rack'
5
+ require 'rack/server'
6
+
7
+ SplunkTracing.configure(
8
+ component_name: 'splunktracing/ruby/examples/rack',
9
+ access_token: '{your_access_token}'
10
+ )
11
+
12
+ class HelloWorldApp
13
+ def self.call(env)
14
+ span = SplunkTracing.start_span('request',tags: {name: "G"})
15
+ span.log event: 'env', env: env
16
+ resp = [200, {}, ["Hello World. You said: #{env['QUERY_STRING']}"]]
17
+ span.finish
18
+ resp
19
+ end
20
+ end
21
+
22
+ Rack::Server.start app: HelloWorldApp
@@ -0,0 +1,68 @@
1
+ require 'bundler/setup'
2
+ require 'splunktracing'
3
+ require 'opentracing'
4
+
5
+ require 'rack'
6
+ require 'rack/server'
7
+
8
+ $token = '{your_access_token}'
9
+ $request_id = 'abc123'
10
+
11
+ class Router
12
+ def initialize
13
+ @tracer = SplunkTracing::Tracer.new(component_name: 'router', access_token: $token)
14
+ end
15
+
16
+ def call(env)
17
+ span = @tracer.start_span("router_call").set_baggage_item("request-id", $request_id)
18
+ span.log(event: "router_request", env: env)
19
+ puts "parent #{span.span_context.trace_id}"
20
+
21
+ client = Net::HTTP.new("localhost", "9002")
22
+ req = Net::HTTP::Post.new("/")
23
+ @tracer.inject(span.span_context, OpenTracing::FORMAT_RACK, req)
24
+ res = client.request(req)
25
+
26
+ span.log(event: "application_response", response: res.to_s)
27
+ span.finish
28
+ @tracer.flush
29
+ puts "----> #{$token} span_guid=#{span.span_context.id} at_micros=#{span.start_micros} <----"
30
+ [200, {}, [res.body]]
31
+ end
32
+ end
33
+
34
+ class App
35
+ def initialize
36
+ @tracer = SplunkTracing::Tracer.new(component_name: 'app', access_token: $token)
37
+ end
38
+
39
+ def call(env)
40
+ wire_ctx = @tracer.extract(OpenTracing::FORMAT_RACK, env)
41
+ span = @tracer.start_span("app_call", child_of: wire_ctx)
42
+ puts "child #{span.to_h[:trace_guid]}"
43
+ span.log(event: "application", env: env)
44
+ sleep 0.05
45
+ span.finish
46
+ @tracer.flush
47
+ [200, {}, ["application"]]
48
+ end
49
+ end
50
+
51
+ router_thread = Thread.new do
52
+ Thread.abort_on_exception = true
53
+ Rack::Server.start(app: Router.new, Port: 9001)
54
+ end
55
+
56
+ app_thread = Thread.new do
57
+ Thread.abort_on_exception = true
58
+ Rack::Server.start(app: App.new, Port: 9002)
59
+ end
60
+
61
+ loop do
62
+ begin
63
+ p Net::HTTP.get(URI("http://127.0.0.1:9001/"))
64
+ break
65
+ rescue Errno::ECONNREFUSED
66
+ sleep 0.05
67
+ end
68
+ end
@@ -0,0 +1,50 @@
1
+ require 'forwardable'
2
+ require 'logger'
3
+
4
+ # Splunk Tracer
5
+ module SplunkTracing
6
+ extend SingleForwardable
7
+
8
+ # Base class for all SplunkTracing errors
9
+ class Error < StandardError; end
10
+
11
+ # Returns the singleton instance of the Tracer.
12
+ def self.instance
13
+ SplunkTracing::GlobalTracer.instance
14
+ end
15
+
16
+ def_delegator :instance, :configure
17
+ def_delegator :instance, :start_span
18
+ def_delegator :instance, :start_active_span
19
+ def_delegator :instance, :disable
20
+ def_delegator :instance, :enable
21
+ def_delegator :instance, :flush
22
+
23
+ # Convert a time to microseconds
24
+ def self.micros(time)
25
+ (time.to_f * 1E6).floor
26
+ end
27
+
28
+ # Returns a random guid. Note: this intentionally does not use SecureRandom,
29
+ # which is slower and cryptographically secure randomness is not required here.
30
+ def self.guid
31
+ unless @_lastpid == Process.pid
32
+ @_lastpid = Process.pid
33
+ @_rng = Random.new
34
+ end
35
+ @_rng.bytes(8).unpack('H*')[0]
36
+ end
37
+
38
+ def self.logger
39
+ @logger ||= defined?(::Rails) ? Rails.logger : Logger.new(STDOUT)
40
+ end
41
+
42
+ def self.logger=(logger)
43
+ @logger = logger
44
+ end
45
+ end
46
+
47
+ require 'splunktracing/tracer'
48
+ require 'splunktracing/global_tracer'
49
+ require 'splunktracing/scope'
50
+ require 'splunktracing/scope_manager'
@@ -0,0 +1,32 @@
1
+ require 'singleton'
2
+
3
+ module SplunkTracing
4
+ # GlobalTracer is a singleton version of the SplunkTracing::Tracer.
5
+ #
6
+ # You should access it via `SplunkTracing.instance`.
7
+ class GlobalTracer < Tracer
8
+ private
9
+ def initialize
10
+ end
11
+
12
+ public
13
+ include Singleton
14
+
15
+ # Configure the GlobalTracer
16
+ # See {SplunkTracing::Tracer#initialize}
17
+ def configure(**options)
18
+ if configured
19
+ SplunkTracing.logger.warn "[SplunkTracing] Already configured"
20
+ SplunkTracing.logger.info "Stack trace:\n\t#{caller.join("\n\t")}"
21
+ return
22
+ end
23
+
24
+ self.configured = true
25
+ super
26
+ end
27
+
28
+ private
29
+
30
+ attr_accessor :configured
31
+ end
32
+ end
@@ -0,0 +1,119 @@
1
+ require 'socket'
2
+
3
+ require 'splunktracing/version'
4
+
5
+ module SplunkTracing
6
+ # Reporter builds up reports of spans and flushes them to a transport
7
+ class Reporter
8
+ DEFAULT_PERIOD_SECONDS = 3.0
9
+ attr_accessor :max_span_records
10
+ attr_accessor :period
11
+
12
+ def initialize(max_span_records:, transport:, guid:, component_name:, tags: {})
13
+ @max_span_records = max_span_records
14
+ @span_records = Concurrent::Array.new
15
+ @dropped_spans = Concurrent::AtomicFixnum.new
16
+ @transport = transport
17
+ @period = DEFAULT_PERIOD_SECONDS
18
+
19
+ start_time = SplunkTracing.micros(Time.now)
20
+ @report_start_time = start_time
21
+
22
+ @runtime = {
23
+ guid: guid,
24
+ device: Socket.gethostname,
25
+ start_micros: start_time,
26
+ component_name: component_name,
27
+ tracer_platform: "ruby",
28
+ tracer_version: SplunkTracing::VERSION,
29
+ tracer_platform_version: RUBY_VERSION,
30
+ attrs: tags
31
+ }.freeze
32
+
33
+ reset_on_fork
34
+ end
35
+
36
+ def add_span(span)
37
+ reset_on_fork
38
+
39
+ @span_records.push(span.to_h)
40
+ if @span_records.size > max_span_records
41
+ @span_records.shift
42
+ @dropped_spans.increment
43
+ end
44
+ end
45
+
46
+ def clear
47
+ reset_on_fork
48
+
49
+ span_records = @span_records.slice!(0, @span_records.length)
50
+ @dropped_spans.increment(span_records.size)
51
+ end
52
+
53
+ def flush
54
+ reset_on_fork
55
+
56
+ return if @span_records.empty?
57
+
58
+ now = SplunkTracing.micros(Time.now)
59
+
60
+ span_records = @span_records.slice!(0, @span_records.length)
61
+ dropped_spans = 0
62
+ @dropped_spans.update do |old|
63
+ dropped_spans = old
64
+ 0
65
+ end
66
+
67
+ report_request = {
68
+ runtime: @runtime,
69
+ oldest_micros: @report_start_time,
70
+ youngest_micros: now,
71
+ span_records: span_records,
72
+ internal_metrics: {
73
+ counts: [{
74
+ name: 'spans.dropped',
75
+ int64_value: dropped_spans
76
+ }]
77
+ }
78
+ }
79
+
80
+ @report_start_time = now
81
+
82
+ begin
83
+ @transport.report(report_request)
84
+ rescue StandardError => e
85
+ SplunkTracing.logger.error "SplunkTracing error reporting to collector: #{e.message}"
86
+ # an error occurs, add the previous dropped_spans and count of spans
87
+ # that would have been recorded
88
+ @dropped_spans.increment(dropped_spans + span_records.length)
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ # When the process forks, reset the child. All data that was copied will be handled
95
+ # by the parent. Also, restart the thread since forking killed it
96
+ def reset_on_fork
97
+ if @pid != $$
98
+ @pid = $$
99
+ @span_records.clear
100
+ @dropped_spans.value = 0
101
+ report_spans
102
+ end
103
+ end
104
+
105
+ def report_spans
106
+ return if @period <= 0
107
+ Thread.new do
108
+ begin
109
+ loop do
110
+ sleep(@period)
111
+ flush
112
+ end
113
+ rescue StandardError => e
114
+ SplunkTracing.logger.error "SplunkTracing failed to report spans: #{e.message}"
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end