Skip to content

Commit e6e83c1

Browse files
committed
Add config include_metadata_keys.
Set include_metadata default to false.
1 parent d27966f commit e6e83c1

File tree

6 files changed

+151
-34
lines changed

6 files changed

+151
-34
lines changed

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@
77
## 1.1.0
88
- Disable mfa for gem push
99
## 1.2.0
10-
- Add metadata by default to every log
10+
- Add metadata by default to every log
1111
## 1.2.1
1212
- Fix ensuring metadata exists before deleting the original while forwarding to lm-logs
1313
## 1.3.0
14-
- Add bearer token support for authentication with Logicmonitor
14+
- Add bearer token support for authentication with Logicmonitor
15+
## 1.3.1
16+
- Fix user agent populated in headers
17+
## 2.0.0
18+
- Dont send event metadata by default. Add config include_metadata_keys for including custom metadata

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[![Gem Version](https://badge.fury.io/rb/logstash-output-lmlogs.svg)](https://badge.fury.io/rb/logstash-output-lmlogs)
22

3-
This plugin sends Logstash events to the [Logicmonitor Logs](https://www.logicmonitor.com)
3+
This plugin sends Logstash events to the [Logicmonitor Logs](https://www.logicmonitor.com)
44

55
# Getting started
66

@@ -28,27 +28,28 @@ You would need either `access_id` and `access_id` both or `bearer_token` for aut
2828

2929
| Option | Description| Default |
3030
| --- | --- | --- |
31-
| batch_size | Event batch size to send to LM Logs.| 100 |
31+
| batch_size | Event batch size to send to LM Logs.| 100 |
3232
| message_key | Key that will be used by the plugin as the system key | "message" |
3333
| lm_property | Key that will be used by LM to match resource based on property | "system.hostname" |
3434
| keep_timestamp | If false, LM Logs will use the ingestion timestamp as the event timestamp | true |
3535
| timestamp_is_key | If true, LM Logs will use a specified key as the event timestamp | false |
3636
| timestamp_key | If timestamp_is_key is set, LM Logs will use this key in the event as the timestamp | "logtimestamp" |
37-
| include_metadata | If false, the metadata fields will not be sent to LM Logs | true |
37+
| include_metadata | If true, all metadata fields will be sent to LM Logs | false |
38+
| include_metadata_keys | Array of json keys for which plugin looks for these keys and adds as event meatadata. A dot "." can be used to add nested subjson. If config `include_metadata` is set to true, all metadata will be sent regardless of this config. | [] |
3839

3940
See the [source code](lib/logstash/outputs/lmlogs.rb) for the full list of options
4041

4142
The syntax for `message_key` and `source_key` values are available in the [Logstash Event API Documentation](https://www.elastic.co/guide/en/logstash/current/event-api.html)
4243

43-
## Known issues
44+
## Known issues
4445
- Installation of the plugin fails on Logstash 6.2.1.
45-
46-
46+
47+
4748
## Contributing
48-
49+
4950
Bug reports and pull requests are welcome. This project is intended to
5051
be a safe, welcoming space for collaboration.
51-
52+
5253
## Development
53-
54+
5455
We use docker to build the plugin. You can build it by running `docker-compose run jruby gem build logstash-output-lmlogs.gemspec `

lib/logstash/outputs/lmlogs.rb

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,17 @@ class InvalidHTTPConfigError < StandardError; end
9595
config :access_id, :validate => :string, :required => false, :default => nil
9696

9797
# Include/Exclude metadata from sending to LM Logs
98-
config :include_metadata, :validate => :boolean, :default => true
98+
config :include_metadata, :validate => :boolean, :default => false
9999

100100
# Password to use for HTTP auth
101101
config :access_key, :validate => :password, :required => false, :default => nil
102102

103103
# Use bearer token instead of access key/id for authentication.
104104
config :bearer_token, :validate => :password, :required => false, :default => nil
105105

106+
# json keys for which plugin looks for these keys and adds as event meatadata. A dot "." can be used to add nested subjson.
107+
config :include_metadata_keys, :validate => :array, :required => false, :default => []
108+
106109
@@MAX_PAYLOAD_SIZE = 8*1024*1024
107110

108111
# For developer debugging.
@@ -117,6 +120,14 @@ def register
117120
logger.info("Max Payload Size: ",
118121
:size => @@MAX_PAYLOAD_SIZE)
119122
configure_auth
123+
124+
@final_metadata_keys = Hash.new
125+
if @include_metadata_keys.any?
126+
include_metadata_keys.each do | nested_key |
127+
@final_metadata_keys[nested_key] = nested_key.to_s.split('.')
128+
end
129+
end
130+
120131
end # def register
121132

122133
def client_config
@@ -266,34 +277,48 @@ def multi_receive(events)
266277
events.each_slice(@batch_size) do |chunk|
267278
documents = []
268279
chunk.each do |event|
269-
event_json = JSON.parse(event.to_json)
270-
lmlogs_event = {}
271-
272-
if @include_metadata
273-
lmlogs_event = event_json
274-
lmlogs_event.delete("@timestamp") # remove redundant timestamp field
275-
if lmlogs_event.dig("event", "original") != nil
276-
lmlogs_event["event"].delete("original") # remove redundant log field
277-
end
278-
end
279280

280-
lmlogs_event["message"] = event.get(@message_key).to_s
281-
lmlogs_event["_lm.resourceId"] = {}
282-
lmlogs_event["_lm.resourceId"]["#{@lm_property}"] = event.get(@property_key.to_s)
281+
documents = isValidPayloadSize(documents, processEvent(event), @@MAX_PAYLOAD_SIZE)
282+
end
283+
send_batch(documents)
284+
end
285+
end
283286

284-
if @keep_timestamp
285-
lmlogs_event["timestamp"] = event.get("@timestamp")
286-
end
287287

288-
if @timestamp_is_key
289-
lmlogs_event["timestamp"] = event.get(@timestamp_key.to_s)
288+
def processEvent(event)
289+
event_json = JSON.parse(event.to_json)
290+
lmlogs_event = {}
291+
292+
if @include_metadata
293+
lmlogs_event = event_json
294+
lmlogs_event.delete("@timestamp") # remove redundant timestamp field
295+
if lmlogs_event.dig("event", "original") != nil
296+
lmlogs_event["event"].delete("original") # remove redundant log field
297+
end
298+
elsif @final_metadata_keys
299+
@final_metadata_keys.each do | key, value |
300+
nestedVal = event_json
301+
value.each { |x| nestedVal = nestedVal[x] }
302+
if nestedVal != nil
303+
lmlogs_event[key] = nestedVal
290304
end
305+
end
306+
end
291307

292-
documents = isValidPayloadSize(documents,lmlogs_event,@@MAX_PAYLOAD_SIZE)
308+
lmlogs_event["message"] = event.get(@message_key).to_s
309+
lmlogs_event["_lm.resourceId"] = {}
310+
lmlogs_event["_lm.resourceId"]["#{@lm_property}"] = event.get(@property_key.to_s)
293311

294-
end
295-
send_batch(documents)
312+
if @keep_timestamp
313+
lmlogs_event["timestamp"] = event.get("@timestamp")
314+
end
315+
316+
if @timestamp_is_key
317+
lmlogs_event["timestamp"] = event.get(@timestamp_key.to_s)
296318
end
319+
320+
return lmlogs_event
321+
297322
end
298323

299324
def log_failure(message, opts)

lib/logstash/outputs/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module LmLogsLogstashPlugin
4-
VERSION = '1.3.1'
4+
VERSION = '2.0.0'
55
end

logstash-output-lmlogs.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ Gem::Specification.new do |s|
3333
s.add_runtime_dependency 'manticore', '>= 0.5.2', '< 1.0.0'
3434

3535
s.add_development_dependency 'logstash-devutils'
36+
s.add_development_dependency 'hashdiff', '>= 1.0.0'
3637
end

spec/outputs/metadata_spec.rb

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# encoding: utf-8
2+
require "logstash/devutils/rspec/spec_helper"
3+
require "logstash/outputs/lmlogs"
4+
require "logstash/event"
5+
require "hashdiff"
6+
7+
8+
describe LogStash::Outputs::LMLogs do
9+
10+
11+
let(:logstash_event) {LogStash::Event.new("message" => "hello this is log 1",
12+
"host" => "host1",
13+
"nested1" => {"nested2" => {"nested3" => "value"},
14+
"nested2a" => {"nested3a" => {"nested4" => "valueA"}},
15+
"nested2b" => {"nested3b" => "value"}
16+
},
17+
"nested1_" => "value",
18+
"nested" => {"nested2" => {"nested3" => "value",
19+
"nested3b" => "value"},
20+
"nested_ignored" => "somevalue"
21+
}
22+
)}
23+
let(:sample_lm_logs_event){{"message" => "hello this is log 1", "_lm.resourceId" => {"test.property" => "host1"}, "timestamp" => "2021-03-22T04:28:55.907121106Z"}}
24+
let(:include_metadata_keys) {["host", "nested1.nested2.nested3", "nested1.nested2a", "nested.nested2" ]}
25+
26+
def create_output_plugin_with_conf(conf)
27+
return LogStash::Outputs::LMLogs.new(conf)
28+
end
29+
30+
def check_same_hash(h1,h2)
31+
32+
end
33+
34+
it "default behaviour" do
35+
puts "default behaviour"
36+
plugin = create_output_plugin_with_conf({
37+
"portal_name" => "localhost",
38+
"access_id" => "abcd",
39+
"access_key" => "abcd",
40+
"lm_property" => "system.hostname",
41+
"property_key" => "host"
42+
})
43+
constructed_event = plugin.processEvent(logstash_event)
44+
expected_event = {
45+
"message" => "hello this is log 1",
46+
"timestamp" => logstash_event.timestamp,
47+
"_lm.resourceId" => {"system.hostname" => "host1"},
48+
49+
}
50+
puts " actual : #{constructed_event} \n expected : #{expected_event}"
51+
52+
expect(Hashdiff.diff(constructed_event,expected_event)).to eq([])
53+
end
54+
55+
it "with include_metadata set to true" do
56+
puts "with include_metadata set to true"
57+
plugin = create_output_plugin_with_conf({
58+
"portal_name" => "localhost",
59+
"access_id" => "abcd",
60+
"access_key" => "abcd",
61+
"lm_property" => "system.hostname",
62+
"property_key" => "host",
63+
"include_metadata" => true
64+
})
65+
constructed_event = plugin.processEvent(logstash_event)
66+
expected_event = {
67+
"message" => "hello this is log 1",
68+
"timestamp" => logstash_event.timestamp,
69+
"@version" => "1",
70+
"_lm.resourceId" => {"system.hostname" => "host1"},
71+
"host" => "host1",
72+
"nested1" => {"nested2" => {"nested3" => "value"},
73+
"nested2a" => {"nested3a" => {"nested4" => "valueA"}},
74+
"nested2b" => {"nested3b" => "value"}
75+
},
76+
"nested1_" => "value",
77+
"nested" => {"nested2" => {"nested3" => "value",
78+
"nested3b" => "value"},
79+
"nested_ignored" => "somevalue"
80+
}
81+
}
82+
puts " actual : #{constructed_event} \n expected : #{expected_event}"
83+
puts " hash diff : #{Hashdiff.diff(constructed_event,expected_event)}"
84+
expect(Hashdiff.diff(constructed_event,expected_event)).to eq([])
85+
end
86+
end

0 commit comments

Comments
 (0)