lusis-signalfx 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +43 -0
- data/.travis.yml +7 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +84 -0
- data/README.md +139 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/examples/generic_usecase.rb +45 -0
- data/lib/proto/signal_fx_protocol_buffers.pb.rb +73 -0
- data/lib/signalfx/conf.rb +19 -0
- data/lib/signalfx/json_signal_fx_client.rb +40 -0
- data/lib/signalfx/protobuf_signal_fx_client.rb +63 -0
- data/lib/signalfx/signal_fx_client.rb +226 -0
- data/lib/signalfx/version.rb +6 -0
- data/lib/signalfx.rb +43 -0
- data/signalfx.gemspec +36 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c7b95445941d5e9e609f221e8c9c72aa9f014cbf
|
4
|
+
data.tar.gz: 1eef0a1069ddc25c2ee790f0746e2be80e27f388
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cd28fc56c10b35781dd11c361a6ea22760afa2a8a26f1b1b7a2098a30da8266ae0e3f6619fc5ef4a578042549e5a6ea7a54247a632cf57e6b29d8c9c50a45aaa
|
7
|
+
data.tar.gz: 51366533c3d3c8644357de9bee69edc51bcd792492b09671616f41831d157962b6f9d17dca1cae2ab7ded899cf6ec44b2ba981a54e931c908fe100f2b7f336bf
|
data/.gitignore
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
### IDEA
|
2
|
+
.idea
|
3
|
+
*.iml
|
4
|
+
|
5
|
+
### Ruby template
|
6
|
+
*.gem
|
7
|
+
*.rbc
|
8
|
+
/.config
|
9
|
+
/coverage/
|
10
|
+
/InstalledFiles
|
11
|
+
/pkg/
|
12
|
+
/spec/reports/
|
13
|
+
/spec/examples.txt
|
14
|
+
/test/tmp/
|
15
|
+
/test/version_tmp/
|
16
|
+
/tmp/
|
17
|
+
|
18
|
+
## Specific to RubyMotion:
|
19
|
+
.dat*
|
20
|
+
.repl_history
|
21
|
+
build/
|
22
|
+
|
23
|
+
## Documentation cache and generated files:
|
24
|
+
/.yardoc/
|
25
|
+
/_yardoc/
|
26
|
+
/doc/
|
27
|
+
/rdoc/
|
28
|
+
|
29
|
+
## Environment normalisation:
|
30
|
+
/.bundle/
|
31
|
+
/vendor/bundle
|
32
|
+
/lib/bundler/man/
|
33
|
+
|
34
|
+
# for a library or gem, you might want to ignore these files since the code is
|
35
|
+
# intended to run in multiple environments; otherwise, check them in:
|
36
|
+
# Gemfile.lock
|
37
|
+
# .ruby-version
|
38
|
+
# .ruby-gemset
|
39
|
+
|
40
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
41
|
+
.rvmrc
|
42
|
+
|
43
|
+
# Created by .ignore support plugin (hsz.mobi)
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
signalfx (0.1.0)
|
5
|
+
protobuf (~> 3.5.1, >= 3.5.1)
|
6
|
+
rest-client (~> 1.8)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activesupport (4.2.4)
|
12
|
+
i18n (~> 0.7)
|
13
|
+
json (~> 1.7, >= 1.7.7)
|
14
|
+
minitest (~> 5.1)
|
15
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
16
|
+
tzinfo (~> 1.1)
|
17
|
+
addressable (2.3.8)
|
18
|
+
crack (0.4.2)
|
19
|
+
safe_yaml (~> 1.0.0)
|
20
|
+
diff-lcs (1.2.5)
|
21
|
+
docile (1.1.5)
|
22
|
+
domain_name (0.5.25)
|
23
|
+
unf (>= 0.0.5, < 1.0.0)
|
24
|
+
http-cookie (1.0.2)
|
25
|
+
domain_name (~> 0.5)
|
26
|
+
i18n (0.7.0)
|
27
|
+
json (1.8.3)
|
28
|
+
middleware (0.1.0)
|
29
|
+
mime-types (2.6.2)
|
30
|
+
minitest (5.8.0)
|
31
|
+
netrc (0.10.3)
|
32
|
+
protobuf (3.5.1)
|
33
|
+
activesupport (>= 3.2)
|
34
|
+
middleware
|
35
|
+
thor
|
36
|
+
thread_safe
|
37
|
+
rake (10.4.2)
|
38
|
+
rest-client (1.8.0)
|
39
|
+
http-cookie (>= 1.0.2, < 2.0)
|
40
|
+
mime-types (>= 1.16, < 3.0)
|
41
|
+
netrc (~> 0.7)
|
42
|
+
rspec (3.3.0)
|
43
|
+
rspec-core (~> 3.3.0)
|
44
|
+
rspec-expectations (~> 3.3.0)
|
45
|
+
rspec-mocks (~> 3.3.0)
|
46
|
+
rspec-core (3.3.2)
|
47
|
+
rspec-support (~> 3.3.0)
|
48
|
+
rspec-expectations (3.3.1)
|
49
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
50
|
+
rspec-support (~> 3.3.0)
|
51
|
+
rspec-mocks (3.3.2)
|
52
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
53
|
+
rspec-support (~> 3.3.0)
|
54
|
+
rspec-support (3.3.0)
|
55
|
+
safe_yaml (1.0.4)
|
56
|
+
simplecov (0.10.0)
|
57
|
+
docile (~> 1.1.0)
|
58
|
+
json (~> 1.8)
|
59
|
+
simplecov-html (~> 0.10.0)
|
60
|
+
simplecov-html (0.10.0)
|
61
|
+
thor (0.19.1)
|
62
|
+
thread_safe (0.3.5)
|
63
|
+
tzinfo (1.2.2)
|
64
|
+
thread_safe (~> 0.1)
|
65
|
+
unf (0.1.4)
|
66
|
+
unf_ext
|
67
|
+
unf_ext (0.0.7.1)
|
68
|
+
webmock (1.21.0)
|
69
|
+
addressable (>= 2.3.6)
|
70
|
+
crack (>= 0.3.2)
|
71
|
+
|
72
|
+
PLATFORMS
|
73
|
+
ruby
|
74
|
+
|
75
|
+
DEPENDENCIES
|
76
|
+
bundler (~> 1.10)
|
77
|
+
rake (~> 10.0)
|
78
|
+
rspec
|
79
|
+
signalfx!
|
80
|
+
simplecov
|
81
|
+
webmock (~> 1.21)
|
82
|
+
|
83
|
+
BUNDLED WITH
|
84
|
+
1.10.6
|
data/README.md
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
# Ruby client library for SignalFx
|
2
|
+
|
3
|
+
This is a programmatic interface in Ruby for SignalFx's metadata and ingest APIs. It is meant to provide a base for communicating with SignalFx APIs that can be easily leveraged by scripts and applications to interact with SignalFx or report metric and event data to SignalFx.
|
4
|
+
Library supports Ruby 2.x versions
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'signalfx'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install signalfx
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### API access token
|
23
|
+
|
24
|
+
To use this library, you need a SignalFx API access token, which can be obtained from the SignalFx organization you want to report data into.
|
25
|
+
|
26
|
+
### Create client
|
27
|
+
|
28
|
+
The default constructor `SignalFx` uses Protobuf to send data to SignalFx. If it cannot send Protobuf, it falls back to sending JSON.
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
require('signalfx');
|
32
|
+
|
33
|
+
// Create client
|
34
|
+
client = SignalFx.new 'MY_SIGNALFX_TOKEN';
|
35
|
+
```
|
36
|
+
|
37
|
+
Optional constructor parameters:
|
38
|
+
+ **api_token** - Your private SignalFx token
|
39
|
+
+ **enable_aws_unique_id** - boolean, `false` by default.
|
40
|
+
If `true`, library will retrieve Amazon unique identifier
|
41
|
+
and set it as `AWSUniqueId` dimension for each datapoint and event.
|
42
|
+
Use this option only if your application deployed to Amazon
|
43
|
+
+ **ingest_endpoint** - string
|
44
|
+
+ **api_endpoint** - string
|
45
|
+
+ **timeout** - number
|
46
|
+
+ **batch_size** - number
|
47
|
+
+ **user_agents** - array
|
48
|
+
|
49
|
+
### Reporting data
|
50
|
+
|
51
|
+
This example shows how to report metrics to SignalFx, as gauges, counters, or cumulative counters.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
require('signalfx');
|
55
|
+
|
56
|
+
client = SignalFx.new 'MY_SIGNALFX_TOKEN';
|
57
|
+
|
58
|
+
client.send(
|
59
|
+
cumulative_counters:[
|
60
|
+
{ :metric => 'myfunc.calls_cumulative',
|
61
|
+
:value => 10,
|
62
|
+
:timestamp => 1442960607000 },
|
63
|
+
...
|
64
|
+
],
|
65
|
+
gauges:[
|
66
|
+
{ :metric => 'myfunc.time',
|
67
|
+
:value => 532,
|
68
|
+
:timestamp => 1442960607000},
|
69
|
+
...
|
70
|
+
],
|
71
|
+
counters:[
|
72
|
+
{ :metric => 'myfunc.calls',
|
73
|
+
:value => 42,
|
74
|
+
:timestamp => 1442960607000},
|
75
|
+
...
|
76
|
+
]);
|
77
|
+
```
|
78
|
+
The `timestamp` must be a millisecond precision timestamp; the number of milliseconds elapsed since Epoch. The `timestamp` field is optional, but strongly recommended. If not specified, it will be set by SignalFx's ingest servers automatically; in this situation, the timestamp of your datapoints will not accurately represent the time of their measurement (network latency, batching, etc. will all impact when those datapoints actually make it to SignalFx).
|
79
|
+
|
80
|
+
### Sending multi-dimensional data
|
81
|
+
|
82
|
+
Reporting dimensions for the data is also optional, and can be accomplished by specifying a `dimensions` parameter on each datapoint containing a dictionary of string to string key/value pairs representing the dimensions:
|
83
|
+
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
require('signalfx');
|
87
|
+
|
88
|
+
client = SignalFx.new 'MY_SIGNALFX_TOKEN';
|
89
|
+
|
90
|
+
client.send(
|
91
|
+
cumulative_counters:[
|
92
|
+
{ :metric => 'myfunc.calls_cumulative',
|
93
|
+
:value => 10,
|
94
|
+
:dimensions => [{:key => 'host', :value => 'server1'}]},
|
95
|
+
...
|
96
|
+
],
|
97
|
+
gauges:[
|
98
|
+
{ :metric => 'myfunc.time',
|
99
|
+
:value=> 532,
|
100
|
+
:dimensions=> [{:key => 'host', :value => 'server1'}]},
|
101
|
+
...
|
102
|
+
],
|
103
|
+
counters:[
|
104
|
+
{ :metric=> 'myfunc.calls',
|
105
|
+
:value=> 42,
|
106
|
+
:dimensions=> [{:key => 'host', :value => 'server1'}]},
|
107
|
+
...
|
108
|
+
]);
|
109
|
+
```
|
110
|
+
See `examples/generic_usecase.py` for a complete code example for Reporting data.
|
111
|
+
|
112
|
+
### Sending events
|
113
|
+
|
114
|
+
Events can be sent to SignalFx via the `send_event` function. The
|
115
|
+
event type must be specified, and dimensions and extra event properties
|
116
|
+
can be supplied as well.
|
117
|
+
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
require('signalfx');
|
121
|
+
|
122
|
+
client = SignalFx.new 'MY_SIGNALFX_TOKEN';
|
123
|
+
|
124
|
+
client.send_event(
|
125
|
+
'[event_type]',
|
126
|
+
{
|
127
|
+
host: 'myhost',
|
128
|
+
service: 'myservice',
|
129
|
+
instance: 'myinstance'
|
130
|
+
},
|
131
|
+
properties={
|
132
|
+
version: 'event_version'})
|
133
|
+
```
|
134
|
+
|
135
|
+
See `examples/generic_usecase.py` for a complete code example for Reporting data.
|
136
|
+
|
137
|
+
## License
|
138
|
+
|
139
|
+
Apache Software License v2 © [SignalFx](https://signalfx.com)
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "signalfx"
|
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
|
data/bin/setup
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require './lib/signalfx'
|
3
|
+
|
4
|
+
puts 'SignalFx metrics reporting demo:'
|
5
|
+
token = ARGV[0] # Your SignalFx API access token
|
6
|
+
if token.nil? || token.empty?
|
7
|
+
puts '
|
8
|
+
SignalFx API access token not defined. Please specify token in command line.
|
9
|
+
$ ./generic_usecase.rb YOUR_TOKEN
|
10
|
+
|
11
|
+
'
|
12
|
+
exit 0
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
#create client instance with SignalFx API access token
|
17
|
+
client = SignalFx.new token, enable_aws_unique_id: false, timeout: 3000
|
18
|
+
|
19
|
+
puts 'SignalFx metrics reporting demo:'
|
20
|
+
#run loop to send datapoints to SignalFx
|
21
|
+
counter = 0
|
22
|
+
while true do
|
23
|
+
puts "Send datapoints ##{counter}"
|
24
|
+
timestamp = (Time.now.to_i * 1000).to_i
|
25
|
+
gauges = [{:metric => 'test.cpu', :value => counter % 10, :timestamp => timestamp}]
|
26
|
+
counters = [{:metric => 'cpu_cnt', :value => counter % 2, :timestamp => timestamp}]
|
27
|
+
|
28
|
+
client.send(gauges: gauges, counters: counters)
|
29
|
+
|
30
|
+
if counter % 100 == 0
|
31
|
+
event_type = 'deployments'
|
32
|
+
version = Time.now.strftime("%Y-%m-%d") + '-' + counter.to_s
|
33
|
+
dimensions =
|
34
|
+
{host: 'myhost',
|
35
|
+
service: 'myservice',
|
36
|
+
instance: 'myinstance'}
|
37
|
+
properties = {version: version}
|
38
|
+
|
39
|
+
|
40
|
+
client.send_event(event_type, dimensions: dimensions, properties: properties)
|
41
|
+
end
|
42
|
+
|
43
|
+
counter +=1
|
44
|
+
sleep(1)
|
45
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
##
|
4
|
+
# This file is auto-generated. DO NOT EDIT!
|
5
|
+
#
|
6
|
+
require 'protobuf'
|
7
|
+
|
8
|
+
module Com
|
9
|
+
module Signalfx
|
10
|
+
module Metrics
|
11
|
+
module Protobuf
|
12
|
+
|
13
|
+
##
|
14
|
+
# Enum Classes
|
15
|
+
#
|
16
|
+
class MetricType < ::Protobuf::Enum
|
17
|
+
define :GAUGE, 0
|
18
|
+
define :COUNTER, 1
|
19
|
+
define :ENUM, 2
|
20
|
+
define :CUMULATIVE_COUNTER, 3
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
##
|
25
|
+
# Message Classes
|
26
|
+
#
|
27
|
+
class Datum < ::Protobuf::Message; end
|
28
|
+
class Dimension < ::Protobuf::Message; end
|
29
|
+
class DataPoint < ::Protobuf::Message; end
|
30
|
+
class DataPointUploadMessage < ::Protobuf::Message; end
|
31
|
+
class PointValue < ::Protobuf::Message; end
|
32
|
+
|
33
|
+
|
34
|
+
##
|
35
|
+
# Message Fields
|
36
|
+
#
|
37
|
+
class Datum
|
38
|
+
optional :string, :strValue, 1
|
39
|
+
optional :double, :doubleValue, 2
|
40
|
+
optional :int64, :intValue, 3
|
41
|
+
end
|
42
|
+
|
43
|
+
class Dimension
|
44
|
+
optional :string, :key, 1
|
45
|
+
optional :string, :value, 2
|
46
|
+
end
|
47
|
+
|
48
|
+
class DataPoint
|
49
|
+
optional :string, :source, 1
|
50
|
+
optional :string, :metric, 2
|
51
|
+
optional :int64, :timestamp, 3
|
52
|
+
optional ::Com::Signalfx::Metrics::Protobuf::Datum, :value, 4
|
53
|
+
optional ::Com::Signalfx::Metrics::Protobuf::MetricType, :metricType, 5
|
54
|
+
repeated ::Com::Signalfx::Metrics::Protobuf::Dimension, :dimensions, 6
|
55
|
+
end
|
56
|
+
|
57
|
+
class DataPointUploadMessage
|
58
|
+
repeated ::Com::Signalfx::Metrics::Protobuf::DataPoint, :datapoints, 1
|
59
|
+
end
|
60
|
+
|
61
|
+
class PointValue
|
62
|
+
optional :int64, :timestamp, 3
|
63
|
+
optional ::Com::Signalfx::Metrics::Protobuf::Datum, :value, 4
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Copyright (C) 2015 SignalFx, Inc. All rights reserved.
|
2
|
+
|
3
|
+
module SignalFX
|
4
|
+
module Config
|
5
|
+
# Default Parameters
|
6
|
+
DEFAULT_INGEST_ENDPOINT = 'https://ingest.signalfx.com'
|
7
|
+
DEFAULT_API_ENDPOINT = 'https://api.signalfx.com'
|
8
|
+
DEFAULT_BATCH_SIZE = 300 # Will wait for this many requests before posting
|
9
|
+
DEFAULT_TIMEOUT = 1
|
10
|
+
|
11
|
+
# Global Parameters
|
12
|
+
PROTOBUF_HEADER_CONTENT_TYPE = 'application/x-protobuf'
|
13
|
+
JSON_HEADER_CONTENT_TYPE = 'application/json'
|
14
|
+
|
15
|
+
AWS_UNIQUE_ID_URL = 'http://169.254.169.254/2014-11-05/dynamic/instance-identity/document'
|
16
|
+
|
17
|
+
AWS_UNIQUE_ID_DIMENSION_NAME = :AWSUniqueId
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Copyright (C) 2015 SignalFx, Inc. All rights reserved.
|
2
|
+
|
3
|
+
require_relative './signal_fx_client'
|
4
|
+
require_relative './conf'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
class JsonSignalFx < SignalFxClient
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def header_content_type
|
12
|
+
SignalFX::Config::JSON_HEADER_CONTENT_TYPE
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_to_queue(metric_type, datapoint)
|
16
|
+
#set datapoint dimensions
|
17
|
+
dimensions = {}
|
18
|
+
if datapoint[:dimensions] != nil
|
19
|
+
datapoint[:dimensions].each {
|
20
|
+
|dimension| dimensions[dimension[:key]] = dimension[:value]
|
21
|
+
}
|
22
|
+
end
|
23
|
+
datapoint[:dimensions] = dimensions
|
24
|
+
get_queue << {metric_type => datapoint}
|
25
|
+
end
|
26
|
+
|
27
|
+
def batch_data(data_point_list)
|
28
|
+
data = Hash.new
|
29
|
+
data_point_list.each do |datapoint|
|
30
|
+
datapoint.each do |key, value|
|
31
|
+
if data[key] == nil
|
32
|
+
data[key] = []
|
33
|
+
end
|
34
|
+
data[key] << value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
data.to_json
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Copyright (C) 2015 SignalFx, Inc. All rights reserved.
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
require_relative './conf'
|
5
|
+
require_relative './signal_fx_client'
|
6
|
+
require_relative '../proto/signal_fx_protocol_buffers.pb'
|
7
|
+
|
8
|
+
class ProtoBufSignalFx < SignalFxClient
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def header_content_type
|
13
|
+
SignalFX::Config::PROTOBUF_HEADER_CONTENT_TYPE
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def add_to_queue(metric_type, datapoint)
|
18
|
+
protobuf_datapoint = Com::Signalfx::Metrics::Protobuf::DataPoint.new
|
19
|
+
|
20
|
+
# assign value type
|
21
|
+
datapoint_value = datapoint[:value]
|
22
|
+
if datapoint_value.kind_of?(String)
|
23
|
+
protobuf_datapoint.value = Com::Signalfx::Metrics::Protobuf::Datum.new :strValue => datapoint_value
|
24
|
+
else
|
25
|
+
if datapoint_value.kind_of?(Float)
|
26
|
+
protobuf_datapoint.value = Com::Signalfx::Metrics::Protobuf::Datum.new :doubleValue => datapoint_value
|
27
|
+
else
|
28
|
+
if datapoint_value.kind_of?(Fixnum)
|
29
|
+
protobuf_datapoint.value = Com::Signalfx::Metrics::Protobuf::Datum.new :intValue => datapoint_value
|
30
|
+
else
|
31
|
+
throw TypeError('Invalid Value ' + datapoint_value);
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
protobuf_datapoint.metricType = Com::Signalfx::Metrics::Protobuf::MetricType.const_get(metric_type.upcase)
|
38
|
+
protobuf_datapoint.metric = datapoint[:metric]
|
39
|
+
if datapoint[:timestamp] != nil
|
40
|
+
protobuf_datapoint.timestamp = datapoint[:timestamp]
|
41
|
+
end
|
42
|
+
|
43
|
+
#set datapoint dimensions
|
44
|
+
dimensions = Array.new
|
45
|
+
if datapoint[:dimensions] != nil
|
46
|
+
datapoint[:dimensions].each {
|
47
|
+
|dimension| dimensions.push(
|
48
|
+
Com::Signalfx::Metrics::Protobuf::Dimension.new :key => dimension[:key], :value => dimension[:value])
|
49
|
+
}
|
50
|
+
end
|
51
|
+
protobuf_datapoint.dimensions = dimensions
|
52
|
+
|
53
|
+
# add object to queue
|
54
|
+
get_queue. << protobuf_datapoint
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def batch_data(data_point_list)
|
59
|
+
dpum = Com::Signalfx::Metrics::Protobuf::DataPointUploadMessage.new
|
60
|
+
data_point_list.each { |datapoint| dpum.datapoints << datapoint }
|
61
|
+
dpum.to_s
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
# Copyright (C) 2015 SignalFx, Inc. All rights reserved.
|
2
|
+
|
3
|
+
require_relative './version'
|
4
|
+
require_relative './conf'
|
5
|
+
|
6
|
+
require 'net/http'
|
7
|
+
require 'uri'
|
8
|
+
require 'openssl'
|
9
|
+
require 'rest-client'
|
10
|
+
|
11
|
+
class SignalFxClient
|
12
|
+
HEADER_API_TOKEN_KEY = 'X-SF-Token'
|
13
|
+
HEADER_USER_AGENT_KEY = 'User-Agent'
|
14
|
+
HEADER_CONTENT_TYPE = 'Content-Type'
|
15
|
+
INGEST_ENDPOINT_SUFFIX = 'v2/datapoint'
|
16
|
+
API_ENDPOINT_SUFFIX = 'v1/event'
|
17
|
+
|
18
|
+
def initialize(api_token, enable_aws_unique_id: false, ingest_endpoint: SignalFX::Config::DEFAULT_INGEST_ENDPOINT,
|
19
|
+
api_endpoint: SignalFX::Config::DEFAULT_API_ENDPOINT, timeout: SignalFX::Config::DEFAULT_TIMEOUT,
|
20
|
+
batch_size: SignalFX::Config::DEFAULT_BATCH_SIZE, user_agents: [])
|
21
|
+
|
22
|
+
@api_token = api_token
|
23
|
+
@ingest_endpoint = ingest_endpoint
|
24
|
+
@api_endpoint = api_endpoint
|
25
|
+
@timeout = timeout
|
26
|
+
@batch_size = batch_size
|
27
|
+
@user_agents = user_agents
|
28
|
+
|
29
|
+
@aws_unique_id = nil
|
30
|
+
|
31
|
+
@queue = Queue.new
|
32
|
+
@async_running = false
|
33
|
+
|
34
|
+
if enable_aws_unique_id
|
35
|
+
retrieve_aws_unique_id { |request|
|
36
|
+
if request != nil
|
37
|
+
json_resp = JSON.parse(request.body)
|
38
|
+
@aws_unique_id = json_resp['instanceId']+'_'+json_resp['region']+'_'+json_resp['accountId']
|
39
|
+
puts("AWS Unique ID loaded: #{@aws_unique_id}")
|
40
|
+
else
|
41
|
+
puts('Failed to retrieve AWS unique ID.')
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
puts('initialize end')
|
46
|
+
end
|
47
|
+
|
48
|
+
#Send the given metrics to SignalFx synchronously.
|
49
|
+
#You can use this method to send data via reporters such as Codahale style libraries
|
50
|
+
#
|
51
|
+
#Args:
|
52
|
+
# cumulative_counters (list): a list of dictionaries representing the
|
53
|
+
# cumulative counters to report.
|
54
|
+
# gauges (list): a list of dictionaries representing the gauges to report.
|
55
|
+
# counters (list): a list of dictionaries representing the counters to report.
|
56
|
+
def send(cumulative_counters: nil, gauges: nil, counters: nil)
|
57
|
+
process_datapoint('cumulative_counter', cumulative_counters)
|
58
|
+
process_datapoint('gauge', gauges)
|
59
|
+
process_datapoint('counter', counters)
|
60
|
+
|
61
|
+
data_points_list = []
|
62
|
+
while @queue.length > 0 && data_points_list.length < @batch_size
|
63
|
+
data_points_list << @queue.shift
|
64
|
+
end
|
65
|
+
|
66
|
+
data_to_send = batch_data(data_points_list)
|
67
|
+
|
68
|
+
begin
|
69
|
+
post(data_to_send, @ingest_endpoint, INGEST_ENDPOINT_SUFFIX)
|
70
|
+
ensure
|
71
|
+
@async_running = false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
#Send the given metrics to SignalFx asynchronously.
|
76
|
+
#
|
77
|
+
#Args:
|
78
|
+
# cumulative_counters (list): a list of dictionaries representing the
|
79
|
+
# cumulative counters to report.
|
80
|
+
# gauges (list): a list of dictionaries representing the gauges to report.
|
81
|
+
# counters (list): a list of dictionaries representing the counters to report.
|
82
|
+
def send_async(cumulative_counters: nil, gauges: nil, counters: nil)
|
83
|
+
process_datapoint('cumulative_counter', cumulative_counters)
|
84
|
+
process_datapoint('gauge', gauges)
|
85
|
+
process_datapoint('counter', counters)
|
86
|
+
|
87
|
+
if @async_running
|
88
|
+
return
|
89
|
+
end
|
90
|
+
|
91
|
+
data_points_list = []
|
92
|
+
while @queue.length > 0 && data_points_list.length < @batch_size
|
93
|
+
data_points_list << @queue.shift
|
94
|
+
end
|
95
|
+
|
96
|
+
data_to_send = batch_data(data_points_list)
|
97
|
+
|
98
|
+
@async_running = true
|
99
|
+
|
100
|
+
Thread.abort_on_exception = true
|
101
|
+
Thread.start {
|
102
|
+
begin
|
103
|
+
|
104
|
+
post(data_to_send, @ingest_endpoint, INGEST_ENDPOINT_SUFFIX){
|
105
|
+
@async_running = false
|
106
|
+
}
|
107
|
+
ensure
|
108
|
+
@async_running = false
|
109
|
+
end
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
#Send an event to SignalFx.
|
115
|
+
#
|
116
|
+
#Args:
|
117
|
+
# event_type (string): the event type (name of the event time series).
|
118
|
+
# dimensions (dict): a map of event dimensions.
|
119
|
+
# properties (dict): a map of extra properties on that event.
|
120
|
+
def send_event(event_type, dimensions: {}, properties: {})
|
121
|
+
data = {
|
122
|
+
eventType: event_type,
|
123
|
+
dimensions: dimensions,
|
124
|
+
properties: properties
|
125
|
+
}
|
126
|
+
|
127
|
+
if @aws_unique_id
|
128
|
+
data[:dimensions][SignalFX::Config::AWS_UNIQUE_ID_DIMENSION_NAME] = @aws_unique_id
|
129
|
+
end
|
130
|
+
|
131
|
+
puts(data)
|
132
|
+
|
133
|
+
post(data.to_json, @api_endpoint, API_ENDPOINT_SUFFIX, SignalFX::Config::JSON_HEADER_CONTENT_TYPE)
|
134
|
+
end
|
135
|
+
|
136
|
+
protected
|
137
|
+
|
138
|
+
def get_queue
|
139
|
+
@queue
|
140
|
+
end
|
141
|
+
|
142
|
+
def header_content_type
|
143
|
+
raise 'Subclasses should implement this!'
|
144
|
+
end
|
145
|
+
|
146
|
+
def add_to_queue(metric_type, datapoint)
|
147
|
+
raise 'Subclasses should implement this!'
|
148
|
+
end
|
149
|
+
|
150
|
+
def batch_data(data_point_list)
|
151
|
+
raise 'Subclasses should implement this!'
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def post(data_to_send, url, suffix, content_type = nil, &block)
|
157
|
+
begin
|
158
|
+
http_user_agents = ''
|
159
|
+
if @user_agents != nil && @user_agents.length > 0
|
160
|
+
http_user_agents = ', ' + @user_agents.join(', ')
|
161
|
+
end
|
162
|
+
|
163
|
+
headers = {HEADER_CONTENT_TYPE => content_type != nil ? content_type : header_content_type,
|
164
|
+
HEADER_API_TOKEN_KEY => @api_token,
|
165
|
+
HEADER_USER_AGENT_KEY => Version::NAME + '/' + Version::VERSION + http_user_agents}
|
166
|
+
|
167
|
+
RestClient::Request.execute(
|
168
|
+
method: :post,
|
169
|
+
url: url + '/' + suffix,
|
170
|
+
headers: headers,
|
171
|
+
payload: data_to_send,
|
172
|
+
verify_ssl: OpenSSL::SSL::VERIFY_NONE,
|
173
|
+
timeout: @timeout) { |response|
|
174
|
+
case response.code
|
175
|
+
when 200
|
176
|
+
if block
|
177
|
+
block.call(response)
|
178
|
+
end
|
179
|
+
else
|
180
|
+
puts "Failed to send datapoints. Response code: #{response.code}"
|
181
|
+
if block
|
182
|
+
block.call(nil)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
}
|
186
|
+
rescue Exception => e
|
187
|
+
puts "Failed to send datapoints. Error: #{e}"
|
188
|
+
if block
|
189
|
+
block.call(nil)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def retrieve_aws_unique_id(&block)
|
195
|
+
begin
|
196
|
+
RestClient::Request.execute(method: :get, url: SignalFX::Config::AWS_UNIQUE_ID_URL,
|
197
|
+
timeout: 1) { |response|
|
198
|
+
case response.code
|
199
|
+
when 200
|
200
|
+
return block.call(response)
|
201
|
+
else
|
202
|
+
puts "Failed to retrieve AWS unique ID. Response code: #{response.code}"
|
203
|
+
return block.call(nil)
|
204
|
+
end
|
205
|
+
}
|
206
|
+
rescue Exception => e
|
207
|
+
puts "Failed to retrieve AWS unique ID. Error: #{e}"
|
208
|
+
block.call(nil)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def process_datapoint(metric_type, data_points)
|
213
|
+
if data_points != nil && data_points.kind_of?(Array)
|
214
|
+
data_points.each { |datapoint|
|
215
|
+
if @aws_unique_id
|
216
|
+
if datapoint[:dimensions] == nil
|
217
|
+
datapoint[:dimensions] = []
|
218
|
+
end
|
219
|
+
datapoint[:dimensions] << {:key => SignalFX::Config::AWS_UNIQUE_ID_DIMENSION_NAME, :value => @aws_unique_id}
|
220
|
+
end
|
221
|
+
|
222
|
+
add_to_queue(metric_type, datapoint)
|
223
|
+
}
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
data/lib/signalfx.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Copyright (C) 2015 SignalFx, Inc. All rights reserved.
|
2
|
+
|
3
|
+
require_relative 'signalfx/conf'
|
4
|
+
require_relative 'signalfx/protobuf_signal_fx_client'
|
5
|
+
require_relative 'signalfx/json_signal_fx_client'
|
6
|
+
|
7
|
+
module SignalFx
|
8
|
+
|
9
|
+
# SignalFx API client.
|
10
|
+
# This class presents a programmatic interface to SignalFx's metadata and
|
11
|
+
# ingest APIs. At the time being, only ingest is supported; more will come
|
12
|
+
# later.
|
13
|
+
#
|
14
|
+
# @param api_token - Your private SignalFx token
|
15
|
+
# @param enable_aws_unique_id - boolean, `false` by default.
|
16
|
+
# If `true`, library will retrieve Amazon unique identifier
|
17
|
+
# and set it as `AWSUniqueId` dimension for each datapoint and event.
|
18
|
+
# Use this option only if your application deployed to Amazon
|
19
|
+
# @param ingest_endpoint - string
|
20
|
+
# @param api_endpoint - string
|
21
|
+
# @param timeout - number
|
22
|
+
# @param batch_size - number
|
23
|
+
# @param user_agents - array
|
24
|
+
def self.new(api_token, enable_aws_unique_id: false, ingest_endpoint: SignalFX::Config::DEFAULT_INGEST_ENDPOINT,
|
25
|
+
api_endpoint: SignalFX::Config::DEFAULT_API_ENDPOINT, timeout: SignalFX::Config::DEFAULT_TIMEOUT,
|
26
|
+
batch_size: SignalFX::Config::DEFAULT_BATCH_SIZE, user_agents: [])
|
27
|
+
begin
|
28
|
+
require_relative './proto/signal_fx_protocol_buffers.pb'
|
29
|
+
ProtoBufSignalFx.new(api_token, enable_aws_unique_id: enable_aws_unique_id, ingest_endpoint: ingest_endpoint,
|
30
|
+
api_endpoint: api_endpoint, timeout: timeout,
|
31
|
+
batch_size: batch_size, user_agents: user_agents)
|
32
|
+
|
33
|
+
rescue Exception => e
|
34
|
+
puts "Protocol Buffers not installed properly. Switch to JSON.
|
35
|
+
#{e}"
|
36
|
+
JsonSignalFx.new(api_token, enable_aws_unique_id: enable_aws_unique_id, ingest_endpoint: ingest_endpoint,
|
37
|
+
api_endpoint: api_endpoint, timeout: timeout,
|
38
|
+
batch_size: batch_size, user_agents: user_agents)
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
data/signalfx.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require_relative 'lib/signalfx/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "lusis-signalfx"
|
8
|
+
spec.version = Version::VERSION
|
9
|
+
spec.authors = ["SignalFx, Inc"]
|
10
|
+
spec.email = ["info@signalfx.com"]
|
11
|
+
|
12
|
+
spec.summary = "Ruby client library for SignalFx"
|
13
|
+
spec.description = "This is a programmatic interface in Ruby for SignalFx's metadata and ingest APIs. It is meant to provide a base for communicating with SignalFx APIs that can be easily leveraged by scripts and applications to interact with SignalFx or report metric and event data to SignalFx. Library supports Ruby 2.x versions"
|
14
|
+
spec.homepage = "https://signalfx.com"
|
15
|
+
spec.license = "Apache Software License v2 © SignalFx"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
18
|
+
# delete this section to allow pushing this gem to any host.
|
19
|
+
#if spec.respond_to?(:metadata)
|
20
|
+
# spec.metadata['allowed_push_host'] = "Set to 'http://mygemserver.com'"
|
21
|
+
#else
|
22
|
+
# raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
|
23
|
+
#end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
31
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
32
|
+
spec.add_development_dependency "rspec"
|
33
|
+
spec.add_development_dependency "webmock", "~> 1.21"
|
34
|
+
spec.add_dependency "protobuf", "~> 3.5.1", ">= 3.5.1"
|
35
|
+
spec.add_dependency "rest-client", "~> 1.8"
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lusis-signalfx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- SignalFx, Inc
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
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: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.21'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.21'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: protobuf
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 3.5.1
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 3.5.1
|
79
|
+
type: :runtime
|
80
|
+
prerelease: false
|
81
|
+
version_requirements: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - "~>"
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 3.5.1
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 3.5.1
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rest-client
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1.8'
|
96
|
+
type: :runtime
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1.8'
|
103
|
+
description: This is a programmatic interface in Ruby for SignalFx's metadata and
|
104
|
+
ingest APIs. It is meant to provide a base for communicating with SignalFx APIs
|
105
|
+
that can be easily leveraged by scripts and applications to interact with SignalFx
|
106
|
+
or report metric and event data to SignalFx. Library supports Ruby 2.x versions
|
107
|
+
email:
|
108
|
+
- info@signalfx.com
|
109
|
+
executables: []
|
110
|
+
extensions: []
|
111
|
+
extra_rdoc_files: []
|
112
|
+
files:
|
113
|
+
- ".gitignore"
|
114
|
+
- ".travis.yml"
|
115
|
+
- Gemfile
|
116
|
+
- Gemfile.lock
|
117
|
+
- README.md
|
118
|
+
- Rakefile
|
119
|
+
- bin/console
|
120
|
+
- bin/setup
|
121
|
+
- examples/generic_usecase.rb
|
122
|
+
- lib/proto/signal_fx_protocol_buffers.pb.rb
|
123
|
+
- lib/signalfx.rb
|
124
|
+
- lib/signalfx/conf.rb
|
125
|
+
- lib/signalfx/json_signal_fx_client.rb
|
126
|
+
- lib/signalfx/protobuf_signal_fx_client.rb
|
127
|
+
- lib/signalfx/signal_fx_client.rb
|
128
|
+
- lib/signalfx/version.rb
|
129
|
+
- signalfx.gemspec
|
130
|
+
homepage: https://signalfx.com
|
131
|
+
licenses:
|
132
|
+
- Apache Software License v2 © SignalFx
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.4.8
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: Ruby client library for SignalFx
|
154
|
+
test_files: []
|
155
|
+
has_rdoc:
|