appdash 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +9 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +57 -0
- data/MIT-LICENCE +22 -0
- data/README.md +35 -0
- data/Rakefile +28 -0
- data/appdash.gemspec +23 -0
- data/defs/appdash/collector.proto +30 -0
- data/lib/appdash.rb +4 -0
- data/lib/appdash/client.rb +77 -0
- data/lib/appdash/collector.pb.rb +41 -0
- data/lib/appdash/event/base.rb +62 -0
- data/lib/appdash/event/log.rb +17 -0
- data/lib/appdash/event/message.rb +16 -0
- data/lib/appdash/event/rack_server.rb +54 -0
- data/lib/appdash/event/span_name.rb +16 -0
- data/lib/appdash/events.rb +21 -0
- data/lib/appdash/span.rb +48 -0
- data/lib/appdash/span/id.rb +44 -0
- data/lib/appdash/wire.rb +13 -0
- data/spec/appdash/client_spec.rb +32 -0
- data/spec/appdash/event/base_spec.rb +22 -0
- data/spec/appdash/event/log_spec.rb +22 -0
- data/spec/appdash/event/message_spec.rb +16 -0
- data/spec/appdash/event/rack_server_spec.rb +37 -0
- data/spec/appdash/event/span_name_spec.rb +16 -0
- data/spec/appdash/span/id_spec.rb +25 -0
- data/spec/appdash/span_spec.rb +33 -0
- data/spec/appdash/wire_spec.rb +10 -0
- data/spec/spec_helper.rb +15 -0
- metadata +154 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 832463798ecc366655cfe139b52dfdab61e4ad14
|
4
|
+
data.tar.gz: f5b7db5b952464cfe30f1aa93c673bb4252d2a9d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 87131161bf4489115b1b559e342180e4538e9e48363e314ff9e947b393ccf8592bff625a7022d168ba7fb94f19f2786eb52451d63b4b01734f95aa192f41aee9
|
7
|
+
data.tar.gz: 04963a82c52bedb5fbd7a5b93feca849c0494b95b98e9a5264126ad2529339110b0eddc73622c9db5b33aeb12c0d483f1d342cb7c905e6772583c86e27eb1f2d
|
data/.editorconfig
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
appdash (0.6.0)
|
5
|
+
protobuf
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (4.2.5)
|
11
|
+
i18n (~> 0.7)
|
12
|
+
json (~> 1.7, >= 1.7.7)
|
13
|
+
minitest (~> 5.1)
|
14
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
15
|
+
tzinfo (~> 1.1)
|
16
|
+
diff-lcs (1.2.5)
|
17
|
+
i18n (0.7.0)
|
18
|
+
json (1.8.3)
|
19
|
+
middleware (0.1.0)
|
20
|
+
minitest (5.8.3)
|
21
|
+
protobuf (3.5.5)
|
22
|
+
activesupport (>= 3.2)
|
23
|
+
middleware
|
24
|
+
thor
|
25
|
+
thread_safe
|
26
|
+
rack (1.6.4)
|
27
|
+
rake (10.4.2)
|
28
|
+
rspec (3.4.0)
|
29
|
+
rspec-core (~> 3.4.0)
|
30
|
+
rspec-expectations (~> 3.4.0)
|
31
|
+
rspec-mocks (~> 3.4.0)
|
32
|
+
rspec-core (3.4.1)
|
33
|
+
rspec-support (~> 3.4.0)
|
34
|
+
rspec-expectations (3.4.0)
|
35
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
36
|
+
rspec-support (~> 3.4.0)
|
37
|
+
rspec-mocks (3.4.0)
|
38
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
39
|
+
rspec-support (~> 3.4.0)
|
40
|
+
rspec-support (3.4.1)
|
41
|
+
thor (0.19.1)
|
42
|
+
thread_safe (0.3.5)
|
43
|
+
tzinfo (1.2.2)
|
44
|
+
thread_safe (~> 0.1)
|
45
|
+
|
46
|
+
PLATFORMS
|
47
|
+
ruby
|
48
|
+
|
49
|
+
DEPENDENCIES
|
50
|
+
appdash!
|
51
|
+
bundler
|
52
|
+
rack
|
53
|
+
rake
|
54
|
+
rspec
|
55
|
+
|
56
|
+
BUNDLED WITH
|
57
|
+
1.10.6
|
data/MIT-LICENCE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Black Square Media
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
Ruby Appdash
|
2
|
+
============
|
3
|
+
|
4
|
+
[Appdash](https://github.com/sourcegraph/appdash) client for Ruby.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'appdash'
|
11
|
+
|
12
|
+
Or install:
|
13
|
+
|
14
|
+
$ gem install appdash
|
15
|
+
|
16
|
+
Connect to an instance:
|
17
|
+
|
18
|
+
client = Appdash::Client.new host: "remote.host", port: 7701, max_buffer_size: 20
|
19
|
+
|
20
|
+
Collect spans:
|
21
|
+
|
22
|
+
client.span do |s|
|
23
|
+
s.name "Request"
|
24
|
+
s.log "a log entry with a timestamp"
|
25
|
+
end
|
26
|
+
|
27
|
+
For full options and event types, please see the [Documentation](http://www.rubydoc.info/gems/appdash).
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
1. Fork it
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
5. Make a pull request
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
8
|
+
task default: :spec
|
9
|
+
|
10
|
+
namespace :protobuf do
|
11
|
+
PROTO_ROOT = "./defs/appdash"
|
12
|
+
|
13
|
+
task :fetch do
|
14
|
+
target = "#{PROTO_ROOT}/collector.proto"
|
15
|
+
sh %(mkdir -p #{PROTO_ROOT})
|
16
|
+
sh %(curl -sSL https://raw.githubusercontent.com/sourcegraph/appdash/master/internal/wire/collector.proto | sed 's/package wire/package appdash/' > #{target})
|
17
|
+
end
|
18
|
+
|
19
|
+
task :compile do
|
20
|
+
Dir[PROTO_ROOT+"/**/*.proto"].each do |file|
|
21
|
+
sh "PB_NO_TAG_WARNINGS=1 protoc -I ./defs --ruby_out ./lib #{file}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Rebuild protobuf definitions"
|
26
|
+
task rebuild: [:fetch, :compile]
|
27
|
+
|
28
|
+
end
|
data/appdash.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "appdash"
|
5
|
+
s.version = "0.6.0"
|
6
|
+
s.authors = ["Black Square Media"]
|
7
|
+
s.email = ["dimitrij@blacksquaremedia.com"]
|
8
|
+
s.summary = %q{Appdash client for ruby}
|
9
|
+
s.description = %q{Ruby client for Appdash, Sourcegraph's application tracing system, based on Google's Dapper}
|
10
|
+
s.homepage = "https://github.com/bsm/appdash-rb"
|
11
|
+
s.required_ruby_version = '>= 1.9.0'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split($/)
|
14
|
+
s.test_files = s.files.grep(%r{^(spec)/})
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
|
17
|
+
s.add_dependency(%q<protobuf>)
|
18
|
+
|
19
|
+
s.add_development_dependency(%q<rake>)
|
20
|
+
s.add_development_dependency(%q<rack>)
|
21
|
+
s.add_development_dependency(%q<bundler>)
|
22
|
+
s.add_development_dependency(%q<rspec>)
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
package appdash;
|
2
|
+
|
3
|
+
// CollectPacket is the message sent to a remote collector server by one of
|
4
|
+
// it's clients.
|
5
|
+
message CollectPacket {
|
6
|
+
// SpanID is the group of information which can uniquely identify the exact
|
7
|
+
// span being collected.
|
8
|
+
required group SpanID = 1 {
|
9
|
+
// trace is the root ID of the tree that contains all of the spans
|
10
|
+
// related to this one.
|
11
|
+
required fixed64 trace = 2;
|
12
|
+
|
13
|
+
// span is an ID that probabilistically uniquely identifies this span.
|
14
|
+
required fixed64 span = 3;
|
15
|
+
|
16
|
+
// parent is the ID of the parent span, if any.
|
17
|
+
optional fixed64 parent = 4;
|
18
|
+
}
|
19
|
+
|
20
|
+
// Annotation is any number of annotations for the span to be collected.
|
21
|
+
repeated group Annotation = 5 {
|
22
|
+
// key is the annotation's key.
|
23
|
+
required string key = 6;
|
24
|
+
|
25
|
+
// value is the annotation's value, which may be either human or
|
26
|
+
// machine readable, depending on the schema of the event that
|
27
|
+
// generated it.
|
28
|
+
optional bytes value = 7;
|
29
|
+
}
|
30
|
+
}
|
data/lib/appdash.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module Appdash
|
5
|
+
class Client
|
6
|
+
|
7
|
+
# Client defaults
|
8
|
+
DEFAULTS = {
|
9
|
+
host: 'localhost',
|
10
|
+
port: 7701,
|
11
|
+
max_buffer_size: 1,
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
# Initializes a new client
|
15
|
+
# @param [Hash] opts
|
16
|
+
# @option opts [String] :host the hostname, defaults to localhost
|
17
|
+
# @option opts [Integer] :port the port, defaults to 7701
|
18
|
+
# @option opts [Integer] :max_buffer_size number of spans in the buffer before flushing, defaults to 1 (= no buffering)
|
19
|
+
def initialize(opts = {})
|
20
|
+
@config = DEFAULTS.merge(opts)
|
21
|
+
@sock = TCPSocket.new @config[:host], @config[:port]
|
22
|
+
@buffer = []
|
23
|
+
@mutex = Mutex.new
|
24
|
+
end
|
25
|
+
|
26
|
+
# Traces a new span with a series of associated events. Accepts an optional block. If no block is given you must flush
|
27
|
+
# to send data to the collector.
|
28
|
+
#
|
29
|
+
# @example manual flush
|
30
|
+
#
|
31
|
+
# span = client.span
|
32
|
+
# span.name "Request"
|
33
|
+
# span.message "A simple message"
|
34
|
+
# span.flush
|
35
|
+
#
|
36
|
+
# @example with block
|
37
|
+
#
|
38
|
+
# client.span do |span|
|
39
|
+
# span.name "Request"
|
40
|
+
# span.message "A simple message"
|
41
|
+
# end
|
42
|
+
def span(&block)
|
43
|
+
span = Appdash::Span.new(self)
|
44
|
+
return span unless block
|
45
|
+
|
46
|
+
begin
|
47
|
+
block.call(span)
|
48
|
+
ensure
|
49
|
+
span.flush
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Shutdown flushes any remaining buffered packets and closes the connection
|
54
|
+
def shutdown
|
55
|
+
flush_buffer!
|
56
|
+
@sock.shutdown
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def write(packets)
|
62
|
+
packets.each do |packet|
|
63
|
+
raw = Protobuf::Field::VarintField.encode(packet.bytesize)+packet
|
64
|
+
@buffer.push(raw)
|
65
|
+
end
|
66
|
+
flush_buffer! unless @buffer.size < @config[:max_buffer_size]
|
67
|
+
end
|
68
|
+
|
69
|
+
def flush_buffer!
|
70
|
+
@mutex.synchronize do
|
71
|
+
@sock.write @buffer.join("\n") unless @buffer.empty?
|
72
|
+
@buffer.clear
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
##
|
4
|
+
# This file is auto-generated. DO NOT EDIT!
|
5
|
+
#
|
6
|
+
require 'protobuf/message'
|
7
|
+
|
8
|
+
module Appdash
|
9
|
+
|
10
|
+
##
|
11
|
+
# Message Classes
|
12
|
+
#
|
13
|
+
class CollectPacket < ::Protobuf::Message
|
14
|
+
class SpanID < ::Protobuf::Message; end
|
15
|
+
class Annotation < ::Protobuf::Message; end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
##
|
22
|
+
# Message Fields
|
23
|
+
#
|
24
|
+
class CollectPacket
|
25
|
+
class SpanID
|
26
|
+
required :fixed64, :trace, 2
|
27
|
+
required :fixed64, :span, 3
|
28
|
+
optional :fixed64, :parent, 4
|
29
|
+
end
|
30
|
+
|
31
|
+
class Annotation
|
32
|
+
required :string, :key, 6
|
33
|
+
optional :bytes, :value, 7
|
34
|
+
end
|
35
|
+
|
36
|
+
required ::Appdash::CollectPacket::SpanID, :spanid, 1
|
37
|
+
repeated ::Appdash::CollectPacket::Annotation, :annotation, 5
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'appdash/wire'
|
2
|
+
|
3
|
+
module Appdash
|
4
|
+
module Event
|
5
|
+
|
6
|
+
# Base forms the most basic event type
|
7
|
+
class Base
|
8
|
+
|
9
|
+
attr_reader :schema, :attrs
|
10
|
+
|
11
|
+
# @param [String] schema the event schema name
|
12
|
+
# @param [Hash] attrs event attributes
|
13
|
+
def initialize(schema, attrs = {})
|
14
|
+
super()
|
15
|
+
@schema = schema
|
16
|
+
@attrs = attrs
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Array<Appdash::CollectPacket::Annotation>] marshalable annotations
|
20
|
+
def to_a
|
21
|
+
annotate(attrs) + [annotation(['_schema', schema].join(':'))]
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def annotate(hash, prefix = nil)
|
27
|
+
hash.map do |key, value|
|
28
|
+
key = [prefix, normalize(key)].compact.join('.')
|
29
|
+
|
30
|
+
case value
|
31
|
+
when Hash
|
32
|
+
annotate(value, key)
|
33
|
+
else
|
34
|
+
annotation(key, value)
|
35
|
+
end
|
36
|
+
end.flatten
|
37
|
+
end
|
38
|
+
|
39
|
+
def annotation(key, value = nil)
|
40
|
+
case value
|
41
|
+
when DateTime, Time
|
42
|
+
value = value.strftime('%FT%T%:z')
|
43
|
+
when NilClass
|
44
|
+
# ignore
|
45
|
+
else
|
46
|
+
value = value.to_s
|
47
|
+
end
|
48
|
+
Appdash::CollectPacket::Annotation.new(key: key, value: value)
|
49
|
+
end
|
50
|
+
|
51
|
+
def normalize(key)
|
52
|
+
key = key.to_s
|
53
|
+
key = key.sub(/^[a-z\d]*/) { Appdash::Event.acronyms[$&] || $&.capitalize }
|
54
|
+
key.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{Appdash::Event.acronyms[$2] || $2.capitalize}" }
|
55
|
+
|
56
|
+
key[0] = key[0].upcase if key.size > 0
|
57
|
+
key
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'appdash/event/base'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Appdash
|
5
|
+
module Event
|
6
|
+
|
7
|
+
# Log is an event whose timestamp is the current time and contains the given human-readable log message.
|
8
|
+
class Log < Base
|
9
|
+
|
10
|
+
# @param [String] msg the log message
|
11
|
+
def initialize(msg)
|
12
|
+
super("log", msg: msg, time: Time.now)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'appdash/event/base'
|
2
|
+
|
3
|
+
module Appdash
|
4
|
+
module Event
|
5
|
+
|
6
|
+
# Message is an event that contains only a human-readable message.
|
7
|
+
class Message < Base
|
8
|
+
|
9
|
+
# @param [String] msg the message
|
10
|
+
def initialize(msg)
|
11
|
+
super("msg", msg: msg)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'appdash/event/base'
|
2
|
+
|
3
|
+
module Appdash
|
4
|
+
module Event
|
5
|
+
|
6
|
+
# RackServer represents a HTTP event where a client's request was served via Rack.
|
7
|
+
class RackServer < Appdash::Event::Base
|
8
|
+
|
9
|
+
# @param [Rack::Request] req a Rack request object
|
10
|
+
# @param [Rack::Response] resp an optional Rack response object
|
11
|
+
# @param [Hash] attrs additional attributes, e.g. :user or :route
|
12
|
+
def initialize(req, resp = nil, attrs = {})
|
13
|
+
server = attrs.dup
|
14
|
+
server[:send] ||= Time.now
|
15
|
+
server[:request] ||= {}
|
16
|
+
server[:request].update parse_request(req)
|
17
|
+
if resp
|
18
|
+
server[:response] ||= {}
|
19
|
+
server[:response].update parse_response(resp)
|
20
|
+
end
|
21
|
+
|
22
|
+
super("HTTPServer", server: server)
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def parse_request(req)
|
28
|
+
data = {
|
29
|
+
method: req.request_method,
|
30
|
+
url: req.url,
|
31
|
+
scheme: req.scheme,
|
32
|
+
host: req.host,
|
33
|
+
remote_ip: req.ip,
|
34
|
+
content_length: req.content_length
|
35
|
+
}
|
36
|
+
|
37
|
+
[:content_type, :user_agent].each do |name|
|
38
|
+
value = req.send(name)
|
39
|
+
data[name] = value if value
|
40
|
+
end
|
41
|
+
|
42
|
+
data
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_response(resp)
|
46
|
+
{
|
47
|
+
content_length: resp.length,
|
48
|
+
status_code: resp.status
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'appdash/event/base'
|
2
|
+
|
3
|
+
module Appdash
|
4
|
+
module Event
|
5
|
+
|
6
|
+
# SpanName is an event which sets a span's name.
|
7
|
+
class SpanName < Base
|
8
|
+
|
9
|
+
# @param [String] name the event span name
|
10
|
+
def initialize(name)
|
11
|
+
super("name", name: name)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'appdash/event/base'
|
2
|
+
require 'appdash/event/span_name'
|
3
|
+
require 'appdash/event/message'
|
4
|
+
require 'appdash/event/log'
|
5
|
+
require 'appdash/event/rack_server'
|
6
|
+
|
7
|
+
module Appdash
|
8
|
+
module Event
|
9
|
+
|
10
|
+
# @return [Hash] known acronyms for attribute translations
|
11
|
+
def self.acronyms
|
12
|
+
@acronyms ||= {
|
13
|
+
"url" => "URL",
|
14
|
+
"uri" => "URI",
|
15
|
+
"id" => "ID",
|
16
|
+
"ip" => "IP",
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/appdash/span.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'appdash/span/id'
|
2
|
+
|
3
|
+
module Appdash
|
4
|
+
class Span
|
5
|
+
|
6
|
+
# @attr_reader :root_id returns the root span ID
|
7
|
+
attr_reader :root_id
|
8
|
+
|
9
|
+
# @attr_reader :id returns the current span ID
|
10
|
+
attr_reader :id
|
11
|
+
|
12
|
+
def initialize(client)
|
13
|
+
@client = client
|
14
|
+
@packets = []
|
15
|
+
@root_id = Appdash::Span::ID.new
|
16
|
+
@id = root_id
|
17
|
+
end
|
18
|
+
|
19
|
+
# Appends a new Appdash::Event::SpanName event
|
20
|
+
def name(val)
|
21
|
+
event Appdash::Event::SpanName.new(val)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Appends a new Appdash::Event::Message event
|
25
|
+
def message(msg)
|
26
|
+
event Appdash::Event::Message.new(msg)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Appends a new Appdash::Event::Log event
|
30
|
+
def log(msg)
|
31
|
+
event Appdash::Event::Log.new(msg)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Appends a generic Appdash::Event event
|
35
|
+
def event(evt)
|
36
|
+
@packets.push Appdash::CollectPacket.encode(evt, @id)
|
37
|
+
@id = @id.child
|
38
|
+
end
|
39
|
+
|
40
|
+
def flush
|
41
|
+
count = @packets.size
|
42
|
+
@client.send :write, @packets
|
43
|
+
@packets.clear
|
44
|
+
count
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Appdash
|
4
|
+
class Span
|
5
|
+
|
6
|
+
class ID
|
7
|
+
# @attr_reader [Integer] root ID of the tree that contains all of the spans related to this one.
|
8
|
+
attr_reader :trace
|
9
|
+
|
10
|
+
# @attr_reader [Integer] an ID that probabilistically uniquely identifies this span.
|
11
|
+
attr_reader :span
|
12
|
+
|
13
|
+
# @attr_reader [Integer] the ID of the parent span, if any.
|
14
|
+
attr_reader :parent
|
15
|
+
|
16
|
+
# Creates a new root span
|
17
|
+
# @param [Appdash::SpanID] parent, optional parent
|
18
|
+
def initialize(parent = nil)
|
19
|
+
@trace = parent ? parent.trace : random_uint64
|
20
|
+
@span = random_uint64
|
21
|
+
@parent = parent.span if parent
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Appdash::SpanID] creates a child span
|
25
|
+
def child
|
26
|
+
self.class.new(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [String] string ID
|
30
|
+
def to_s
|
31
|
+
[trace, span, parent].compact.map do |num|
|
32
|
+
num.to_s(16).rjust(16, '0')
|
33
|
+
end.join("/")
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def random_uint64
|
39
|
+
SecureRandom.random_bytes(8).unpack("Q")[0]
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/appdash/wire.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'protobuf'
|
2
|
+
require 'appdash/collector.pb.rb'
|
3
|
+
|
4
|
+
module Appdash
|
5
|
+
class CollectPacket
|
6
|
+
|
7
|
+
def self.encode(event, id)
|
8
|
+
wired = SpanID.new(trace: id.trace, span: id.span, parent: id.parent)
|
9
|
+
new(spanid: wired, annotation: event.to_a).encode
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Appdash::Client do
|
4
|
+
|
5
|
+
let(:mock_socket) { double("TCPSocket", shutdown: nil) }
|
6
|
+
before { allow(TCPSocket).to receive(:new).and_return(mock_socket) }
|
7
|
+
|
8
|
+
it "should collect spans" do
|
9
|
+
span = subject.span
|
10
|
+
span.event(Appdash::Event::Message.new("Hello world!"))
|
11
|
+
|
12
|
+
expect(mock_socket).to receive(:write) do |raw|
|
13
|
+
expect(raw.bytesize).to eq(57)
|
14
|
+
expect(raw.bytes.first).to eq(56)
|
15
|
+
end.and_return(57)
|
16
|
+
span.flush
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should support buffering" do
|
20
|
+
subject = described_class.new(max_buffer_size: 3)
|
21
|
+
subject.span do |s|
|
22
|
+
s.message("Message A")
|
23
|
+
s.message("Message B")
|
24
|
+
end
|
25
|
+
|
26
|
+
expect(mock_socket).to receive(:write) do |raw|
|
27
|
+
expect(raw.bytesize).to eq(118)
|
28
|
+
end
|
29
|
+
subject.shutdown
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Appdash::Event::Base do
|
4
|
+
|
5
|
+
subject { described_class.new 'test', name: "Query", some_other: 2, sub: { req_url: "http://example.com/path", nest: { v: Time.utc(2015) }}}
|
6
|
+
|
7
|
+
it "should init" do
|
8
|
+
expect(subject.schema).to eq("test")
|
9
|
+
expect(subject.attrs.size).to eq(3)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should annotate" do
|
13
|
+
expect(atoh(subject.to_a)).to eq(
|
14
|
+
"Name" => "Query",
|
15
|
+
"SomeOther" => "2",
|
16
|
+
"Sub.ReqURL" => "http://example.com/path",
|
17
|
+
"Sub.Nest.V"=>"2015-01-01T00:00:00+00:00",
|
18
|
+
"_schema:test"=>"",
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Appdash::Event::Log do
|
4
|
+
|
5
|
+
let(:time) { Time.utc(2001,2,3,4,5,6) }
|
6
|
+
before { allow(Time).to receive(:now).and_return(time) }
|
7
|
+
subject { described_class.new "Hi" }
|
8
|
+
|
9
|
+
it "should init" do
|
10
|
+
expect(subject.schema).to eq("log")
|
11
|
+
expect(subject.attrs).to eq(msg: "Hi", time: time)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should annotate" do
|
15
|
+
expect(atoh(subject.to_a)).to eq(
|
16
|
+
"Msg" => "Hi",
|
17
|
+
"Time" => "2001-02-03T04:05:06+00:00",
|
18
|
+
"_schema:log" => "",
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Appdash::Event::Message do
|
4
|
+
|
5
|
+
subject { described_class.new "Hi" }
|
6
|
+
|
7
|
+
it "should init" do
|
8
|
+
expect(subject.schema).to eq("msg")
|
9
|
+
expect(subject.attrs).to eq(msg: "Hi")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should annotate" do
|
13
|
+
expect(atoh(subject.to_a)).to eq("Msg"=>"Hi", "_schema:msg"=>"")
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Appdash::Event::RackServer do
|
4
|
+
|
5
|
+
let(:time) { Time.utc(2001,2,3,4,5,6) }
|
6
|
+
let(:response) { Rack::Response.new([], 201, 'Content-Length' => 33) }
|
7
|
+
let(:request) do
|
8
|
+
env = Rack::MockRequest.env_for("http://example.com:8080/", "REMOTE_ADDR" => "10.10.10.10", 'CONTENT_LENGTH' => 12, 'HTTP_USER_AGENT' => 'Test/1.0')
|
9
|
+
Rack::Request.new(env)
|
10
|
+
end
|
11
|
+
|
12
|
+
before { allow(Time).to receive(:now).and_return(time) }
|
13
|
+
subject { described_class.new request, response, route: 'createPost' }
|
14
|
+
|
15
|
+
it "should init" do
|
16
|
+
expect(subject.schema).to eq("HTTPServer")
|
17
|
+
expect(subject.attrs.size).to eq(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should annotate" do
|
21
|
+
expect(atoh(subject.to_a)).to eq(
|
22
|
+
"Server.Request.ContentLength" => "12",
|
23
|
+
"Server.Request.Host" => "example.com",
|
24
|
+
"Server.Request.Method" => "GET",
|
25
|
+
"Server.Request.Scheme" => "http",
|
26
|
+
"Server.Request.RemoteIP" => "10.10.10.10",
|
27
|
+
"Server.Request.URL" => "http://example.com:8080/",
|
28
|
+
"Server.Request.UserAgent" => "Test/1.0",
|
29
|
+
"Server.Response.ContentLength" => "0",
|
30
|
+
"Server.Response.StatusCode" => "201",
|
31
|
+
"Server.Route" => "createPost",
|
32
|
+
"Server.Send" => "2001-02-03T04:05:06+00:00",
|
33
|
+
"_schema:HTTPServer" => "",
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Appdash::Event::SpanName do
|
4
|
+
|
5
|
+
subject { described_class.new "Query" }
|
6
|
+
|
7
|
+
it "should init" do
|
8
|
+
expect(subject.schema).to eq("name")
|
9
|
+
expect(subject.attrs).to eq(name: "Query")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should annotate" do
|
13
|
+
expect(atoh(subject.to_a)).to eq("Name" => "Query", "_schema:name" => "")
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Appdash::Span::ID do
|
4
|
+
|
5
|
+
before do
|
6
|
+
allow(SecureRandom).to receive(:random_bytes).with(8).
|
7
|
+
and_return("A~CDEF~H", "xyz\x00\x00\x00\x00\x00", "^&<>^*%!", "2531afas", "*&^%$-[]")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should create instances" do
|
11
|
+
expect(subject.trace).to eq(5223689881108315713)
|
12
|
+
expect(subject.span).to eq(8026488)
|
13
|
+
expect(subject.parent).to be_nil
|
14
|
+
expect(subject.to_s).to eq("487e464544437e41/00000000007a7978")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should create children" do
|
18
|
+
child = subject.child
|
19
|
+
expect(child.trace).to eq(5223689881108315713)
|
20
|
+
expect(child.span).to eq(2388361761649337950)
|
21
|
+
expect(child.parent).to eq(8026488)
|
22
|
+
expect(child.to_s).to eq("487e464544437e41/21252a5e3e3c265e/00000000007a7978")
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Appdash::Span do
|
4
|
+
|
5
|
+
let(:mock_client) { double("Client", write: nil) }
|
6
|
+
subject { described_class.new mock_client }
|
7
|
+
|
8
|
+
it "should append events" do
|
9
|
+
expect(subject.id).to eq(subject.root_id)
|
10
|
+
subject.name("test")
|
11
|
+
expect(subject.id).not_to eq(subject.root_id)
|
12
|
+
expect(subject.id.to_s.size).to eq(50)
|
13
|
+
|
14
|
+
subject.message("test message")
|
15
|
+
subject.log("test log")
|
16
|
+
expect(subject.instance_variable_get(:@packets).size).to eq(3)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should flush" do
|
20
|
+
expect(subject.flush).to eq(0)
|
21
|
+
|
22
|
+
subject.name("test")
|
23
|
+
subject.message("test message")
|
24
|
+
|
25
|
+
expect(mock_client).to receive(:write) do |packets|
|
26
|
+
expect(packets.size).to eq(2)
|
27
|
+
expect(packets[0].bytesize).to eq(50)
|
28
|
+
expect(packets[1].bytesize).to eq(65)
|
29
|
+
end
|
30
|
+
expect(subject.flush).to eq(2)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: appdash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Black Square Media
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: protobuf
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Ruby client for Appdash, Sourcegraph's application tracing system, based
|
84
|
+
on Google's Dapper
|
85
|
+
email:
|
86
|
+
- dimitrij@blacksquaremedia.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".editorconfig"
|
92
|
+
- Gemfile
|
93
|
+
- Gemfile.lock
|
94
|
+
- MIT-LICENCE
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- appdash.gemspec
|
98
|
+
- defs/appdash/collector.proto
|
99
|
+
- lib/appdash.rb
|
100
|
+
- lib/appdash/client.rb
|
101
|
+
- lib/appdash/collector.pb.rb
|
102
|
+
- lib/appdash/event/base.rb
|
103
|
+
- lib/appdash/event/log.rb
|
104
|
+
- lib/appdash/event/message.rb
|
105
|
+
- lib/appdash/event/rack_server.rb
|
106
|
+
- lib/appdash/event/span_name.rb
|
107
|
+
- lib/appdash/events.rb
|
108
|
+
- lib/appdash/span.rb
|
109
|
+
- lib/appdash/span/id.rb
|
110
|
+
- lib/appdash/wire.rb
|
111
|
+
- spec/appdash/client_spec.rb
|
112
|
+
- spec/appdash/event/base_spec.rb
|
113
|
+
- spec/appdash/event/log_spec.rb
|
114
|
+
- spec/appdash/event/message_spec.rb
|
115
|
+
- spec/appdash/event/rack_server_spec.rb
|
116
|
+
- spec/appdash/event/span_name_spec.rb
|
117
|
+
- spec/appdash/span/id_spec.rb
|
118
|
+
- spec/appdash/span_spec.rb
|
119
|
+
- spec/appdash/wire_spec.rb
|
120
|
+
- spec/spec_helper.rb
|
121
|
+
homepage: https://github.com/bsm/appdash-rb
|
122
|
+
licenses: []
|
123
|
+
metadata: {}
|
124
|
+
post_install_message:
|
125
|
+
rdoc_options: []
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: 1.9.0
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
requirements: []
|
139
|
+
rubyforge_project:
|
140
|
+
rubygems_version: 2.4.8
|
141
|
+
signing_key:
|
142
|
+
specification_version: 4
|
143
|
+
summary: Appdash client for ruby
|
144
|
+
test_files:
|
145
|
+
- spec/appdash/client_spec.rb
|
146
|
+
- spec/appdash/event/base_spec.rb
|
147
|
+
- spec/appdash/event/log_spec.rb
|
148
|
+
- spec/appdash/event/message_spec.rb
|
149
|
+
- spec/appdash/event/rack_server_spec.rb
|
150
|
+
- spec/appdash/event/span_name_spec.rb
|
151
|
+
- spec/appdash/span/id_spec.rb
|
152
|
+
- spec/appdash/span_spec.rb
|
153
|
+
- spec/appdash/wire_spec.rb
|
154
|
+
- spec/spec_helper.rb
|