logstash-output-newrelic 0.9.1 → 1.0.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.
@@ -1,220 +0,0 @@
1
- # encoding: utf-8
2
- require "json"
3
- require "logstash/outputs/base"
4
- require "logstash/namespace"
5
- require "net/http" # Connectivity back to New Relic Insights
6
- require "net/https" # Connectivity back to New Relic Insights
7
- require "stud/buffer" # For buffering events being sent
8
- require "time"
9
- require "uri"
10
-
11
- # This output sends logstash events to New Relic Insights as custom events.
12
- #
13
- # You can learn more about New Relic Insights here:
14
- # https://docs.newrelic.com/docs/insights/new-relic-insights/understanding-insights/new-relic-insights
15
- class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
16
- include Stud::Buffer
17
-
18
- config_name "newrelic"
19
- milestone 1
20
-
21
- # Your New Relic account ID. This is the 5 or 6-digit number found in the URL when you are logged into New Relic:
22
- # https://rpm.newrelic.com/accounts/[account_id]/...
23
- config :account_id, :validate => :string, :required => true
24
-
25
- # Your Insights Insert Key. You will need to generate one if you haven't already, as described here:
26
- # https://docs.newrelic.com/docs/insights/new-relic-insights/adding-querying-data/inserting-custom-events-insights-api#register
27
- config :insert_key, :validate => :string, :required => true
28
-
29
- # The name for your event type. Use alphanumeric characters only.
30
- # If left out, your events will be stored under "logstashEvent".
31
- config :event_type, :validate => :string, :default => "LogstashEvent"
32
-
33
- # Should the log events be sent to Insights over https instead of plain http (typically yes).
34
- config :proto, :validate => :string, :default => "https"
35
-
36
- # Proxy info - all optional
37
- # If using a proxy, only proxy_host is required.
38
- config :proxy_host, :validate => :string
39
- # Proxy_port will default to port 80 if left out.
40
- config :proxy_port, :validate => :number, :default => 80
41
- # Proxy_user should be left out if connecting to your proxy unauthenticated.
42
- config :proxy_user, :validate => :string
43
- # Proxy_password should be left out if connecting to your proxy unauthenticated.
44
- config :proxy_password, :validate => :password, :default => ""
45
-
46
- # Batch Processing - all optional
47
- # This plugin uses the New Relic Insights REST API to send data.
48
- # To make efficient REST API calls, we will buffer a certain number of events before flushing that out to Insights.
49
- config :batch, :validate => :boolean, :default => true
50
- # This setting controls how many events will be buffered before sending a batch of events.
51
- config :batch_events, :validate => :number, :default => 10
52
- # This setting controls how long the output will wait before sending a batch of a events,
53
- # should the minimum specified in batch_events not be met yet.
54
- config :batch_timeout, :validate => :number, :default => 5
55
-
56
- # New Relic Insights Reserved Words
57
- # https://docs.newrelic.com/docs/insights/new-relic-insights/adding-querying-data/inserting-custom-events#keywords
58
- # backticks = change "word" to "`word`"
59
- # moved = change "word" to "word_moved"
60
- # skipped = skip this attribute altogether (not used so far)
61
- # If you enter anything else, the "word" will change to the "anything else"
62
- RESWORDS = {
63
- "accountId" => "moved",
64
- "appId" => "moved",
65
- "timestamp" => "logstash timestamp",
66
- "@timestamp" => "logstash timestamp",
67
- "type" => "moved",
68
- "ago" => "backticks",
69
- "and" => "backticks",
70
- "as" => "backticks",
71
- "auto" => "backticks",
72
- "begin" => "backticks",
73
- "begintime" => "backticks",
74
- "compare" => "backticks",
75
- "day" => "backticks",
76
- "days" => "backticks",
77
- "end" => "backticks",
78
- "endtime" => "backticks",
79
- "explain" => "backticks",
80
- "facet" => "backticks",
81
- "from" => "backticks",
82
- "hour" => "backticks",
83
- "hours" => "backticks",
84
- "in" => "backticks",
85
- "is" => "backticks",
86
- "like" => "backticks",
87
- "limit" => "backticks",
88
- "minute" => "backticks",
89
- "minutes" => "backticks",
90
- "month" => "backticks",
91
- "months" => "backticks",
92
- "not" => "backticks",
93
- "null" => "backticks",
94
- "offset" => "backticks",
95
- "or" => "backticks",
96
- "second" => "backticks",
97
- "seconds" => "backticks",
98
- "select" => "backticks",
99
- "since" => "backticks",
100
- "timeseries" => "backticks",
101
- "until" => "backticks",
102
- "week" => "backticks",
103
- "weeks" => "backticks",
104
- "where" => "backticks",
105
- "with" => "backticks",
106
- }
107
-
108
- public
109
- def register
110
- # URL to send event over http(s) to the New Relic Insights REST API
111
- @url = URI.parse("#{@proto}://insights-collector.newrelic.com/v1/accounts/#{@account_id}/events")
112
- @logger.info("New Relic Insights output initialized.")
113
- @logger.info("New Relic URL: #{@url}")
114
- if @batch
115
- @logger.info("Batch processing of events enabled.")
116
- if @batch_events > 1000
117
- raise RuntimeError.new("New Relic Insights only allows a batch_events parameter of 1000 or less")
118
- end
119
- buffer_initialize(
120
- :max_items => @batch_events,
121
- :max_interval => @batch_timeout,
122
- :logger => @logger
123
- )
124
- end
125
- end # def register
126
-
127
- public
128
- def receive(event)
129
- return unless output?(event)
130
- if event == LogStash::SHUTDOWN
131
- finished
132
- return
133
- end
134
- parsed_event = parse_event(event)
135
- if @batch
136
- buffer_receive(parsed_event)
137
- else
138
- send_to_insights(parsed_event)
139
- end
140
- end # def receive
141
-
142
- public
143
- # Insights REST API handles multiple events the same way as a single event. Score!
144
- # All we need to do on 'flush' is send the contents of the events buffer.
145
- def flush(events, teardown=false)
146
- @logger.debug("Sending batch of #{events.size} events to insights")
147
- send_to_insights(events)
148
- end # def flush
149
-
150
- public
151
- def teardown
152
- buffer_flush(:final => true)
153
- finished
154
- end # def teardown
155
-
156
- # Turn event into an Insights-compliant event
157
- public
158
- def parse_event(event)
159
- this_event = event.to_hash
160
- output_event = Hash.new
161
-
162
- # Setting eventType to what's in the config
163
- output_event['eventType'] = @event_type
164
-
165
- # Setting timestamp to what logstash reports
166
- if this_event.key?('@timestamp')
167
- timestamp_parsed = Time.parse(this_event['@timestamp'].to_s)
168
- output_event['timestamp'] = timestamp_parsed.to_i
169
- elsif this_event.key?('timestamp')
170
- timestamp_parsed = Time.parse(this_event['timestamp'].to_s)
171
- output_event['timestamp'] = timestamp_parsed.to_i
172
- end
173
-
174
- # Search event's attribute names for reserved words, replace with 'compliant' versions
175
- # Storing 'compliant' key names in "EVENT_KEYS" to minimize time spent doing this
176
- this_event.each_key do |event_key|
177
- if RESWORDS.has_key?(event_key)
178
- @logger.debug("Reserved word found", :reserved_word => event_key)
179
- if RESWORDS[event_key] == "skipped"
180
- next
181
- elsif RESWORDS[event_key] == "moved"
182
- proper_name = event_key + "_moved"
183
- elsif RESWORDS[event_key] == "backticks"
184
- proper_name = "`" + event_key + "`"
185
- else
186
- proper_name = RESWORDS[event_key]
187
- end
188
- else
189
- proper_name = event_key
190
- end
191
- output_event[proper_name] = this_event[event_key]
192
- end
193
- return output_event
194
- end # def parse_event
195
-
196
- # Send event(s) to the NR Insights REST API.
197
- # Can handle a single event or batched events.
198
- def send_to_insights(event)
199
- http = Net::HTTP.new(@url.host, @url.port, @proxy_host, @proxy_port, @proxy_user, @proxy_password.value)
200
- if @url.scheme == 'https'
201
- http.use_ssl = true
202
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
203
- end
204
-
205
- # Insights uses a POST, requires Content-Type and X-Insert-Key.
206
- request = Net::HTTP::Post.new(@url.path)
207
- request['Content-Type'] = "application/json"
208
- request['X-Insert-Key'] = @insert_key
209
- request.body = event.to_json
210
- @logger.debug("Request Body:", :request_body => request.body)
211
-
212
- response = http.request(request)
213
- if response.is_a?(Net::HTTPSuccess)
214
- @logger.debug("Event sent to New Relic SUCCEEDED! Response Code:", :response_code => response.code)
215
- else
216
- @logger.warn("Event sent to New Relic FAILED. Error:", :error => response.error!)
217
- end
218
- end # def send_to_insights
219
-
220
- end # class LogStash::Outputs::NewRelic
@@ -1,66 +0,0 @@
1
- # encoding: utf-8
2
- require "logstash/devutils/rspec/spec_helper"
3
- require "logstash/outputs/newrelic"
4
- require "logstash/event"
5
- require "logstash/timestamp"
6
- require "time"
7
-
8
- # TO DO (in order of easiest to hardest):
9
- # + Test batch_timeout
10
- # + Test proxy (oy gevalt)
11
-
12
- describe LogStash::Outputs::NewRelic do
13
-
14
- let (:simple_event_contents) { { 'message' => 'hello', 'topic_name' => 'my_topic', 'host' => '172.0.0.1' } }
15
- let(:options) { { 'account_id' => "284929",
16
- 'insert_key' => "BYh7sByiVrkfqcDa2eqVMhjxafkdyuX0" } }
17
- let(:simple_output) { LogStash::Plugin.lookup("output", "newrelic").new(options) }
18
-
19
- describe "#register" do
20
- it "should register" do
21
- output = LogStash::Plugin.lookup("output", "newrelic").new(options)
22
- expect { output.register }.to_not raise_error
23
- end
24
-
25
- it "should NOT register when batch_events > 1000" do
26
- options['batch_events'] = 1001
27
- output = LogStash::Plugin.lookup("output", "newrelic").new(options)
28
- expect { output.register }.to raise_error(RuntimeError)
29
- end
30
- end
31
-
32
- describe "#parse_event" do
33
- it "should convert attribute names" do
34
- simple_event_contents['accountId'] = '123456'
35
- simple_event_contents['compare'] = 'backtick that'
36
- simple_event_contents['test_of_anything_else'] = 'leave this'
37
- simple_event = LogStash::Event.new(simple_event_contents)
38
- test_event = simple_output.parse_event(simple_event)
39
- expect(test_event['accountId_moved']).to eq(simple_event_contents['accountId'])
40
- expect(test_event['`compare`']).to eq(simple_event_contents['compare'])
41
- expect(test_event['test_of_anything_else']).to eq(simple_event_contents['test_of_anything_else'])
42
- end
43
- end
44
-
45
- describe "#receive" do
46
- it "should send a single event" do
47
- options['batch'] = false
48
- output = LogStash::Plugin.lookup("output", "newrelic").new(options)
49
- output.register
50
- simple_event = LogStash::Event.new(simple_event_contents)
51
- expect { output.receive(simple_event) }.to_not raise_error
52
- end
53
-
54
- it "should send multiple events" do
55
- batch_event_count = 5
56
- options["batch_events"] = batch_event_count
57
- output = LogStash::Plugin.lookup("output", "newrelic").new(options)
58
- output.register
59
- for i in 0..batch_event_count
60
- simple_event_contents['iteration'] = i
61
- simple_event = LogStash::Event.new(simple_event_contents)
62
- expect { output.receive(simple_event) }.to_not raise_error
63
- end
64
- end
65
- end
66
- end