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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +2 -5
- data/CONTRIBUTORS +10 -0
- data/DEVELOPER.md +37 -0
- data/Gemfile +1 -0
- data/LICENSE +196 -17
- data/README.md +60 -74
- data/lib/logstash/outputs/newrelic_internal.rb +126 -0
- data/lib/logstash/outputs/newrelic_internal_version/version.rb +7 -0
- data/logstash-output-newrelic.gemspec +22 -17
- data/spec/outputs/newrelic_internal_spec.rb +284 -0
- metadata +54 -38
- data/.github/CONTRIBUTING.md +0 -65
- data/.github/ISSUE_TEMPLATE.md +0 -9
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -1
- data/.gitignore +0 -2
- data/.travis.yml +0 -11
- data/NOTICE.TXT +0 -5
- data/Rakefile +0 -7
- data/bin/logstash-newrelic +0 -2
- data/bin/logstash-newrelic-debug +0 -2
- data/bin/logstash-newrelic.conf.template +0 -10
- data/lib/logstash/outputs/newrelic.rb +0 -220
- data/spec/outputs/newrelic_spec.rb +0 -66
@@ -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
|