signalfx 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5fa0fd8975ae62bdd02d8bd289c8b76e62f79031
4
+ data.tar.gz: 2615a0bd2a102874a11312e6af172d4bcf76c0a0
5
+ SHA512:
6
+ metadata.gz: 98412f576fa8e655e14ef784adb852414414dde3dc0a34440435ed8f5cf3871e419610c4cbf258a4a0d9c23c610341304ba8c45ae39cc8caf89d52ebe78e66fa
7
+ data.tar.gz: 31eb6b8efc37a5e9f6af6b73a3de4a7d5da2d37f5b4b9aa06c5c79bf05a61429c8fe0c60d542ecf529420d1b70bcffd6a6df71700d4cc5273aa8ad4ef987db10
@@ -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)
@@ -0,0 +1,7 @@
1
+ sudo: required
2
+ language: ruby
3
+
4
+ rvm:
5
+ - 2.1.0
6
+ - 2.2.3
7
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in signalfx.gemspec
4
+ gemspec
5
+
6
+ source 'https://rubygems.org'
7
+
8
+ gem 'simplecov', :require => false, :group => :test
@@ -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
@@ -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)
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -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,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: Config::DEFAULT_INGEST_ENDPOINT,
25
+ api_endpoint: Config::DEFAULT_API_ENDPOINT, timeout: Config::DEFAULT_TIMEOUT,
26
+ batch_size: 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
@@ -0,0 +1,17 @@
1
+ # Copyright (C) 2015 SignalFx, Inc. All rights reserved.
2
+
3
+ module Config
4
+ # Default Parameters
5
+ DEFAULT_INGEST_ENDPOINT = 'https://ingest.signalfx.com'
6
+ DEFAULT_API_ENDPOINT = 'https://api.signalfx.com'
7
+ DEFAULT_BATCH_SIZE = 300 # Will wait for this many requests before posting
8
+ DEFAULT_TIMEOUT = 1
9
+
10
+ # Global Parameters
11
+ PROTOBUF_HEADER_CONTENT_TYPE = 'application/x-protobuf'
12
+ JSON_HEADER_CONTENT_TYPE = 'application/json'
13
+
14
+ AWS_UNIQUE_ID_URL = 'http://169.254.169.254/2014-11-05/dynamic/instance-identity/document'
15
+
16
+ AWS_UNIQUE_ID_DIMENSION_NAME = :AWSUniqueId
17
+ 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
+ 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
+ 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: Config::DEFAULT_INGEST_ENDPOINT,
19
+ api_endpoint: Config::DEFAULT_API_ENDPOINT, timeout: Config::DEFAULT_TIMEOUT,
20
+ batch_size: 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][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, 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: 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 => 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
@@ -0,0 +1,6 @@
1
+ # Copyright (C) 2015 SignalFx, Inc. All rights reserved.
2
+
3
+ module Version
4
+ VERSION = '0.1.0'
5
+ NAME = 'signalfx-ruby-client'
6
+ end
@@ -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 = "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,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: 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-11-11 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.0.14
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: Ruby client library for SignalFx
154
+ test_files: []