wavefront-client 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YmVlOTljMDc5ZWJkOTAwYzZiMDlhOTJhYTY3ZTI2YjRjNTBjZDliMw==
4
+ MDNiNGYxMWY3Y2NmNWMzM2YwY2YxNDczZDIxNGU0MGEyN2IwNDQ0MQ==
5
5
  data.tar.gz: !binary |-
6
- M2Y4ZDExZTE1NDFmNjUxZTI1NGYzOTExYzQ0ZmU2YjVhMTMzMGVmZA==
6
+ MWI3YTFiNTc2MWJlOWY3NWMyNDgwNDg1YjlkNWZiNTU0MjdhMGRkNg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NjRmYmU4NjU2NGQ0MGIwMzU4N2Q0MjQwNzQ4NWViYTJkYTZlNjk1Njk0Mzlh
10
- MWU1MTYyNDc3NzhlYzNiMDhmMjkwOWNiODExNDMwOWNlYWY5NWMzYWJhNjYw
11
- ZTdiMmVlNGYzOTY1MWQxMjZjYTUyZjA4ZjZiODY2MTI0Y2ZmZjE=
9
+ NDUwYjRjOWE3MWU4OWUzZTkyN2NiMDhiZWE4ODQ1YWU5ZDA0MWM0Mjc2OWVm
10
+ NzMxYjFiMTMzMWRiMWMyMDVkOGJlN2FmZjMyNThhYWY0YzdkMzIzZTczYTcw
11
+ ODRkMjJhOWY4M2ZiM2I1NjVhNjBmNDJjMDA4ZmRhNzlhMWEzYzA=
12
12
  data.tar.gz: !binary |-
13
- ZjUzOTZlZDlhOGY3M2NkZWU3ZDUwM2E4Y2NmNWJiNmFmZGI4MjQ4NmI1Mzk3
14
- NjZiMmQ1MTE5MzAwYmRhN2NkZTI0NTVmYjkyNDM0YzNiNWViMWNiYjExNTE1
15
- YmQ2ZTIzNzVmMTY4ZDJmNjI2YmQ1ZDg2NzYxMzIzZjJmYTY4N2M=
13
+ YTUzNTMxMzg1OTIwYzdmYWUzMDNiOTZkOGM3NzI2NTA3OWFkM2E5YzBiOGRm
14
+ ODZiNjhiNzNhMjYzODZkMDgwZDJkMGZmNGM0NmQ0NjBmYTEzMWIzNGM1ZDFl
15
+ MTU4Njk1NjFkMWRiYjBhMWM1OWEwMjFiYjA3Yzk4OGRiOThiN2E=
data/.travis.yml CHANGED
@@ -5,6 +5,7 @@ rvm:
5
5
  - 2.0.0
6
6
  - 2.1.0
7
7
  - 2.2.2
8
+ - 2.3.0
8
9
  deploy:
9
10
  provider: rubygems
10
11
  api_key:
data/README-cli.md CHANGED
@@ -12,7 +12,6 @@ The following options are valid in almost all contexts.
12
12
  ```
13
13
  -c, --config=FILE path to configuration file [default: ~/.wavefront]
14
14
  -P, --profile=NAME profile in configuration file [default: default]
15
- -E, --endpoint=URI cluster endpoint [default: metrics.wavefront.com]
16
15
  -t, --token=TOKEN Wavefront authentication token
17
16
  -D, --debug enable debug mode
18
17
  -h, --help show help for command
@@ -32,6 +31,8 @@ Usage:
32
31
  [-X bool] <query>
33
32
 
34
33
  Options:
34
+ -E, --endpoint=URI cluster endpoint [default:
35
+ metrics.wavefront.com]
35
36
  -S, --seconds query granularity of seconds
36
37
  -m, --minutes query granularity of minutes
37
38
  -H, --hours query granularity of hours
@@ -109,6 +110,7 @@ Usage:
109
110
  [-f format] [-p tag] [ -s tag] <state>
110
111
 
111
112
  Options:
113
+ -E, --endpoint=URI cluster endpoint [default: metrics.wavefront.com]
112
114
  -f, --format=STRING output format (ruby, json, human)
113
115
  [default: human]
114
116
  -p, --private=TAG retrieve only alerts with named private tags,
@@ -167,6 +169,7 @@ Usage:
167
169
  wavefront event --help
168
170
 
169
171
  Options:
172
+ -E, --endpoint=URI cluster endpoint [default: metrics.wavefront.com]
170
173
  -i, --instant create an instantaneous event
171
174
  -V, --verbose be verbose
172
175
  -s, --start=TIME time at which event begins
@@ -238,6 +241,110 @@ Closing event 'puppet_run'. [2016-06-27 19:25:16 +0100]
238
241
  Removing state file /var/tmp/wavefront/events/rob/1467051916712::puppet_run.
239
242
  ```
240
243
 
244
+ ## `write` Mode: Sending Points to Wavefront
245
+
246
+ The `write` command is used to put data points into Wavefront. It is
247
+ different from other commands in that it communicates with a
248
+ **proxy** rather than with the Wavefront API. This means it does
249
+ not require any credentials.
250
+
251
+ ```
252
+ Usage:
253
+ wavefront write point [-DV] [-c file] [-P profile] [-E proxy] [-t time]
254
+ [-p port] [-H host] [-n] [-T tag...] <metric> <value>
255
+ wavefront write file [-DV] [-c file] [-P profile] [-E proxy] [-H host]
256
+ [-p port] [-n] [-F format] [-m metric] [-T tag...] <file>
257
+
258
+ Options:
259
+ -E, --proxy=URI proxy endpoint [default: wavefront]
260
+ -t, --time=TIME time of data point (omit to use current time)
261
+ -H, --host=STRING source host [default: box]
262
+ -p, --port=INT Wavefront proxy port [default: 2878]
263
+ -T, --tag=TAG point tag in key=value form
264
+ -F, --format=STRING format of input file or stdin [default: tmv]
265
+ -n, --noop show the metric without sending it
266
+ -m, --metric=STRING the metric path to which contents of a file will be
267
+ assigned. If the file contains a metric name, the two
268
+ will be concatenated
269
+ -V, --verbose be verbose
270
+ ```
271
+
272
+ `write` has two sub-commands: `write point` and `write file`. Both
273
+ allow you to specify the proxy address and port either with
274
+ command-line switches, or by the `proxy` and `port` values in your
275
+ `.wavefront` file.
276
+
277
+ ### `write point`
278
+
279
+ This provides a very quick method of putting a point into Wavefront.
280
+ It takes two mandatory arguments: the metric path, and the metric
281
+ value.
282
+
283
+ You can optionally add point tags with multiple uses of `-T
284
+ key=val`, and specify a timestamp with the `-t` option.
285
+
286
+ ### `write file`
287
+
288
+ `write file` takes a whitespace-separated file, and turns it into a
289
+ series of points, which it sends to a Wavefront proxy. Each line
290
+ will be mapped to a single point.
291
+
292
+ Each line in the file must be of the same format, and must contain a
293
+ value. It can optionally contain metric path, a timestamp, and point
294
+ tags. The format of the file is passed in with the `-F` option, and
295
+ it must contain only the letters `t` (timestamp), `m` (metric path),
296
+ `v` (value) and `T` (point tags). If `T` is used, it must come
297
+ last: this allows you to have as many point tags as you like, and
298
+ you do not have to have the same number for each point.
299
+
300
+ The metric can also be, to some extent, described by options. If you
301
+ do not have a metric path in the file, you can use `-m` to supply a
302
+ path to which every point will be assigned. If you use `-m` *and* a
303
+ field in the file, the two will be concatenated, with `-m` used as a
304
+ global prefix.
305
+
306
+ Similarly, a global timestamp can be supplied with `-t` (timestamps
307
+ in files must be in epoch seconds, but `-t` can be any `strptime()`
308
+ parseable string), and global point tags with one or more `-T
309
+ key=val`s. If you supply tags with `-T` and in the file, the points
310
+ will get both.
311
+
312
+ The input file does not have to be on disk: following the Unix
313
+ convention, you can use `-` as the filename, and `wavefront` will
314
+ read from standard in, converting lines to points as it receives
315
+ them. All the same rules apply as with standard files.
316
+
317
+ `wavefront write file` takes some efforts to protect the user from
318
+ sending bad data. It will not allow metrics with less than two
319
+ components, and it will not permit timestamps prior to 2000-01-01,
320
+ or more than a year in the future. It also checks that every
321
+ potential data point conforms to the limits described in the
322
+ Wavefront wire format documentation.
323
+
324
+ ### Examples
325
+
326
+ Tell Wavefront that the value of `dev.myvalue` at this moment is
327
+ 123.
328
+
329
+ ```
330
+ $ wavefront write point dev.myvalue 123
331
+ ```
332
+
333
+ Write a file of retrospective data, whwere the fields are, in order,
334
+ a timestamp, the metric path, and the value. Tag all points with
335
+ `mytag` set to `my value`.
336
+
337
+ ```
338
+ $ wavefront write file -F tmvT -T mytag="my value" datafile
339
+ ```
340
+
341
+ The command `parabola.rb` prints a timestamp and a 'y' value every
342
+ second. Plot the parabola in Wavefront.
343
+
344
+ ```
345
+ $ parabola.rb | wavefront write file -F tv -m cli.demo.parabola -
346
+ ```
347
+
241
348
  ## Notes on Options
242
349
 
243
350
  ### Times
data/bin/wavefront CHANGED
@@ -18,11 +18,12 @@ require 'pathname'
18
18
  require 'wavefront/client'
19
19
  require 'wavefront/cli'
20
20
  require 'docopt'
21
+ require 'socket'
21
22
  include Wavefront::Constants
22
23
 
23
24
  def sanitize_keys(hash)
24
25
  hash.each_with_object({}) do |(k, v), aggr|
25
- aggr[k.gsub(/-/, '').to_sym] = v
26
+ aggr[k.delete('-').to_sym] = v
26
27
  end
27
28
  end
28
29
 
@@ -31,12 +32,10 @@ DEF_CF = Pathname.new(ENV['HOME']) + '.wavefront'
31
32
 
32
33
  # The global_opts are available in every command.
33
34
  #
34
- global_opts = %Q(
35
+ global_opts = %(
35
36
  Global options:
36
37
  -c, --config=FILE path to configuration file [default: #{DEF_CF}]
37
38
  -P, --profile=NAME profile in configuration file [default: default]
38
- -E, --endpoint=URI cluster endpoint [default: #{DEFAULT_HOST}]
39
- -t, --token=TOKEN Wavefront authentication token
40
39
  -D, --debug enable debug mode
41
40
  -h, --help show this message
42
41
  )
@@ -45,13 +44,15 @@ Global options:
45
44
  # commands we offer. They must include the global_opts.
46
45
  #
47
46
  usage = {
48
- ts: %Q(
47
+ ts: %(
49
48
  Usage:
50
49
  #{ME} ts [-c file] [-P profile] [-E endpoint] [-t token] [-OD]
51
50
  [-S | -m | -H | -d] [-s time] [-e time] [-f format] [-p num]
52
51
  [-X bool] <query>
53
52
  #{global_opts}
54
53
  Options:
54
+ -E, --endpoint=URI cluster endpoint [default: #{DEFAULT_HOST}]
55
+ -t, --token=TOKEN Wavefront authentication token
55
56
  -S, --seconds query granularity of seconds
56
57
  -m, --minutes query granularity of minutes
57
58
  -H, --hours query granularity of hours
@@ -69,12 +70,14 @@ Options:
69
70
  -O, --includeObsoleteMetrics include metrics unreported for > 4 weeks
70
71
  ),
71
72
 
72
- alerts: %Q(
73
+ alerts: %(
73
74
  Usage:
74
75
  #{ME} alerts [-c file] [-P profile] [-E endpoint] [-t token]
75
76
  [-f format] [-p tag] [ -s tag] <state>
76
77
  #{global_opts}
77
78
  Options:
79
+ -E, --endpoint=URI cluster endpoint [default: #{DEFAULT_HOST}]
80
+ -t, --token=TOKEN Wavefront authentication token
78
81
  -f, --format=STRING output format (#{ALERT_FORMATS.join(', ')})
79
82
  [default: #{DEFAULT_ALERT_FORMAT}]
80
83
  -p, --private=TAG retrieve only alerts with named private tags,
@@ -83,10 +86,10 @@ Options:
83
86
  delimited.
84
87
  ),
85
88
 
86
- event: %Q(
89
+ event: %(
87
90
  Usage:
88
91
  #{ME} event create [-V] [-c file] [-P profile] [-E endpoint] [-t token]
89
- [-d description] [-s time] [-i | -e time] [-l level] [-t type]
92
+ [-d description] [-s time] [-i | -e time] [-l level] [-T type]
90
93
  [-H host] [-n] <event>
91
94
  #{ME} event close [-V] [-c file] [-P profile] [-E endpoint] [-t token]
92
95
  [<event>] [<timestamp>]
@@ -106,7 +109,33 @@ Options:
106
109
 
107
110
  View events in detail using the 'ts' command with the 'events()' function.
108
111
  ),
109
- default: %Q(
112
+
113
+ write: %(
114
+ Usage:
115
+ #{ME} write point [-DV] [-c file] [-P profile] [-E proxy] [-t time]
116
+ [-p port] [-H host] [-n] [-T tag...] <metric> <value>
117
+ #{ME} write file [-DV] [-c file] [-P profile] [-E proxy] [-H host]
118
+ [-p port] [-n] [-F format] [-m metric] [-T tag...] <file>
119
+ #{ME} write --help
120
+ #{global_opts}
121
+ Options:
122
+ -E, --proxy=URI proxy endpoint [default: #{DEFAULT_PROXY}]
123
+ -t, --time=TIME time of data point (omit to use current time)
124
+ -H, --host=STRING source host [default: #{Socket.gethostname}]
125
+ -p, --port=INT Wavefront proxy port [default: #{DEFAULT_PROXY_PORT}]
126
+ -T, --tag=TAG point tag in key=value form
127
+ -F, --format=STRING format of input file or stdin [default: #{DEFAULT_INFILE_FORMAT}]
128
+ -n, --noop show the metric without sending it
129
+ -m, --metric=STRING the metric path to which contents of a file will be
130
+ assigned. If the file contains a metric name, the two
131
+ will be concatenated
132
+ -V, --verbose be verbose
133
+
134
+ Files are whitespace separated, and fields can be defined with the -F
135
+ option. Use 't' for timestamp; 'm' for metric name; 'v' for value
136
+ and 'T' for tags. Put 'T' last.
137
+ ),
138
+ default: %(
110
139
  Wavefront CLI
111
140
 
112
141
  Usage:
@@ -118,6 +147,7 @@ Commands:
118
147
  ts view timeseries data
119
148
  alerts view alerts
120
149
  event open and close events
150
+ write send data points to a Wavefront proxy
121
151
 
122
152
  Use '#{ME} <command> --help' for further information.)
123
153
  }
@@ -126,13 +156,13 @@ Use '#{ME} <command> --help' for further information.)
126
156
  # help/option parser generation.
127
157
  #
128
158
  begin
129
- opts = Docopt::docopt(usage[:default], version: '3.2.0')
159
+ opts = Docopt.docopt(usage[:default], version: '3.2.0')
130
160
  rescue Docopt::Exit => e
131
161
  cmd = ARGV.length > 0 ? ARGV.first.to_sym : nil
132
162
 
133
163
  if usage.keys.include?(cmd)
134
164
  begin
135
- opts = sanitize_keys(Docopt::docopt(usage[cmd]))
165
+ opts = sanitize_keys(Docopt.docopt(usage[cmd]))
136
166
  rescue Docopt::Exit => e
137
167
  abort e.message
138
168
  end
@@ -156,6 +186,14 @@ when :event
156
186
  when :alerts
157
187
  require 'wavefront/cli/alerts'
158
188
  cli = Wavefront::Cli::Alerts.new(opts, [opts[:'<state>']])
189
+ when :write
190
+ if opts[:file]
191
+ require 'wavefront/cli/batch_write'
192
+ cli = Wavefront::Cli::BatchWrite.new(opts, [opts[:'<state>']])
193
+ else
194
+ require 'wavefront/cli/write'
195
+ cli = Wavefront::Cli::Write.new(opts, [opts[:'<state>']])
196
+ end
159
197
  end
160
198
 
161
199
  begin
@@ -0,0 +1,247 @@
1
+ require 'wavefront/client/version'
2
+ require 'wavefront/exception'
3
+ require 'wavefront/constants'
4
+ require 'uri'
5
+ require 'socket'
6
+
7
+ HOSTNAME = Socket.gethostname
8
+
9
+ module Wavefront
10
+ #
11
+ # This class exists to facilitate sending of multiple data points
12
+ # to a Wavefront proxy. It sends points in native Wavefront
13
+ # format.
14
+ #
15
+ # When initializing the instance you can
16
+ # define point tags which will apply to all points sent via that
17
+ # instance.
18
+ #
19
+ # Though we provide methods to do it, it is the developer's
20
+ # responsibility to open and close the socket to the proxy. Points
21
+ # are sent by calling the write() method.
22
+ #
23
+ # The class keeps a count of the points the current instance has
24
+ # sent, dropped, and failed to send, in @summary. The socket is accessed
25
+ # through the instance variable @sock.
26
+ #
27
+ class BatchWriter
28
+ attr_reader :sock, :opts, :summary
29
+ include Wavefront::Constants
30
+
31
+ def initialize(options = {})
32
+ #
33
+ # options is of the form:
34
+ #
35
+ # {
36
+ # tags: a key-value hash of tags which will be applied to
37
+ # every point
38
+ # proxy: the address of the Wavefront proxy
39
+ # port: the port of the Wavefront proxy
40
+ # noop: if this is true, no proxy connection will be made,
41
+ # and instead of sending the points, they will
42
+ # be printed in Wavefront wire format.
43
+ # novalidate: if this is true, points will not be validated.
44
+ # This might make things go marginally quicker
45
+ # if you have done point validation higher up in
46
+ # the chain.
47
+ # verbose: if this is true, many of the methods will report
48
+ # their progress.
49
+ # debug: if this is true, debugging output will be
50
+ # printed.
51
+ # }
52
+ #
53
+ defaults = {
54
+ tags: false,
55
+ proxy: DEFAULT_PROXY,
56
+ port: DEFAULT_PROXY_PORT,
57
+ noop: false,
58
+ novalidate: false,
59
+ verbose: false,
60
+ debug: false,
61
+ }
62
+
63
+ @summary = { sent: 0,
64
+ rejected: 0,
65
+ unsent: 0,
66
+ }
67
+ @opts = setup_options(options, defaults)
68
+
69
+ if opts[:tags]
70
+ valid_tags?(opts[:tags])
71
+ @global_tags = opts[:tags]
72
+ end
73
+ end
74
+
75
+ def setup_options(user, defaults)
76
+ #
77
+ # Fill in some defaults, if the user hasn't supplied them
78
+ #
79
+ defaults.merge(user)
80
+ end
81
+
82
+ def write(points = [], options = {})
83
+ #
84
+ # Points are defined as hashes of the following form:
85
+ # {
86
+ # path: metrics path. String. Mandatory.
87
+ # value: value of metric. Numeric. Mandatory.
88
+ # ts: timestamp as a Time or Date object. default:
89
+ # Time.now. May be omitted or false.
90
+ # source: originating source of metric. default: `hostname`
91
+ # tags: optional hash of key: value point tags
92
+ # }
93
+ #
94
+ # Send multiple points by using an array of the above hashes.
95
+ #
96
+ unless points.is_a?(Hash) || points.is_a?(Array)
97
+ summary[:rejected] += 1
98
+ return false
99
+ end
100
+
101
+ points = [points] if points.is_a?(Hash)
102
+
103
+ points.each do |p|
104
+ p[:ts] = Time.at(p[:ts]) if p[:ts].is_a?(Integer)
105
+ begin
106
+ valid_point?(p)
107
+ rescue Wavefront::Exception::InvalidMetricName,
108
+ Wavefront::Exception::InvalidMetricValue,
109
+ Wavefront::Exception::InvalidTimestamp,
110
+ Wavefront::Exception::InvalidSource,
111
+ Wavefront::Exception::InvalidTag => e
112
+ puts 'Invalid point, skipping.' if opts[:verbose]
113
+ puts "Invalid point: #{p}. (#{e})" if opts[:debug]
114
+ summary[:rejected] += 1
115
+ next
116
+ end
117
+
118
+ send_point(hash_to_wf(p))
119
+ end
120
+ return summary[:rejected] == 0 ? true : false
121
+ end
122
+
123
+ def valid_point?(point)
124
+ #
125
+ # Validate a point so it conforms to the standard described in
126
+ # https://community.wavefront.com/docs/DOC-1031
127
+ #
128
+ return true if opts.key?(:novalidate) && opts[:novalidate]
129
+ valid_path?(point[:path])
130
+ valid_value?(point[:value])
131
+ valid_ts?(point[:ts]) if point[:ts]
132
+ valid_source?(point[:source])
133
+ valid_tags?(point[:tags]) if point[:tags] && point[:tags].length > 0
134
+ true
135
+ end
136
+
137
+ def valid_path?(path)
138
+ fail Wavefront::Exception::InvalidMetricName unless \
139
+ path.is_a?(String) && path.match(/^[a-z0-9\-_\.]+$/) &&
140
+ path.length < 1024
141
+ true
142
+ end
143
+
144
+ def valid_value?(value)
145
+ fail Wavefront::Exception::InvalidMetricValue unless value.is_a?(Numeric)
146
+ true
147
+ end
148
+
149
+ def valid_ts?(ts)
150
+ unless ts.is_a?(Time) || ts.is_a?(Date)
151
+ fail Wavefront::Exception::InvalidTimestamp
152
+ end
153
+ true
154
+ end
155
+
156
+ def valid_source?(path)
157
+ unless path.is_a?(String) && path.match(/^[a-z0-9\-_\.]+$/) &&
158
+ path.length < 1024
159
+ fail Wavefront::Exception::InvalidSource
160
+ end
161
+ true
162
+ end
163
+
164
+ def valid_tags?(tags)
165
+ tags.each do |k, v|
166
+ fail Wavefront::Exception::InvalidTag unless (k.length +
167
+ v.length < 254) && k.match(/^[a-z0-9\-_\.]+$/)
168
+ end
169
+ true
170
+ end
171
+
172
+ def hash_to_wf(p)
173
+ #
174
+ # Convert the hash received by the write() method to a string
175
+ # conforming with that defined in
176
+ # https://community.wavefront.com/docs/DOC-1031
177
+ #
178
+ fail ArgumentError unless p.key?(:path) && p.key?(:value) &&
179
+ p.key?(:source)
180
+
181
+ m = [p[:path], p[:value]]
182
+ m.<< p[:ts].to_i.to_s if p.key?(:ts) && p[:ts]
183
+ m.<< 'source=' + p[:source]
184
+ m.<< tag_hash_to_str(p[:tags]) if p.key?(:tags) && p[:tags]
185
+ m.<< tag_hash_to_str(opts[:tags]) if opts[:tags]
186
+ m.join(' ')
187
+ end
188
+
189
+ def tag_hash_to_str(tags)
190
+ #
191
+ # Convert a hash of tags into a string of key="val" tags. The
192
+ # quoting is recommended in the WF wire-format guide. No tag
193
+ # validation is done here: we assume you used valid_tags()
194
+ #
195
+ return '' unless tags.is_a?(Hash)
196
+ tags.map { |k, v| "#{k}=\"#{v}\"" }.join(' ')
197
+ end
198
+
199
+ def send_point(point)
200
+ #
201
+ # Send a point, which should already be in Wavefront wire
202
+ # format.
203
+ #
204
+ if opts[:noop]
205
+ puts "Would send: #{point}"
206
+ return
207
+ end
208
+
209
+ puts "Sending: #{point}" if opts[:verbose] || opts[:debug]
210
+
211
+ begin
212
+ sock.puts(point)
213
+ summary[:sent] += 1
214
+ return true
215
+ rescue
216
+ summary[:unsent] += 1
217
+ puts 'WARNING: failed to send point.'
218
+ return false
219
+ end
220
+ end
221
+
222
+ def open_socket
223
+ #
224
+ # Open a socket to a Wavefront proxy, putting the descriptor
225
+ # in instance variable @sock.
226
+ #
227
+ if opts[:noop]
228
+ puts 'No-op requested. Not opening connection to proxy.'
229
+ return true
230
+ end
231
+
232
+ puts "Connecting to #{opts[:proxy]}:#{opts[:port]}." if opts[:verbose]
233
+
234
+ begin
235
+ @sock = TCPSocket.new(opts[:proxy], opts[:port])
236
+ rescue
237
+ raise Wavefront::Exception::InvalidEndpoint
238
+ end
239
+ end
240
+
241
+ def close_socket
242
+ return if opts[:noop]
243
+ puts 'Closing connection to proxy.' if opts[:verbose]
244
+ sock.close
245
+ end
246
+ end
247
+ end