logstash-output-graphite 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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