logstash-output-graphite 1.0.0 → 1.0.1

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: 49103b7165665819b05df09701b9863faccedc9b
4
- data.tar.gz: 6a814793a21fcc8127803e0995d06dd2672d0871
3
+ metadata.gz: aaa1548ced5cd4e3cc3f3724c078a13e9c507fe5
4
+ data.tar.gz: 9c2309ed86b7c371bd35ad4d21ed7bb96536b48d
5
5
  SHA512:
6
- metadata.gz: 086f17b7bdf39a6de526947d03e23f9db690397b34d92eb7de4bf1610d5b139bfc83a0b61b9ceb339f4e546e891f16d659df50c8bf99cec574f09b92e4a31827
7
- data.tar.gz: a49c4df036d303676cdc6e87d841d1a8f6d091b06ede88011578978116a8473e1b9b0fa12504e3f85648d4e9f172267a1f9b350d0c9734aa7a6372102b6ff2d4
6
+ metadata.gz: 26aedf2dae122c104225a6297484edd858457e553b340113d2458c2c4ec963a80133e8505f52cc0e348135db0ab5eb6ee654e8b15419906a1ba7a3f2f62dc7f4
7
+ data.tar.gz: dd176d82e9c199bb713dc8c8e3ecdda2b30117692994b9beef372d6531c125a6ff1056ae13d6bec070fc62d33f80b4637f8b567dd9404fef663d843a2f9a7571
data/CHANGELOG.md CHANGED
@@ -0,0 +1,2 @@
1
+ - 1.0.1
2
+ * Added support for nested hashes as values
@@ -67,6 +67,18 @@ class LogStash::Outputs::Graphite < LogStash::Outputs::Base
67
67
  # NOTE: If no metrics_format is defined, the name of the metric will be used as fallback.
68
68
  config :metrics_format, :validate => :string, :default => DEFAULT_METRICS_FORMAT
69
69
 
70
+ # When hashes are passed in as values they are broken out into a dotted notation
71
+ # For instance if you configure this plugin with
72
+ # # [source,ruby]
73
+ # metrics => "mymetrics"
74
+ #
75
+ # and "mymetrics" is a nested hash of '{a => 1, b => { c => 2 }}'
76
+ # this plugin will generate two metrics: a => 1, and b.c => 2 .
77
+ # If you've specified a 'metrics_format' it will respect that,
78
+ # but you still may want control over the separator within these nested key names.
79
+ # This config setting changes the separator from the '.' default.
80
+ config :nested_object_separator, :validate => :string, :default => "."
81
+
70
82
  def register
71
83
  @include_metrics.collect!{|regexp| Regexp.new(regexp)}
72
84
  @exclude_metrics.collect!{|regexp| Regexp.new(regexp)}
@@ -106,26 +118,9 @@ class LogStash::Outputs::Graphite < LogStash::Outputs::Base
106
118
 
107
119
  # Graphite message format: metric value timestamp\n
108
120
 
109
- messages = []
110
- timestamp = event[@timestamp_field].to_i
111
-
112
- if @fields_are_metrics
113
- @logger.debug("got metrics event", :metrics => event.to_hash)
114
- event.to_hash.each do |metric,value|
115
- next if EXCLUDE_ALWAYS.include?(metric)
116
- next unless @include_metrics.empty? || @include_metrics.any? { |regexp| metric.match(regexp) }
117
- next if @exclude_metrics.any? {|regexp| metric.match(regexp)}
118
- messages << "#{construct_metric_name(metric)} #{event.sprintf(value.to_s).to_f} #{timestamp}"
119
- end
120
- else
121
- @metrics.each do |metric, value|
122
- @logger.debug("processing", :metric => metric, :value => value)
123
- metric = event.sprintf(metric)
124
- next unless @include_metrics.any? {|regexp| metric.match(regexp)}
125
- next if @exclude_metrics.any? {|regexp| metric.match(regexp)}
126
- messages << "#{construct_metric_name(event.sprintf(metric))} #{event.sprintf(value).to_f} #{timestamp}"
127
- end
128
- end
121
+ messages = @fields_are_metrics ?
122
+ messages_from_event_fields(event, @include_metrics, @exclude_metrics) :
123
+ messages_from_event_metrics(event, @metrics)
129
124
 
130
125
  if messages.empty?
131
126
  @logger.debug("Message is empty, not sending anything to Graphite", :messages => messages, :host => @host, :port => @port)
@@ -145,6 +140,69 @@ class LogStash::Outputs::Graphite < LogStash::Outputs::Base
145
140
  retry if @resend_on_failure
146
141
  end
147
142
  end
148
-
149
143
  end # def receive
144
+
145
+ private
146
+
147
+ def messages_from_event_fields(event, include_metrics, exclude_metrics)
148
+ timestamp = event_timestamp(event)
149
+ @logger.debug? && @logger.debug("got metrics event", :metrics => event.to_hash)
150
+ event.to_hash.flat_map do |metric,value|
151
+ next if EXCLUDE_ALWAYS.include?(metric)
152
+ next unless include_metrics.empty? || include_metrics.any? { |regexp| metric.match(regexp) }
153
+ next if exclude_metrics.any? {|regexp| metric.match(regexp)}
154
+
155
+ metrics_lines_for_event(event, metric, value, timestamp)
156
+ end
157
+ end
158
+
159
+ def messages_from_event_metrics(event, metrics)
160
+ timestamp = event_timestamp(event)
161
+ metrics.flat_map do |metric, value|
162
+ @logger.debug("processing", :metric => metric, :value => value)
163
+ metric = event.sprintf(metric)
164
+ next unless @include_metrics.any? {|regexp| metric.match(regexp)}
165
+ next if @exclude_metrics.any? {|regexp| metric.match(regexp)}
166
+
167
+ metrics_lines_for_event(event, metric, value, timestamp)
168
+ end
169
+ end
170
+
171
+ def event_timestamp(event)
172
+ event[@timestamp_field].to_i
173
+ end
174
+
175
+ def metrics_lines_for_event(event, metric, value, timestamp)
176
+ if event[metric].is_a?(Hash)
177
+ dotify(event[metric], metric).map do |k,v|
178
+ metrics_line(k, v, timestamp)
179
+ end
180
+ else
181
+ metrics_line(event.sprintf(metric), event.sprintf(value).to_f, timestamp)
182
+ end
183
+ end
184
+
185
+ def metrics_line(name, value, timestamp)
186
+ "#{construct_metric_name(name)} #{value} #{timestamp}"
187
+ end
188
+
189
+ # Take a nested ruby hash of the form {:a => {:b => 2}, c: => 3} and
190
+ # turn it into a hash of the form
191
+ # { "a.b" => 2, "c" => 3}
192
+ def dotify(hash,prefix=nil)
193
+ hash.reduce({}) do |acc,kv|
194
+ k,v = kv
195
+ pk = prefix ? "#{prefix}#{@nested_object_separator}#{k}" : k.to_s
196
+ if v.is_a?(Hash)
197
+ acc.merge!(dotify(v, pk))
198
+ elsif v.is_a?(Array)
199
+ # There's no right answer here, so we do nothing
200
+ @logger.warn("Array values not supported for graphite metrics! Ignoring #{hash} @ #{prefix}")
201
+ else
202
+ acc[pk] = v
203
+ end
204
+ acc
205
+ end
206
+ end
207
+
150
208
  end # class LogStash::Outputs::Graphite
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-output-graphite'
4
- s.version = '1.0.0'
4
+ s.version = '1.0.1'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "This output allows you to pull metrics from your logs and ship them to Graphite"
7
7
  s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
@@ -36,15 +36,25 @@ describe LogStash::Outputs::Graphite do
36
36
  let(:event) { LogStash::Event.new("foo" => "123") }
37
37
 
38
38
  context "match one key" do
39
- it "generate one element" do
39
+ it "should generate one element" do
40
40
  expect(server.size).to eq(1)
41
41
  end
42
42
 
43
- it "match the generated key" do
43
+ it "should match the generated key" do
44
44
  line = server.pop
45
45
  expect(line).to match(/^foo.bar.sys.data.foo 123.0 \d{10,}\n$/)
46
46
  end
47
47
  end
48
+
49
+ context "when matching a nested hash" do
50
+ let(:event) { LogStash::Event.new("foo" => {"a" => 3, "c" => {"d" => 2}}) }
51
+
52
+ it "should create the proper formatted lines" do
53
+ lines = [server.pop, server.pop].sort # Put key 'a' first
54
+ expect(lines[0]).to match(/^foo.bar.sys.data.foo.a 3 \d{10,}\n$/)
55
+ expect(lines[1]).to match(/^foo.bar.sys.data.foo.c.d 2 \d{10,}\n$/)
56
+ end
57
+ end
48
58
  end
49
59
 
50
60
  context "match all keys" do
@@ -124,6 +134,16 @@ describe LogStash::Outputs::Graphite do
124
134
  line = server.pop
125
135
  expect(line).to match(/^custom.foo 123.0 \d{10,}\n$/)
126
136
  end
137
+
138
+ context "when matching a nested hash" do
139
+ let(:event) { LogStash::Event.new("custom.foo" => {"a" => 3, "c" => {"d" => 2}}) }
140
+
141
+ it "should create the proper formatted lines" do
142
+ lines = [server.pop, server.pop].sort # Put key 'a' first
143
+ expect(lines[0]).to match(/^custom.foo.a 3 \d{10,}\n$/)
144
+ expect(lines[1]).to match(/^custom.foo.c.d 2 \d{10,}\n$/)
145
+ end
146
+ end
127
147
  end
128
148
  end
129
149
  end
@@ -144,4 +164,34 @@ describe LogStash::Outputs::Graphite do
144
164
  expect(line).to match(/^foo 1.0 #{timestamp_new}\n$/)
145
165
  end
146
166
  end
167
+
168
+ describe "dotifying a hash" do
169
+ let(:event) { LogStash::Event.new( "metrics" => hash) }
170
+ let(:dotified) { LogStash::Outputs::Graphite.new().send(:dotify, hash) }
171
+
172
+ context "with a complex hash" do
173
+ let(:hash) { {:a => 2, :b => {:c => 3, :d => 4, :e => {:f => 5}}} }
174
+
175
+ it "should dottify correctly" do
176
+ expect(dotified).to eql({"a" => 2, "b.c" => 3, "b.d" => 4, "b.e.f" => 5})
177
+ end
178
+ end
179
+
180
+ context "with a simple hash" do
181
+ let(:hash) { {:a => 2, 5 => 4} }
182
+
183
+ it "should do nothing more than stringify the keys" do
184
+ expect(dotified).to eql("a" => 2, "5" => 4)
185
+ end
186
+ end
187
+
188
+ context "with an array value" do
189
+ let(:hash) { {:a => 2, 5 => 4, :c => [1,2,3]} }
190
+
191
+ it "should ignore array values" do
192
+ expect(dotified).to eql("a" => 2, "5" => 4)
193
+ end
194
+ end
195
+
196
+ end
147
197
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-graphite
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-24 00:00:00.000000000 Z
11
+ date: 2015-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logstash-core
@@ -127,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
127
  version: '0'
128
128
  requirements: []
129
129
  rubyforge_project:
130
- rubygems_version: 2.2.2
130
+ rubygems_version: 2.1.9
131
131
  signing_key:
132
132
  specification_version: 4
133
133
  summary: This output allows you to pull metrics from your logs and ship them to Graphite