splunk-tracer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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