logstash-output-newrelic 1.0.2 → 1.0.3
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 +4 -4
- data/README.md +22 -29
- data/lib/logstash/outputs/newrelic.rb +38 -23
- data/lib/logstash/outputs/newrelic_version/version.rb +1 -1
- data/spec/outputs/newrelic_spec.rb +58 -10
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6fb3ab8cf5881043a05b318e3a5158a15f9e39f71d35bf1b98a2b0526ec59df8
|
4
|
+
data.tar.gz: b78ec9ccaddeb88589815019ef3a6b8336b9b12aef7d51b03ccb07df21e7c679
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5d13fbb5791e8f7ecf98c6aaebae14596f94581246691cbc9b92a8129b97207405e7af396de356cf1a3bd8fbc882e77700be6353356f54c806a631b1052cd06
|
7
|
+
data.tar.gz: f57941893058a0fdd95c2ab7d3b307051275f428591314d5c2ae93afa71ad18cc29e7ec64bc17db17073670c031f1b0293525c675d00e8db52a1fddda6f85cce
|
data/README.md
CHANGED
@@ -6,13 +6,8 @@ This is a plugin for [Logstash](https://github.com/elastic/logstash) that output
|
|
6
6
|
Install the New Relic Logstash plugin using the following command:</br>
|
7
7
|
`logstash-plugin install logstash-output-newrelic`
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
```
|
13
|
-
Old version: 0.9.1 (unmaintained)
|
14
|
-
Current: 1.0.0
|
15
|
-
```
|
9
|
+
### Versions
|
10
|
+
Versions of this plugin less than 1.0.0 are unsupported.
|
16
11
|
|
17
12
|
## Configuration
|
18
13
|
|
@@ -50,7 +45,8 @@ output {
|
|
50
45
|
|
51
46
|
## Testing
|
52
47
|
|
53
|
-
An easy way to test the plugin is to make sure Logstash is getting input from a log file you
|
48
|
+
An easy way to test the plugin is to make sure Logstash is getting input from a log file you
|
49
|
+
can write to. Something like this in your logstash.conf:
|
54
50
|
```
|
55
51
|
input {
|
56
52
|
file {
|
@@ -62,32 +58,29 @@ input {
|
|
62
58
|
* Append a test log message to your log file: `echo "test message" >> /path/to/your/log/file`
|
63
59
|
* Search New Relic Logs for `"test message"`
|
64
60
|
|
65
|
-
##
|
61
|
+
## JSON message parsing
|
66
62
|
|
67
|
-
This plugin will attempt to parse any 'message' attribute as JSON -- if it is JSON,
|
63
|
+
This plugin will attempt to parse any 'message' attribute as JSON -- if it is JSON, it will be parsed and
|
64
|
+
the JSON attributes will be added to the event.
|
68
65
|
|
69
|
-
For example, the
|
66
|
+
For example, the event:
|
70
67
|
```
|
71
|
-
[{
|
72
|
-
"message": "some message",
|
73
|
-
"timestamp": 1531414060739
|
74
|
-
},
|
75
68
|
{
|
76
|
-
|
77
|
-
}
|
69
|
+
"timestamp": 1562767499238,
|
70
|
+
"message": "{\"service-name\": \"login-service\", \"user\": {\"id\": 123, \"name\": \"alice\"}}"
|
71
|
+
}
|
72
|
+
|
78
73
|
```
|
79
74
|
|
80
|
-
Will be
|
75
|
+
Will be treated as:
|
81
76
|
```
|
82
|
-
|
83
|
-
"
|
84
|
-
"
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
77
|
+
{
|
78
|
+
"timestamp": 1562767499238,
|
79
|
+
"message": "{\"service-name\": \"my-service\", \"user\": {\"id\": 123, \"name\": \"alice\"}}",
|
80
|
+
"service-name": "login-service",
|
81
|
+
"user": {
|
82
|
+
"id": 123,
|
83
|
+
"name": "alice"
|
84
|
+
}
|
85
|
+
}
|
89
86
|
```
|
90
|
-
|
91
|
-
## Development
|
92
|
-
|
93
|
-
See [DEVELOPER.md](DEVELOPER.md)
|
@@ -38,13 +38,32 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
|
|
38
38
|
@executor&.shutdown
|
39
39
|
end
|
40
40
|
|
41
|
+
def time_to_milliseconds(time)
|
42
|
+
timestamp = time_to_logstash_timestamp(time)
|
43
|
+
if timestamp.nil?
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
|
47
|
+
(timestamp.to_f * 1000).to_i # to_f gives seconds with a fractional portion
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def time_to_logstash_timestamp(time)
|
52
|
+
begin
|
53
|
+
LogStash::Timestamp.coerce(time)
|
54
|
+
rescue
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
41
60
|
def encode(event_hash)
|
42
61
|
log_message_hash = {
|
43
|
-
|
62
|
+
:attributes => {}
|
44
63
|
}
|
45
64
|
|
46
65
|
if event_hash.has_key?('message')
|
47
|
-
log_message_hash[
|
66
|
+
log_message_hash[:attributes] = maybe_parse_json(event_hash['message'])
|
48
67
|
end
|
49
68
|
|
50
69
|
event_hash.each do |key, value|
|
@@ -52,22 +71,17 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
|
|
52
71
|
# intrinsic attributes go at the top level
|
53
72
|
log_message_hash[key] = value
|
54
73
|
elsif key == '@timestamp'
|
55
|
-
|
74
|
+
milliseconds = time_to_milliseconds(value)
|
75
|
+
if !milliseconds.nil?
|
76
|
+
log_message_hash[:timestamp] = milliseconds
|
77
|
+
end
|
56
78
|
else
|
57
79
|
# non-intrinsic attributes get put into 'attributes'
|
58
|
-
log_message_hash[
|
80
|
+
log_message_hash[:attributes][key] = value
|
59
81
|
end
|
60
82
|
end
|
61
|
-
|
62
|
-
log_message_hash
|
63
|
-
end
|
64
83
|
|
65
|
-
|
66
|
-
if event_hash.has_key?('message')
|
67
|
-
message = event_hash['message']
|
68
|
-
event_hash = event_hash.merge(maybe_parse_json(message))
|
69
|
-
end
|
70
|
-
event_hash
|
84
|
+
log_message_hash
|
71
85
|
end
|
72
86
|
|
73
87
|
def maybe_parse_json(message)
|
@@ -77,8 +91,9 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
|
|
77
91
|
return parsed
|
78
92
|
end
|
79
93
|
rescue JSON::ParserError
|
94
|
+
# Not JSON
|
80
95
|
end
|
81
|
-
|
96
|
+
{}
|
82
97
|
end
|
83
98
|
|
84
99
|
def multi_receive(events)
|
@@ -87,15 +102,15 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
|
|
87
102
|
payload.push(encode(event.to_hash))
|
88
103
|
end
|
89
104
|
payload = {
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
105
|
+
:common => {
|
106
|
+
:attributes => {
|
107
|
+
:plugin => {
|
108
|
+
:type => 'logstash',
|
109
|
+
:version => LogStash::Outputs::NewRelicVersion::VERSION,
|
110
|
+
}
|
111
|
+
}
|
112
|
+
},
|
113
|
+
:logs => payload
|
99
114
|
}
|
100
115
|
@semaphor.acquire()
|
101
116
|
execute = @executor.java_method :submit, [java.lang.Runnable]
|
@@ -24,6 +24,13 @@ describe LogStash::Outputs::NewRelic do
|
|
24
24
|
}
|
25
25
|
}
|
26
26
|
|
27
|
+
# An arbitrary time to use in these tests, with different representations
|
28
|
+
class FixedTime
|
29
|
+
MILLISECONDS = 1562888528123
|
30
|
+
ISO_8601_STRING_TIME = '2019-07-11T23:42:08.123Z'
|
31
|
+
LOGSTASH_TIMESTAMP = LogStash::Timestamp.coerce(ISO_8601_STRING_TIME)
|
32
|
+
end
|
33
|
+
|
27
34
|
def gunzip(bytes)
|
28
35
|
gz = Zlib::GzipReader.new(StringIO.new(bytes))
|
29
36
|
gz.read
|
@@ -39,6 +46,16 @@ describe LogStash::Outputs::NewRelic do
|
|
39
46
|
JSON.parse(gunzip(body))
|
40
47
|
end
|
41
48
|
|
49
|
+
def now_in_milliseconds()
|
50
|
+
(Time.now.to_f * 1000).to_i # to_f gives seconds with a fractional portion
|
51
|
+
end
|
52
|
+
|
53
|
+
def within_five_seconds_of(time_in_millis, expected_in_millis)
|
54
|
+
five_seconds_in_millis = 5 * 1000
|
55
|
+
(time_in_millis - expected_in_millis).abs < five_seconds_in_millis
|
56
|
+
end
|
57
|
+
|
58
|
+
|
42
59
|
before(:each) do
|
43
60
|
@newrelic_output = LogStash::Plugin.lookup("output", "newrelic").new(simple_config)
|
44
61
|
@newrelic_output.register
|
@@ -61,7 +78,7 @@ describe LogStash::Outputs::NewRelic do
|
|
61
78
|
it "all present" do
|
62
79
|
stub_request(:any, base_uri).to_return(status: 200)
|
63
80
|
|
64
|
-
event = LogStash::Event.new({
|
81
|
+
event = LogStash::Event.new({:message => "Test message" })
|
65
82
|
@newrelic_output.multi_receive([event])
|
66
83
|
|
67
84
|
wait_for(a_request(:post, base_uri)
|
@@ -72,6 +89,7 @@ describe LogStash::Outputs::NewRelic do
|
|
72
89
|
})).to have_been_made
|
73
90
|
end
|
74
91
|
end
|
92
|
+
|
75
93
|
context "request body" do
|
76
94
|
|
77
95
|
it "message contains plugin information" do
|
@@ -88,16 +106,47 @@ describe LogStash::Outputs::NewRelic do
|
|
88
106
|
.to have_been_made
|
89
107
|
end
|
90
108
|
|
91
|
-
# TODO: why is this field always removed?
|
92
|
-
it "'@timestamp' field is removed" do
|
93
|
-
stub_request(:any, base_uri).to_return(status: 200)
|
94
109
|
|
95
|
-
|
96
|
-
@newrelic_output.multi_receive([event])
|
110
|
+
context "@timestamp field" do
|
97
111
|
|
98
|
-
|
99
|
-
.
|
100
|
-
|
112
|
+
def test_timestamp(timestamp, message_matcher)
|
113
|
+
stub_request(:any, base_uri).to_return(status: 200)
|
114
|
+
|
115
|
+
event = LogStash::Event.new({ :message => "Test message", :@timestamp => timestamp })
|
116
|
+
@newrelic_output.multi_receive([event])
|
117
|
+
|
118
|
+
wait_for(a_request(:post, base_uri)
|
119
|
+
.with { |request|
|
120
|
+
message = single_gzipped_message(request.body)
|
121
|
+
message_matcher.call(message)
|
122
|
+
}).to have_been_made
|
123
|
+
end
|
124
|
+
|
125
|
+
it "LogStash::Timestamp '@timestamp' field is added as 'timestamp' as milliseconds since epoch" do
|
126
|
+
def message_matcher(message)
|
127
|
+
message['timestamp'] == FixedTime::MILLISECONDS
|
128
|
+
end
|
129
|
+
|
130
|
+
test_timestamp(FixedTime::LOGSTASH_TIMESTAMP, method(:message_matcher))
|
131
|
+
end
|
132
|
+
|
133
|
+
it "String ISO 8601 '@timestamp' field is added as 'timestamp' as milliseconds since epoch" do
|
134
|
+
def message_matcher(message)
|
135
|
+
message['timestamp'] == FixedTime::MILLISECONDS
|
136
|
+
end
|
137
|
+
|
138
|
+
test_timestamp(FixedTime::ISO_8601_STRING_TIME, method(:message_matcher))
|
139
|
+
end
|
140
|
+
|
141
|
+
# Ideally we might not even set a timestamp if parsing fails. However, the time parsing library doesn't tell us if
|
142
|
+
# parsing fails -- it just returns the current time.
|
143
|
+
it "Unparseable '@timestamp' field is parsed as current time and put into 'timestamp'" do
|
144
|
+
def message_matcher(message)
|
145
|
+
within_five_seconds_of(message['timestamp'], now_in_milliseconds)
|
146
|
+
end
|
147
|
+
|
148
|
+
test_timestamp("Not an ISO 8601 string", method(:message_matcher))
|
149
|
+
end
|
101
150
|
end
|
102
151
|
|
103
152
|
it "all other fields passed through as is" do
|
@@ -109,7 +158,6 @@ describe LogStash::Outputs::NewRelic do
|
|
109
158
|
wait_for(a_request(:post, base_uri)
|
110
159
|
.with { |request|
|
111
160
|
message = single_gzipped_message(request.body)
|
112
|
-
puts message
|
113
161
|
message['message'] == 'Test message' &&
|
114
162
|
message['attributes']['other'] == 'Other value' })
|
115
163
|
.to have_been_made
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-output-newrelic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- New Relic Logging Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-07-
|
11
|
+
date: 2019-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|