logstash-output-sumologic 1.0.4 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4bf404a07cdd75110c532fb0279686b95d3627df
4
- data.tar.gz: f6a8e2b65b662c8f20575ed56ffba768bad20773
3
+ metadata.gz: e52d2f2af928de4e3f0318396239ad13552c8381
4
+ data.tar.gz: adbeece1b46c5a0409eff70626e81ac717e4dfb1
5
5
  SHA512:
6
- metadata.gz: 0b01a0de20c6daa4ce28d0ece3e3d34b6933a7b2ded07807e9fc0fdd8f48898f04fb5c5b3f97a4d8eac0cdd14764a2b458f742c5045650fdd806f465fa151126
7
- data.tar.gz: c0a8f598ad9a36610bc1c3bbfd986e8c1fbef2988dece36d46288402f0ff6a1e06292f2a14253290345e8e0a83cc6dd2d78f2d2f27aab2314fd9250dcc27b193
6
+ metadata.gz: b34303f871a5efbbd9a9a47f0f0eee6edd59672eb321daa7c25883fb0d4972d249eea597e64e4cf50a26e2f9ed9271ce86b010214e60f3357a16b093e6cd1cfb
7
+ data.tar.gz: 6b0e002735a97349cf933f35d16bb7a5cf69c77833c295535bf25c7ecafe4e7c28efbb73d6548ba9573f6d2edb368051bf169e652ada4c792e93ad258ca3ca21
data/CHANGELOG.md CHANGED
@@ -1,23 +1,5 @@
1
- ## 1.0.0
2
- - First public release
3
-
4
- ### 1.0.1
5
- - Update gem description
1
+ ## 1.1.0
2
+ - Support metrics sending
6
3
 
7
- ### 1.0.2
8
- - Support using `%{@json}` in format to send event in json format
9
- - Support pararmeter `json_mapping` to filter json fields. For example:
10
- ```
11
- json_mapping => {
12
- "foo" => "%{@timestamp}"
13
- "bar" => "%{message}"
14
- }
15
- ```
16
- will create message as:
17
- ```
18
- {"foo":"2016-07-27T18:37:59.460Z","bar":"hello world"}
19
- {"foo":"2016-07-27T18:38:01.222Z","bar":"bye!"}
20
- ```
21
-
22
- ### 1.0.3
23
- - Remove version limitation so it works with Log Stash 5.0.0 core
4
+ ## 1.0.0
5
+ - Initial release
data/DEVELOPER.md CHANGED
@@ -1,2 +1,27 @@
1
1
  # logstash-output-sumologic
2
2
  Logstash output plugin for delivering log to Sumo Logic cloud service through HTTP source.
3
+
4
+ # How to build .gem file from repository
5
+ Open logstash-output-sumologic.gemspec and make any necessary configuration changes.
6
+ In your local Git clone, run:
7
+ ```sh
8
+ gem build logstash-output-sumologic.gemspec
9
+ ```
10
+ You will get a .gem file in the same directory as `logstash-output-sumologic-x.y.z.gem`
11
+ Remove old version of plugin (optional):
12
+ ```sh
13
+ bin/logstash-plugin remove logstash-output-sumologic
14
+ ```
15
+ And then install the plugin locally:
16
+ ```sh
17
+ bin/logstash-plugin install <full path of .gem>
18
+ ```
19
+
20
+ # How to run test with rspec
21
+ The test requires JRuby to run. So you need to install [JRuby](http://jruby.org/) and [RVM](https://rvm.io/) (for switching between JRuby and Ruby) first.
22
+ And then run:
23
+ ```bash
24
+ rvm use jruby
25
+ rspec spec/
26
+ ```
27
+
data/README.md CHANGED
@@ -1,9 +1,10 @@
1
1
  # Logstash Sumo Logic Output Plugin
2
2
 
3
- This is a plugin for [Logstash](https://github.com/elastic/logstash).
3
+ This is an output plugin for [Logstash](https://github.com/elastic/logstash).
4
4
  It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
5
5
 
6
6
  ## Getting Started
7
+ This guide is for the users just want download the binary and make the plugin work. For the developer, please refer to the [Developer Guide](DEVELOPER.md)
7
8
 
8
9
  ### 1. Create a Sumo Logic HTTP source
9
10
  Create a [Sumo Logic](https://www.sumologic.com/) free account if you currently don't have one.
@@ -16,57 +17,67 @@ https://events.sumologic.net/receiver/v1/http/XXXXXXXXXX
16
17
  ### 2. Install LogStash on your machine
17
18
  Following this [instruction](https://www.elastic.co/guide/en/logstash/current/getting-started-with-logstash.html) to download and install LogStash. This plugin requires Logstash 2.3 or higher to work.
18
19
 
19
- ### 3. Build your plugin gem
20
- In your local Git clone, running:
20
+ ### 3. Install latest Logstash Sumo Logic Output plugin from [RubyGems](https://rubygems.org/gems/logstash-output-sumologic)
21
21
  ```sh
22
- gem build logstash-output-sumologic.gemspec
22
+ bin/logstash-plugin install logstash-output-sumologic
23
23
  ```
24
- You will get a .gem file as `logstash-output-sumologic-1.0.0.gem`
25
-
26
- ### 4. Install plugin into LogStash
27
- In the Logstash home, running:
28
- ```sh
29
- bin/logstash-plugin install <path of .gem>
30
- ```
31
-
32
- ### 5. Start Logstash and send log
24
+ ### 4. Start Logstash and send log
33
25
  In the Logstash home, running:
34
26
  ```sh
35
- bin/logstash -e 'input{stdin{}}output{sumologic{url=>"<url from step 1>"}}'
27
+ bin/logstash -e 'input{stdin{}}output{sumologic{url=>"<URL from step 1>"}}'
36
28
  ```
37
29
  This will send any input from console to Sumo Logic cloud service.
38
30
 
39
- ### 6. Get result from Sumo Logic web app
40
- Logon to Sumo Logic [web app](https://prod-www.sumologic.net/ui/) and run [Search](http://help.sumologic.com/Search) or [Live Tail](http://help.sumologic.com/Search/Live_Tail)
41
-
42
- ### Furthermore
43
- - Try it with different input/filter/codec plugins
44
- - Start LogStash as a service/daemon in your production environment
45
- - Report any issue or idea through [Git Hub](https://github.com/SumoLogic/logstash-output-sumologic)
46
-
47
- ## Parameters
48
- This plugin is based on [logstash-mixin-http_client](https://github.com/logstash-plugins/logstash-mixin-http_client) thus it supports all parameters like proxy, authentication, retry, etc.
31
+ ### 5. Try out samples
32
+ Open samples/sample-logs.conf, replace #URL# placeholder as real URL got from step 1
49
33
 
50
- And it supports following additional prarmeters:
34
+ Launch sample with:
35
+ ```sh
36
+ bin/logstash -f samples/log.conf
51
37
  ```
52
- # The URL to send logs to. This should be given when creating a HTTP Source
53
- # on Sumo Logic web app. See http://help.sumologic.com/Send_Data/Sources/HTTP_Source
54
- config :url, :validate => :string, :required => true
55
-
56
- # Include extra HTTP headers on request if needed
57
- config :extra_headers, :validate => :hash, :default => []
58
-
59
- # The formatter of message, by default is message with timestamp and host as prefix
60
- config :format, :validate => :string, :default => "%{@timestamp} %{host} %{message}"
61
-
62
- # Hold messages for at least (x) seconds as a pile; 0 means sending every events immediately
63
- config :interval, :validate => :number, :default => 0
38
+ The input from console will be sent to Sumo Logic cloud service as log lines.
64
39
 
65
- # Compress the payload
66
- config :compress, :validate => :boolean, :default => false
40
+ Open samples/sample-metrics.conf, replace #URL# placeholder as real URL got from step 1
41
+ (This sample may require installing the [plugins-filters-metrics](https://www.elastic.co/guide/en/logstash/current/plugins-filters-metrics.html) plugin first)
67
42
 
43
+ Launch sample with:
44
+ ```sh
45
+ bin/logstash -f samples/metrics.conf
68
46
  ```
47
+ A mocked event will be sent to Sumo Logic cloud service as 1 minute and 15 minutes rate metrics.
69
48
 
49
+ ### 6. Get result from Sumo Logic web app
50
+ Logon to Sumo Logic [web app](https://prod-www.sumologic.net/ui/) and run
51
+ - [Log Search](http://help.sumologic.com/Search)
52
+ - [Live Tail](http://help.sumologic.com/Search/Live_Tail)
53
+ - [Metrics Search](https://help.sumologic.com/Metrics)
70
54
 
55
+ ## What's Next
56
+ - Try it with different input/filter/codec plugins
57
+ - Start LogStash as a service/daemon in your production environment
58
+ - Report any issue or idea through [Git Hub](https://github.com/SumoLogic/logstash-output-sumologic)
71
59
 
60
+ ## Parameters of Plugin
61
+ | Parameter | Type | Required? | Default value | Decription |
62
+ | ------------------- | ------- | --------- | ------------- | --------------------- |
63
+ | `url` | string | Yes | | HTTP Source URL
64
+ | `source_category` | string | No | | Source category to appear when searching in Sumo Logic by `_sourceCategory`. If not specified, the source category of the HTTP source will be used.
65
+ | `source_name` | string | No | | Source name to appear when searching in Sumo Logic by `_sourceName`.
66
+ | `source_host` | string | No | | Source host to appear when searching in Sumo Logic by `_sourceHost`. If not specified, it will be the machine host name.
67
+ | `extra_headers` | hash | No | | Extra fields need to be send in HTTP header.
68
+ | `compress` | boolean | No | `false` | Enable or disable compression.
69
+ | `compress_encoding` | string | No | `'deflate'` | Encoding method of comressing, can only be `'deflate'` or `'gzip'`.
70
+ | `interval` | number | No | `0` | The maximum time for waiting before send in batch, in ms.
71
+ | `format` | string | No | `"%{@timestamp} %{host} %{message}"` | For log only, the formatter of log lines. Use `%{@json}` as the placeholder for whole event json.
72
+ | `json_mapping` | hash | No | | Override the structure of `{@json}` tag with the given key value pairs.
73
+ | `metrics` | hash | No | | If defined, the event will be sent as metrics. Keys will be the metrics name and values will be the metrics value.
74
+ | `metrics_format` | string | No | `'cabon2'` | Metrics format, can only be `'graphite'` or `'carbon2'`.
75
+ | `metrics_name` | string | No | `*` | Define the metric name looking, the placeholder '*' will be replaced with the actual metric name.
76
+ | `intrinsic_tags` | hash | No | | For carbon2 format only, send extra intrinsic key-value pairs other than `metric` (which is the metric name).
77
+ | `meta_tags` | hash | No | | For carbon2 format only, send metadata key-value pairs.
78
+ | `fields_as_metrics` | boolean | No | `false` | If `true`, all fields in logstash event with number value will be sent as a metrics (with filtering by `fields_include` and `fields_exclude` ; the `metics` parameter is ignored.
79
+ | `fields_include` | array | No | all fields | Working with `fields_as_metrics` parameter, only the fields which full name matching these RegEx pattern(s) will be included in metrics.
80
+ | `fields_exclude` | array | No | none | Working with `fields_as_metrics` parameter, the fields which full name matching these RegEx pattern(s) will be ignored in metrics.
81
+
82
+ This plugin is based on [logstash-mixin-http_client](https://github.com/logstash-plugins/logstash-mixin-http_client) thus we also support all HTTP layer parameters like proxy, authentication, retry, etc.
72
83
 
@@ -6,6 +6,7 @@ require "logstash/plugin_mixins/http_client"
6
6
  require 'thread'
7
7
  require "uri"
8
8
  require "zlib"
9
+ require "stringio"
9
10
 
10
11
  # Now you can use logstash to deliver logs to Sumo Logic
11
12
  #
@@ -17,35 +18,98 @@ class LogStash::Outputs::SumoLogic < LogStash::Outputs::Base
17
18
  include LogStash::PluginMixins::HttpClient
18
19
 
19
20
  config_name "sumologic"
20
-
21
+
22
+ CONTENT_TYPE = "Content-Type"
23
+ CONTENT_TYPE_LOG = "text/plain"
24
+ CONTENT_TYPE_GRAPHITE = "application/vnd.sumologic.graphite"
25
+ CONTENT_TYPE_CARBON2 = "application/vnd.sumologic.carbon2"
26
+ CATEGORY_HEADER = "X-Sumo-Category"
27
+ HOST_HEADER = "X-Sumo-Host"
28
+ NAME_HEADER = "X-Sumo-Name"
29
+ CLIENT_HEADER = "X-Sumo-Client"
30
+ TIMESTAMP_FIELD = "@timestamp"
31
+ METRICS_NAME_PLACEHOLDER = "*"
32
+ GRAPHITE = "graphite"
33
+ CARBON2 = "carbon2"
34
+ CONTENT_ENCODING = "Content-Encoding"
35
+ DEFLATE = "deflate"
36
+ GZIP = "gzip"
37
+ ALWAYS_EXCLUDED = [ "@timestamp", "@version" ]
38
+
21
39
  # The URL to send logs to. This should be given when creating a HTTP Source
22
40
  # on Sumo Logic web app. See http://help.sumologic.com/Send_Data/Sources/HTTP_Source
23
41
  config :url, :validate => :string, :required => true
24
42
 
43
+ # Define the source category metadata
44
+ config :source_category, :validate => :string
45
+
46
+ # Define the source host metadata
47
+ config :source_host, :validate => :string
48
+
49
+ # Define the source name metadata
50
+ config :source_name, :validate => :string
51
+
25
52
  # Include extra HTTP headers on request if needed
26
- config :extra_headers, :validate => :hash, :default => []
53
+ config :extra_headers, :validate => :hash
27
54
 
28
- # The formatter of message, by default is message with timestamp and host as prefix
29
- # use %{@json} tag to send whole event
30
- config :format, :validate => :string, :default => "%{@timestamp} %{host} %{message}"
55
+ # Compress the payload
56
+ config :compress, :validate => :boolean, :default => false
57
+
58
+ # The encoding method of compress
59
+ config :compress_encoding, :validate =>:string, :default => DEFLATE
31
60
 
32
61
  # Hold messages for at least (x) seconds as a pile; 0 means sending every events immediately
33
62
  config :interval, :validate => :number, :default => 0
34
63
 
35
- # Compress the payload
36
- config :compress, :validate => :boolean, :default => false
64
+ # The formatter of log message, by default is message with timestamp and host as prefix
65
+ # Use %{@json} tag to send whole event
66
+ config :format, :validate => :string, :default => "%{@timestamp} %{host} %{message}"
37
67
 
38
- # This lets you choose the structure and parts of the event that are sent in @json tag.
68
+ # Override the structure of @json tag with the given key value pairs
39
69
  config :json_mapping, :validate => :hash
70
+
71
+ # Send metric(s) if configured. This is a hash with k as metric name and v as metric value
72
+ # Both metric names and values support dynamic strings like %{host}
73
+ # For example:
74
+ # metrics => { "%{host}/uptime" => "%{uptime_1m}" }
75
+ config :metrics, :validate => :hash
76
+
77
+ # Create metric(s) automatically from @json fields if configured.
78
+ config :fields_as_metrics, :validate => :boolean, :default => false
79
+
80
+ config :fields_include, :validate => :array, :default => [ ]
81
+
82
+ config :fields_exclude, :validate => :array, :default => [ ]
83
+
84
+ # Defines the format of the metric, support "graphite" or "carbon2"
85
+ config :metrics_format, :validate => :string, :default => CARBON2
40
86
 
87
+ # Define the metric name looking, the placeholder '*' will be replaced with the actual metric name
88
+ # For example:
89
+ # metrics => { "uptime.1m" => "%{uptime_1m}" }
90
+ # metrics_name => "mynamespace.*"
91
+ # will produce metrics as:
92
+ # "mynamespace.uptime.1m xxx 1234567"
93
+ config :metrics_name, :validate => :string, :default => METRICS_NAME_PLACEHOLDER
94
+
95
+ # For carbon2 metrics format only, define the intrinsic tags (which will be used to identify the metrics)
96
+ # There is always an intrinsic tag as "metric" which value is from metrics_name
97
+ config :intrinsic_tags, :validate => :hash, :default => {}
98
+
99
+ # For carbon2 metrics format only, define the meta tags (which will NOT be used to identify the metrics)
100
+ config :meta_tags, :validate => :hash, :default => {}
101
+
41
102
  public
42
103
  def register
104
+ @source_host = `hostname`.strip unless @source_host
105
+
43
106
  # initialize request pool
44
107
  @request_tokens = SizedQueue.new(@pool_max)
45
108
  @pool_max.times { |t| @request_tokens << true }
46
109
  @timer = Time.now
47
110
  @pile = Array.new
48
111
  @semaphore = Mutex.new
112
+ connect
49
113
  end # def register
50
114
 
51
115
  public
@@ -61,23 +125,9 @@ class LogStash::Outputs::SumoLogic < LogStash::Outputs::Base
61
125
  return
62
126
  end
63
127
 
64
- content = format_event(event)
128
+ content = event2content(event)
129
+ queue_and_send(content)
65
130
 
66
- if @interval <= 0 # means send immediately
67
- send_request(content)
68
- return
69
- end
70
-
71
- @semaphore.synchronize {
72
- now = Time.now
73
- @pile << content
74
-
75
- if now - @timer > @interval # ready to send
76
- send_request(@pile.join($/))
77
- @timer = now
78
- @pile.clear
79
- end
80
- }
81
131
  end # def receive
82
132
 
83
133
  public
@@ -88,15 +138,35 @@ class LogStash::Outputs::SumoLogic < LogStash::Outputs::Base
88
138
  }
89
139
  client.close
90
140
  end # def close
141
+
142
+
143
+ private
144
+ def connect
145
+ # TODO: ping endpoint make sure config correct
146
+ end # def connect
91
147
 
92
148
  private
93
- def send_request(content)
94
- token = @request_tokens.pop
95
- body = if @compress
96
- Zlib::Deflate.deflate(content)
149
+ def queue_and_send(content)
150
+ if @interval <= 0 # means send immediately
151
+ send_request(content)
97
152
  else
98
- content
153
+ @semaphore.synchronize {
154
+ now = Time.now
155
+ @pile << event
156
+
157
+ if now - @timer > @interval # ready to send
158
+ send_request(@pile.join($/))
159
+ @timer = now
160
+ @pile.clear
161
+ end
162
+ }
99
163
  end
164
+ end
165
+
166
+ private
167
+ def send_request(content)
168
+ token = @request_tokens.pop
169
+ body = compress(content)
100
170
  headers = get_headers()
101
171
 
102
172
  request = client.send(:parallel).send(:post, @url, :body => body, :headers => headers)
@@ -128,29 +198,146 @@ class LogStash::Outputs::SumoLogic < LogStash::Outputs::Base
128
198
  request.call
129
199
  end # def send_request
130
200
 
201
+ private
202
+ def compress(content)
203
+ if @compress
204
+ if @compress_encoding == GZIP
205
+ result = gzip(content)
206
+ result.bytes.to_a.pack('c*')
207
+ else
208
+ Zlib::Deflate.deflate(content)
209
+ end
210
+ else
211
+ content
212
+ end
213
+ end # def compress
214
+
215
+ private
216
+ def gzip(content)
217
+ stream = StringIO.new("w")
218
+ stream.set_encoding("ASCII")
219
+ gz = Zlib::GzipWriter.new(stream)
220
+ gz.write(content)
221
+ gz.close
222
+ stream.string.bytes.to_a.pack('c*')
223
+ end # def gzip
224
+
131
225
  private
132
226
  def get_headers()
133
- base = { "Content-Type" => "text/plain" }
134
- base["Content-Encoding"] = "deflate" if @compress
135
- base.merge(@extra_headers)
227
+
228
+ base = {}
229
+ base = @extra_headers if @extra_headers.is_a?(Hash)
230
+
231
+ base[CATEGORY_HEADER] = @source_category if @source_category
232
+ base[HOST_HEADER] = @source_host if @source_host
233
+ base[NAME_HEADER] = @source_name if @source_name
234
+ base[CLIENT_HEADER] = 'logstash-output-sumologic'
235
+
236
+ if @compress
237
+ if @compress_encoding == GZIP
238
+ base[CONTENT_ENCODING] = GZIP
239
+ elsif
240
+ base[CONTENT_ENCODING] = DEFLATE
241
+ else
242
+ log_failure(
243
+ "Unrecogonized compress encoding",
244
+ :encoding => @compress_encoding
245
+ )
246
+ end
247
+ end
248
+
249
+ if @metrics || @fields_as_metrics
250
+ if @metrics_format == CARBON2
251
+ base[CONTENT_TYPE] = CONTENT_TYPE_CARBON2
252
+ elsif @metrics_format == GRAPHITE
253
+ base[CONTENT_TYPE] = CONTENT_TYPE_GRAPHITE
254
+ else
255
+ log_failure(
256
+ "Unrecogonized metrics format",
257
+ :format => @metrics_format
258
+ )
259
+ end
260
+ else
261
+ base[CONTENT_TYPE] = CONTENT_TYPE_LOG
262
+ end
263
+
264
+ base
265
+
136
266
  end # def get_headers
137
267
 
138
268
  private
139
- def format_event(event)
140
- if @format.to_s.strip.length == 0
141
- LogStash::Json.dump(map_event(event))
269
+ def event2content(event)
270
+ if @metrics || @fields_as_metrics
271
+ event2metrics(event)
142
272
  else
143
- f = if @format.include? "%{@json}"
144
- @format.gsub("%{@json}", LogStash::Json.dump(map_event(event)))
273
+ event2log(event)
274
+ end
275
+ end # def event2content
276
+
277
+ private
278
+ def event2log(event)
279
+ @format = "%{@json}" if @format.nil? || @format.empty?
280
+ expand(@format, event)
281
+ end # def event2log
282
+
283
+ private
284
+ def event2metrics(event)
285
+ timestamp = get_timestamp(event)
286
+ source = expand_hash(@metrics, event) unless @fields_as_metrics
287
+ source = event_as_metrics(event) if @fields_as_metrics
288
+ source.flat_map { |key, value|
289
+ get_single_line(event, key, value, timestamp)
290
+ }.reject(&:nil?).join("\n")
291
+ end # def event2metrics
292
+
293
+ def event_as_metrics(event)
294
+ hash = event2hash(event)
295
+ acc = {}
296
+ hash.keys.each do |field|
297
+ value = hash[field]
298
+ dotify(acc, field, value, nil)
299
+ end
300
+ acc
301
+ end # def event_as_metrics
302
+
303
+ def get_single_line(event, key, value, timestamp)
304
+ full = get_metrics_name(event, key)
305
+ if !ALWAYS_EXCLUDED.include?(full) && \
306
+ (fields_include.empty? || fields_include.any? { |regexp| full.match(regexp) }) && \
307
+ !(fields_exclude.any? {|regexp| full.match(regexp)}) && \
308
+ is_number?(value)
309
+ if @metrics_format == CARBON2
310
+ @intrinsic_tags["metric"] = full
311
+ "#{hash2line(@intrinsic_tags, event)} #{hash2line(@meta_tags, event)}#{value} #{timestamp}"
145
312
  else
146
- @format
313
+ "#{full} #{value} #{timestamp}"
147
314
  end
148
- event.sprintf(f)
149
315
  end
150
- end # def format_event
316
+ end # def get_single_line
317
+
318
+ def dotify(acc, key, value, prefix)
319
+ pk = prefix ? "#{prefix}.#{key}" : key.to_s
320
+ if value.is_a?(Hash)
321
+ value.each do |k, v|
322
+ dotify(acc, k, v, pk)
323
+ end
324
+ elsif value.is_a?(Array)
325
+ value.each_with_index.map { |v, i|
326
+ dotify(acc, i.to_s, v, pk)
327
+ }
328
+ else
329
+ acc[pk] = value
330
+ end
331
+ end # def dotify
332
+
333
+ private
334
+ def expand(template, event)
335
+ template = template.gsub("%{@json}", LogStash::Json.dump(event2hash(event))) if template.include? "%{@json}"
336
+ event.sprintf(template)
337
+ end # def expand
151
338
 
152
339
  private
153
- def map_event(event)
340
+ def event2hash(event)
154
341
  if @json_mapping
155
342
  @json_mapping.reduce({}) do |acc, kv|
156
343
  k, v = kv
@@ -161,7 +348,45 @@ class LogStash::Outputs::SumoLogic < LogStash::Outputs::Base
161
348
  event.to_hash
162
349
  end
163
350
  end # def map_event
351
+
352
+ private
353
+ def is_number?(me)
354
+ me.to_f.to_s == me.to_s || me.to_i.to_s == me.to_s
355
+ end
356
+
357
+ private
358
+ def expand_hash(hash, event)
359
+ hash.reduce({}) do |acc, kv|
360
+ k, v = kv
361
+ exp_k = expand(k, event)
362
+ exp_v = expand(v, event)
363
+ acc[exp_k] = exp_v
364
+ acc
365
+ end # def expand_hash
366
+ end
164
367
 
368
+ private
369
+ def get_timestamp(event)
370
+ event.get(TIMESTAMP_FIELD).to_i
371
+ end # def get_timestamp
372
+
373
+ private
374
+ def get_metrics_name(event, name)
375
+ name = @metrics_name.gsub(METRICS_NAME_PLACEHOLDER, name) if @metrics_name
376
+ event.sprintf(name)
377
+ end # def get_metrics_name
378
+
379
+ private
380
+ def hash2line(hash, event)
381
+ if (hash.is_a?(Hash) && !hash.empty?)
382
+ expand_hash(hash, event).flat_map { |k, v|
383
+ "#{k}=#{v} "
384
+ }.join()
385
+ else
386
+ ""
387
+ end
388
+ end # hash2line
389
+
165
390
  private
166
391
  def log_failure(message, opts)
167
392
  @logger.error(message, opts)