appdash 0.6.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.
- 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
|