sentinelblue-logstash-output-azure-loganalytics 1.1.1
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 +7 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +2 -0
- data/LICENSE +13 -0
- data/README.md +167 -0
- data/VERSION +1 -0
- data/lib/logstash/logAnalyticsClient/logAnalyticsClient.rb +72 -0
- data/lib/logstash/logAnalyticsClient/logStashAutoResizeBuffer.rb +143 -0
- data/lib/logstash/logAnalyticsClient/logstashLoganalyticsConfiguration.rb +147 -0
- data/lib/logstash/outputs/sentinelblue-logstash-output-azure-loganalytics.rb +185 -0
- data/sentinelblue-logstash-output-azure-loganalytics.gemspec +25 -0
- data/spec/outputs/azure_loganalytics_spec.rb +78 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 41e20d6292a1fe6e9b70ce0860be3f92dd47b6a90ddbb6d5ee87f7335fb30270
|
4
|
+
data.tar.gz: bbfd6a8f894905f0f2e2e04fdcb3daa034f04047c5f847e26817670ee8cf58f3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 269d4146de6a9bdbd206f76c2d8c2563093cfb71c43a5d1660e429816d5e6577e4610987ce9b2c63f545efe385beaa1b394b0f26f7017a6ca43e9ca6e95195fa
|
7
|
+
data.tar.gz: 78984fe0392d030f17f1842738371c875fc7478db115fc1f0f8a42769210ce0a7e5f277aab641f63e06ab267db55b1016b6d280bcd9c21e6c2c90e887d88dc5d
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2022 Sentinel Blue
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
# Sentinel Blue Azure Log Analytics output plugin for Logstash
|
2
|
+
|
3
|
+
Azure Sentinel provides an output plugin for Logstash. Using this output plugin, you will be able to send any log you want using Logstash to the Azure Sentinel/Log Analytics workspace
|
4
|
+
Today you will be able to send messages to custom logs table that you will define in the output plugin.
|
5
|
+
[Getting started with Logstash](<https://www.elastic.co/guide/en/logstash/current/getting-started-with-logstash.html>)
|
6
|
+
|
7
|
+
Azure Sentinel output plugin uses the rest API integration to Log Analytics, in order to ingest the logs into custom logs tables [What are custom logs tables](<https://docs.microsoft.com/azure/azure-monitor/platform/data-sources-custom-logs>)
|
8
|
+
|
9
|
+
This plugin is based on the original provided by the Azure Sentinel team. View the original plugin here: <https://github.com/Azure/Azure-Sentinel/tree/master/DataConnectors/microsoft-logstash-output-azure-loganalytics>
|
10
|
+
|
11
|
+
```text
|
12
|
+
Plugin version: v1.1.1
|
13
|
+
Released on: 2022-10-20
|
14
|
+
```
|
15
|
+
|
16
|
+
This plugin is currently in development and is free to use. We welcome contributions from the open source community on this project, and we request and appreciate feedback from users.
|
17
|
+
|
18
|
+
## Support
|
19
|
+
|
20
|
+
For issues regarding the output plugin please open a support issue here. Create a new issue describing the problem so that we can assist you.
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
Azure Sentinel provides Logstash an output plugin to Log analytics workspace.
|
25
|
+
Install the sentinelblue-logstash-output-azure-loganalytics, use [Logstash Working with plugins](<https://www.elastic.co/guide/en/logstash/current/working-with-plugins.html>) document.
|
26
|
+
For offline setup follow [Logstash Offline Plugin Management instruction](<https://www.elastic.co/guide/en/logstash/current/offline-plugins.html>).
|
27
|
+
|
28
|
+
Required Logstash version: between 7.0+
|
29
|
+
|
30
|
+
## Configuration
|
31
|
+
|
32
|
+
in your Logstash configuration file, add the Azure Sentinel output plugin to the configuration with following values:
|
33
|
+
|
34
|
+
- workspace_id
|
35
|
+
- your workspace ID guid
|
36
|
+
- workspace_key (primary key)
|
37
|
+
- your workspace primary key guid. You can find your workspace key and id the following path: Home > Log Analytics workspace > Advanced settings
|
38
|
+
- custom_log_table_name
|
39
|
+
- table name, in which the logs will be ingested, limited to one table, the log table will be presented in the logs blade under the custom logs label, with a _CL suffix.
|
40
|
+
- custom_log_table_name must be either a static name consisting only of numbers, letters, and underscores OR a dynamic table name of the format used by logstash (e.g. ```%{field_name}```, ```%{[nested][field]}```.
|
41
|
+
- endpoint
|
42
|
+
- Optional field by default set as log analytics endpoint.
|
43
|
+
- time_generated_field
|
44
|
+
- Optional field, this property is used to override the default TimeGenerated field in Log Analytics. Populate this property with the name of the sent data time field.
|
45
|
+
- key_names
|
46
|
+
- list of Log analytics output schema fields.
|
47
|
+
- plugin_flash_interval
|
48
|
+
- Optional filed, define the maximal time difference (in seconds) between sending two messages to Log Analytics.
|
49
|
+
- Max_items
|
50
|
+
- Optional field, 2000 by default. this parameter will control the maximum batch size. This value will be changed if the user didn’t specify “amount_resizing = false” in the configuration.
|
51
|
+
|
52
|
+
Note: View the GitHub to learn more about the sent message’s configuration, performance settings and mechanism
|
53
|
+
|
54
|
+
Security notice: We recommend not to implicitly state the workspace_id and workspace_key in your Logstash configuration for security reasons.
|
55
|
+
It is best to store this sensitive information in a Logstash KeyStore as described here- https://www.elastic.co/guide/en/elasticsearch/reference/current/get-started-logstash-user.html
|
56
|
+
|
57
|
+
## Tests
|
58
|
+
|
59
|
+
Here is an example configuration who parse Syslog incoming data into a custom table named "logstashCustomTableName".
|
60
|
+
|
61
|
+
### Example Configurations
|
62
|
+
|
63
|
+
#### Basic configuration
|
64
|
+
|
65
|
+
- Using filebeat input pipe
|
66
|
+
|
67
|
+
```text
|
68
|
+
input {
|
69
|
+
beats {
|
70
|
+
port => "5044"
|
71
|
+
}
|
72
|
+
}
|
73
|
+
filter {
|
74
|
+
}
|
75
|
+
output {
|
76
|
+
sentinelblue-logstash-output-azure-loganalytics {
|
77
|
+
workspace_id => "4g5tad2b-a4u4-147v-a4r7-23148a5f2c21" # <your workspace id>
|
78
|
+
workspace_key => "u/saRtY0JGHJ4Ce93g5WQ3Lk50ZnZ8ugfd74nk78RPLPP/KgfnjU5478Ndh64sNfdrsMni975HJP6lp==" # <your workspace key>
|
79
|
+
custom_log_table_name => "tableName"
|
80
|
+
}
|
81
|
+
}
|
82
|
+
```
|
83
|
+
|
84
|
+
Or using the tcp input pipe
|
85
|
+
|
86
|
+
```text
|
87
|
+
input {
|
88
|
+
tcp {
|
89
|
+
port => "514"
|
90
|
+
type => syslog #optional, will effect log type in table
|
91
|
+
}
|
92
|
+
}
|
93
|
+
filter {
|
94
|
+
}
|
95
|
+
output {
|
96
|
+
sentinelblue-logstash-output-azure-loganalytics {
|
97
|
+
workspace_id => "4g5tad2b-a4u4-147v-a4r7-23148a5f2c21" # <your workspace id>
|
98
|
+
workspace_key => "u/saRtY0JGHJ4Ce93g5WQ3Lk50ZnZ8ugfd74nk78RPLPP/KgfnjU5478Ndh64sNfdrsMni975HJP6lp==" # <your workspace key>
|
99
|
+
custom_log_table_name => "tableName"
|
100
|
+
}
|
101
|
+
}
|
102
|
+
```
|
103
|
+
|
104
|
+
#### Advanced Configuration
|
105
|
+
|
106
|
+
```text
|
107
|
+
input {
|
108
|
+
tcp {
|
109
|
+
port => 514
|
110
|
+
type => syslog
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
filter {
|
115
|
+
grok {
|
116
|
+
match => { "message" => "<%{NUMBER:PRI}>1 (?<TIME_TAG>[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}T[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2})[^ ]* (?<HOSTNAME>[^ ]*) %{GREEDYDATA:MSG}" }
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
output {
|
121
|
+
sentinelblue-logstash-output-azure-loganalytics {
|
122
|
+
workspace_id => "<WS_ID>"
|
123
|
+
workspace_key => "${WS_KEY}"
|
124
|
+
custom_log_table_name => "logstashCustomTableName"
|
125
|
+
key_names => ['PRI','TIME_TAG','HOSTNAME','MSG']
|
126
|
+
plugin_flush_interval => 5
|
127
|
+
}
|
128
|
+
}
|
129
|
+
```
|
130
|
+
|
131
|
+
```text
|
132
|
+
filter {
|
133
|
+
grok {
|
134
|
+
match => { "message" => "<%{NUMBER:PRI}>1 (?<TIME_TAG>[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}T[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2})[^ ]* (?<HOSTNAME>[^ ]*) %{GREEDYDATA:MSG}" }
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
output {
|
139
|
+
sentinelblue-logstash-output-azure-loganalytics {
|
140
|
+
workspace_id => "<WS_ID>"
|
141
|
+
workspace_key => "${WS_KEY}"
|
142
|
+
custom_log_table_name => "%{[event][name]}"
|
143
|
+
key_names => ['PRI','TIME_TAG','HOSTNAME','MSG']
|
144
|
+
plugin_flush_interval => 5
|
145
|
+
}
|
146
|
+
}
|
147
|
+
```
|
148
|
+
|
149
|
+
Now you are able to run logstash with the example configuration and send mock data using the 'logger' command.
|
150
|
+
|
151
|
+
For example:
|
152
|
+
|
153
|
+
```text
|
154
|
+
logger -p local4.warn -t CEF: "0|Microsoft|Device|cef-test|example|data|1|here is some more data for the example" -P 514 -d -n 127.0.0.1
|
155
|
+
```
|
156
|
+
|
157
|
+
Note: this format of pushing logs is not tested. You can tail a file for similar results.
|
158
|
+
|
159
|
+
```text
|
160
|
+
logger -p local4.warn -t JSON: "{"event":{"name":"logstashCustomTableName"},"purpose":"testplugin"}"
|
161
|
+
```
|
162
|
+
|
163
|
+
Alternativly you can use netcat to test your configuration:
|
164
|
+
|
165
|
+
```text
|
166
|
+
echo "test string" | netcat localhost 514
|
167
|
+
```
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.1.1
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/logAnalyticsClient/logstashLoganalyticsConfiguration"
|
3
|
+
require 'rest-client'
|
4
|
+
require 'json'
|
5
|
+
require 'openssl'
|
6
|
+
require 'base64'
|
7
|
+
require 'time'
|
8
|
+
|
9
|
+
class LogAnalyticsClient
|
10
|
+
API_VERSION = '2016-04-01'.freeze
|
11
|
+
|
12
|
+
def initialize (logstashLoganalyticsConfiguration)
|
13
|
+
@logstashLoganalyticsConfiguration = logstashLoganalyticsConfiguration
|
14
|
+
set_proxy(@logstashLoganalyticsConfiguration.proxy)
|
15
|
+
@uri = sprintf("https://%s.%s/api/logs?api-version=%s", @logstashLoganalyticsConfiguration.workspace_id, @logstashLoganalyticsConfiguration.endpoint, API_VERSION)
|
16
|
+
end # def initialize
|
17
|
+
|
18
|
+
|
19
|
+
# Post the given json to Azure Loganalytics
|
20
|
+
def post_data(body,custom_table_name)
|
21
|
+
raise ConfigError, 'no json_records' if body.empty?
|
22
|
+
# Create REST request header
|
23
|
+
header = get_header(body.bytesize,custom_table_name)
|
24
|
+
# Post REST request
|
25
|
+
response = RestClient.post(@uri, body, header)
|
26
|
+
|
27
|
+
return response
|
28
|
+
end # def post_data
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Create a header for the given length
|
33
|
+
def get_header(body_bytesize_length,custom_table_name)
|
34
|
+
# We would like each request to be sent with the current time
|
35
|
+
date = rfc1123date()
|
36
|
+
|
37
|
+
return {
|
38
|
+
'Content-Type' => 'application/json',
|
39
|
+
'Authorization' => signature(date, body_bytesize_length),
|
40
|
+
'Log-Type' => custom_table_name,
|
41
|
+
'x-ms-date' => date,
|
42
|
+
'time-generated-field' => @logstashLoganalyticsConfiguration.time_generated_field,
|
43
|
+
'x-ms-AzureResourceId' => @logstashLoganalyticsConfiguration.azure_resource_id
|
44
|
+
}
|
45
|
+
end # def get_header
|
46
|
+
|
47
|
+
# Setting proxy for the REST client.
|
48
|
+
# This option is not used in the output plugin and will be used
|
49
|
+
#
|
50
|
+
def set_proxy(proxy='')
|
51
|
+
RestClient.proxy = proxy.empty? ? ENV['http_proxy'] : proxy
|
52
|
+
end # def set_proxy
|
53
|
+
|
54
|
+
# Return the current data
|
55
|
+
def rfc1123date()
|
56
|
+
current_time = Time.now
|
57
|
+
|
58
|
+
return current_time.httpdate()
|
59
|
+
end # def rfc1123date
|
60
|
+
|
61
|
+
def signature(date, body_bytesize_length)
|
62
|
+
sigs = sprintf("POST\n%d\napplication/json\nx-ms-date:%s\n/api/logs", body_bytesize_length, date)
|
63
|
+
utf8_sigs = sigs.encode('utf-8')
|
64
|
+
decoded_shared_key = Base64.decode64(@logstashLoganalyticsConfiguration.workspace_key)
|
65
|
+
hmac_sha256_sigs = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), decoded_shared_key, utf8_sigs)
|
66
|
+
encoded_hash = Base64.encode64(hmac_sha256_sigs)
|
67
|
+
authorization = sprintf("SharedKey %s:%s", @logstashLoganalyticsConfiguration.workspace_id, encoded_hash)
|
68
|
+
|
69
|
+
return authorization
|
70
|
+
end # def signature
|
71
|
+
|
72
|
+
end # end of class
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "stud/buffer"
|
3
|
+
require "logstash/logAnalyticsClient/logAnalyticsClient"
|
4
|
+
require "stud/buffer"
|
5
|
+
require "logstash/logAnalyticsClient/logstashLoganalyticsConfiguration"
|
6
|
+
|
7
|
+
# LogStashAutoResizeBuffer class setting a resizable buffer which is flushed periodically
|
8
|
+
# The buffer resize itself according to Azure Loganalytics and configuration limitations
|
9
|
+
class LogStashAutoResizeBuffer
|
10
|
+
include Stud::Buffer
|
11
|
+
|
12
|
+
def initialize(logstashLoganalyticsConfiguration,custom_table_name)
|
13
|
+
@logstashLoganalyticsConfiguration = logstashLoganalyticsConfiguration
|
14
|
+
@logger = @logstashLoganalyticsConfiguration.logger
|
15
|
+
@custom_log_table_name = custom_table_name
|
16
|
+
@client=LogAnalyticsClient::new(logstashLoganalyticsConfiguration)
|
17
|
+
buffer_initialize(
|
18
|
+
:max_items => logstashLoganalyticsConfiguration.max_items,
|
19
|
+
:max_interval => logstashLoganalyticsConfiguration.plugin_flush_interval,
|
20
|
+
:logger => @logstashLoganalyticsConfiguration.logger
|
21
|
+
)
|
22
|
+
end # initialize
|
23
|
+
|
24
|
+
# Public methods
|
25
|
+
public
|
26
|
+
|
27
|
+
# Adding an event document into the buffer
|
28
|
+
def add_event_document(event_document)
|
29
|
+
buffer_receive(event_document)
|
30
|
+
end # def add_event_document
|
31
|
+
|
32
|
+
# Flushing all buffer content to Azure Loganalytics.
|
33
|
+
# Called from Stud::Buffer#buffer_flush when there are events to flush
|
34
|
+
def flush (documents, close=false)
|
35
|
+
# Skip in case there are no candidate documents to deliver
|
36
|
+
if documents.length < 1
|
37
|
+
@logger.warn("No documents in batch for log type #{@custom_log_table_name}. Skipping")
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
41
|
+
# We send Json in the REST request
|
42
|
+
documents_json = documents.to_json
|
43
|
+
# Setting resizing to true will cause changing the max size
|
44
|
+
if @logstashLoganalyticsConfiguration.amount_resizing == true
|
45
|
+
# Resizing the amount of messages according to size of message received and amount of messages
|
46
|
+
change_message_limit_size(documents.length, documents_json.bytesize)
|
47
|
+
end
|
48
|
+
send_message_to_loganalytics(documents_json, documents.length)
|
49
|
+
end # def flush
|
50
|
+
|
51
|
+
# Private methods
|
52
|
+
private
|
53
|
+
|
54
|
+
# Send documents_json to Azure Loganalytics
|
55
|
+
def send_message_to_loganalytics(documents_json, amount_of_documents)
|
56
|
+
begin
|
57
|
+
@logger.debug("Posting log batch (log count: #{amount_of_documents}) as log type #{@custom_log_table_name} to DataCollector API.")
|
58
|
+
response = @client.post_data(documents_json,@custom_log_table_name)
|
59
|
+
if is_successfully_posted(response)
|
60
|
+
@logger.info("Successfully posted #{amount_of_documents} logs into custom log analytics table[#{@custom_log_table_name}].")
|
61
|
+
else
|
62
|
+
@logger.error("DataCollector API request failure: error code: #{response.code}, data=>" + (documents.to_json).to_s)
|
63
|
+
resend_message(documents_json, amount_of_documents, @logstashLoganalyticsConfiguration.retransmission_time)
|
64
|
+
end
|
65
|
+
rescue Exception => ex
|
66
|
+
@logger.error("Exception in posting data to Azure Loganalytics.\n[Exception: '#{ex}]'")
|
67
|
+
@logger.trace("Exception in posting data to Azure Loganalytics.[amount_of_documents=#{amount_of_documents} documents=#{documents_json}]")
|
68
|
+
resend_message(documents_json, amount_of_documents, @logstashLoganalyticsConfiguration.retransmission_time)
|
69
|
+
end
|
70
|
+
end # end send_message_to_loganalytics
|
71
|
+
|
72
|
+
# If sending the message toAzure Loganalytics fails we would like to retry to send it again.
|
73
|
+
# We would like to do it until we reached to the duration
|
74
|
+
def resend_message(documents_json, amount_of_documents, remaining_duration)
|
75
|
+
if remaining_duration > 0
|
76
|
+
@logger.info("Resending #{amount_of_documents} documents as log type #{@custom_log_table_name} to DataCollector API in #{@logstashLoganalyticsConfiguration.RETRANSMISSION_DELAY} seconds.")
|
77
|
+
sleep @logstashLoganalyticsConfiguration.RETRANSMISSION_DELAY
|
78
|
+
begin
|
79
|
+
response = @client.post_data(documents_json,@custom_log_table_name)
|
80
|
+
if is_successfully_posted(response)
|
81
|
+
@logger.info("Successfully sent #{amount_of_documents} logs into custom log analytics table[#{@custom_log_table_name}] after resending.")
|
82
|
+
else
|
83
|
+
@logger.debug("Resending #{amount_of_documents} documents failed, will try to resend for #{(remaining_duration - @logstashLoganalyticsConfiguration.RETRANSMISSION_DELAY)}")
|
84
|
+
resend_message(documents_json, amount_of_documents, (remaining_duration - @logstashLoganalyticsConfiguration.RETRANSMISSION_DELAY))
|
85
|
+
end
|
86
|
+
rescue Exception => ex
|
87
|
+
@logger.debug("Resending #{amount_of_documents} documents failed, will try to resend for #{(remaining_duration - @logstashLoganalyticsConfiguration.RETRANSMISSION_DELAY)}")
|
88
|
+
resend_message(documents_json, amount_of_documents, (remaining_duration - @logstashLoganalyticsConfiguration.RETRANSMISSION_DELAY))
|
89
|
+
end
|
90
|
+
else
|
91
|
+
@logger.error("Could not resend #{amount_of_documents} documents, message is dropped.")
|
92
|
+
@logger.trace("Documents (#{amount_of_documents}) dropped. [documents_json=#{documents_json}]")
|
93
|
+
end
|
94
|
+
end # def resend_message
|
95
|
+
|
96
|
+
# We would like to change the amount of messages in the buffer (change_max_size)
|
97
|
+
# We change the amount according to the Azure Loganalytics limitation and the amount of messages inserted to the buffer
|
98
|
+
# in one sending window.
|
99
|
+
# Meaning that if we reached the max amount we would like to increase it.
|
100
|
+
# Else we would like to decrease it(to reduce latency for messages)
|
101
|
+
def change_message_limit_size(amount_of_documents, documents_byte_size)
|
102
|
+
new_buffer_size = @logstashLoganalyticsConfiguration.max_items
|
103
|
+
average_document_size = documents_byte_size / amount_of_documents
|
104
|
+
# If window is full we need to increase it
|
105
|
+
# "amount_of_documents" can be greater since buffer is not synchronized meaning
|
106
|
+
# that flush can occur after limit was reached.
|
107
|
+
if amount_of_documents >= @logstashLoganalyticsConfiguration.max_items
|
108
|
+
# if doubling the size wouldn't exceed the API limit
|
109
|
+
if ((2 * @logstashLoganalyticsConfiguration.max_items) * average_document_size) < @logstashLoganalyticsConfiguration.MAX_SIZE_BYTES
|
110
|
+
new_buffer_size = 2 * @logstashLoganalyticsConfiguration.max_items
|
111
|
+
else
|
112
|
+
new_buffer_size = (@logstashLoganalyticsConfiguration.MAX_SIZE_BYTES / average_document_size) -1000
|
113
|
+
end
|
114
|
+
|
115
|
+
# We would like to decrease the window but not more then the MIN_MESSAGE_AMOUNT
|
116
|
+
# We are trying to decrease it slowly to be able to send as much messages as we can in one window
|
117
|
+
elsif amount_of_documents < @logstashLoganalyticsConfiguration.max_items and @logstashLoganalyticsConfiguration.max_items != [(@logstashLoganalyticsConfiguration.max_items - @logstashLoganalyticsConfiguration.decrease_factor) ,@logstashLoganalyticsConfiguration.MIN_MESSAGE_AMOUNT].max
|
118
|
+
new_buffer_size = [(@logstashLoganalyticsConfiguration.max_items - @logstashLoganalyticsConfiguration.decrease_factor) ,@logstashLoganalyticsConfiguration.MIN_MESSAGE_AMOUNT].max
|
119
|
+
end
|
120
|
+
|
121
|
+
change_buffer_size(new_buffer_size)
|
122
|
+
end # def change_message_limit_size
|
123
|
+
|
124
|
+
# Receiving new_size as the new max buffer size.
|
125
|
+
# Changing both the buffer, the configuration and logging as necessary
|
126
|
+
def change_buffer_size(new_size)
|
127
|
+
# Change buffer size only if it's needed(new size)
|
128
|
+
if @buffer_config[:max_items] != new_size
|
129
|
+
old_buffer_size = @buffer_config[:max_items]
|
130
|
+
@buffer_config[:max_items] = new_size
|
131
|
+
@logstashLoganalyticsConfiguration.max_items = new_size
|
132
|
+
@logger.info("Changing buffer size.[configuration='#{old_buffer_size}' , new_size='#{new_size}']")
|
133
|
+
else
|
134
|
+
@logger.info("Buffer size wasn't changed.[configuration='#{old_buffer_size}' , new_size='#{new_size}']")
|
135
|
+
end
|
136
|
+
end # def change_buffer_size
|
137
|
+
|
138
|
+
# Function to return if the response is OK or else
|
139
|
+
def is_successfully_posted(response)
|
140
|
+
return (response.code == 200) ? true : false
|
141
|
+
end # def is_successfully_posted
|
142
|
+
|
143
|
+
end # LogStashAutoResizeBuffer
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
class LogstashLoganalyticsOutputConfiguration
|
3
|
+
def initialize(workspace_id, workspace_key, logger)
|
4
|
+
@workspace_id = workspace_id
|
5
|
+
@workspace_key = workspace_key
|
6
|
+
@logger = logger
|
7
|
+
|
8
|
+
# Delay between each resending of a message
|
9
|
+
@RETRANSMISSION_DELAY = 2
|
10
|
+
@MIN_MESSAGE_AMOUNT = 100
|
11
|
+
# Maximum of 30 MB per post to Log Analytics Data Collector API.
|
12
|
+
# This is a size limit for a single post.
|
13
|
+
# If the data from a single post that exceeds 30 MB, you should split it.
|
14
|
+
@loganalytics_api_data_limit = 30 * 1000 * 1000
|
15
|
+
|
16
|
+
# Taking 4K safety buffer
|
17
|
+
@MAX_SIZE_BYTES = @loganalytics_api_data_limit - 10000
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_configuration()
|
21
|
+
if @retransmission_time < 0
|
22
|
+
raise ArgumentError, "Setting retransmission_time which sets the time spent for resending each failed messages must be positive integer. [retransmission_time=#{@retransmission_time}]."
|
23
|
+
|
24
|
+
elsif @max_items < @MIN_MESSAGE_AMOUNT
|
25
|
+
raise ArgumentError, "Setting max_items to value must be greater then #{@MIN_MESSAGE_AMOUNT}."
|
26
|
+
|
27
|
+
elsif @workspace_id.empty? or @workspace_key.empty?
|
28
|
+
raise ArgumentError, "Malformed configuration , the following arguments can not be null or empty.[workspace_id=#{@workspace_id} , workspace_key=#{@workspace_key}]"
|
29
|
+
|
30
|
+
elsif @key_names.length > 500
|
31
|
+
raise ArgumentError, 'Azure Loganalytics limits the amount of columns to 500 in each table.'
|
32
|
+
end
|
33
|
+
|
34
|
+
@logger.info("Azure Loganalytics configuration was found valid.")
|
35
|
+
|
36
|
+
# If all validation pass then configuration is valid
|
37
|
+
return true
|
38
|
+
end # def validate_configuration
|
39
|
+
|
40
|
+
def azure_resource_id
|
41
|
+
@azure_resource_id
|
42
|
+
end
|
43
|
+
|
44
|
+
def RETRANSMISSION_DELAY
|
45
|
+
@RETRANSMISSION_DELAY
|
46
|
+
end
|
47
|
+
|
48
|
+
def MAX_SIZE_BYTES
|
49
|
+
@MAX_SIZE_BYTES
|
50
|
+
end
|
51
|
+
|
52
|
+
def amount_resizing
|
53
|
+
@amount_resizing
|
54
|
+
end
|
55
|
+
|
56
|
+
def retransmission_time
|
57
|
+
@retransmission_time
|
58
|
+
end
|
59
|
+
|
60
|
+
def proxy
|
61
|
+
@proxy
|
62
|
+
end
|
63
|
+
|
64
|
+
def logger
|
65
|
+
@logger
|
66
|
+
end
|
67
|
+
|
68
|
+
def decrease_factor
|
69
|
+
@decrease_factor
|
70
|
+
end
|
71
|
+
|
72
|
+
def workspace_id
|
73
|
+
@workspace_id
|
74
|
+
end
|
75
|
+
|
76
|
+
def workspace_key
|
77
|
+
@workspace_key
|
78
|
+
end
|
79
|
+
|
80
|
+
def endpoint
|
81
|
+
@endpoint
|
82
|
+
end
|
83
|
+
|
84
|
+
def time_generated_field
|
85
|
+
@time_generated_field
|
86
|
+
end
|
87
|
+
|
88
|
+
def key_names
|
89
|
+
@key_names
|
90
|
+
end
|
91
|
+
|
92
|
+
def max_items
|
93
|
+
@max_items
|
94
|
+
end
|
95
|
+
|
96
|
+
def plugin_flush_interval
|
97
|
+
@plugin_flush_interval
|
98
|
+
end
|
99
|
+
|
100
|
+
def MIN_MESSAGE_AMOUNT
|
101
|
+
@MIN_MESSAGE_AMOUNT
|
102
|
+
end
|
103
|
+
|
104
|
+
def max_items=(new_max_items)
|
105
|
+
@max_items = new_max_items
|
106
|
+
end
|
107
|
+
|
108
|
+
def endpoint=(new_endpoint)
|
109
|
+
@endpoint = new_endpoint
|
110
|
+
end
|
111
|
+
|
112
|
+
def time_generated_field=(new_time_generated_field)
|
113
|
+
@time_generated_field = new_time_generated_field
|
114
|
+
end
|
115
|
+
|
116
|
+
def key_names=(new_key_names)
|
117
|
+
@key_names = new_key_names
|
118
|
+
end
|
119
|
+
|
120
|
+
def plugin_flush_interval=(new_plugin_flush_interval)
|
121
|
+
@plugin_flush_interval = new_plugin_flush_interval
|
122
|
+
end
|
123
|
+
|
124
|
+
def decrease_factor=(new_decrease_factor)
|
125
|
+
@decrease_factor = new_decrease_factor
|
126
|
+
end
|
127
|
+
|
128
|
+
def amount_resizing=(new_amount_resizing)
|
129
|
+
@amount_resizing = new_amount_resizing
|
130
|
+
end
|
131
|
+
|
132
|
+
def max_items=(new_max_items)
|
133
|
+
@max_items = new_max_items
|
134
|
+
end
|
135
|
+
|
136
|
+
def azure_resource_id=(new_azure_resource_id)
|
137
|
+
@azure_resource_id = new_azure_resource_id
|
138
|
+
end
|
139
|
+
|
140
|
+
def proxy=(new_proxy)
|
141
|
+
@proxy = new_proxy
|
142
|
+
end
|
143
|
+
|
144
|
+
def retransmission_time=(new_retransmission_time)
|
145
|
+
@retransmission_time = new_retransmission_time
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/outputs/base"
|
3
|
+
require "logstash/namespace"
|
4
|
+
require "stud/buffer"
|
5
|
+
require "logstash/logAnalyticsClient/logStashAutoResizeBuffer"
|
6
|
+
require "logstash/logAnalyticsClient/logstashLoganalyticsConfiguration"
|
7
|
+
|
8
|
+
class LogStash::Outputs::AzureLogAnalytics < LogStash::Outputs::Base
|
9
|
+
|
10
|
+
config_name "sentinelblue-logstash-output-azure-loganalytics"
|
11
|
+
|
12
|
+
# Stating that the output plugin will run in concurrent mode
|
13
|
+
concurrency :shared
|
14
|
+
|
15
|
+
# Your Operations Management Suite workspace ID
|
16
|
+
config :workspace_id, :validate => :string, :required => true
|
17
|
+
|
18
|
+
# The primary or the secondary key used for authentication, required by Azure Loganalytics REST API
|
19
|
+
config :workspace_key, :validate => :string, :required => true
|
20
|
+
|
21
|
+
# The name of the event type that is being submitted to Log Analytics.
|
22
|
+
# This must be only alpha characters, numbers and underscore.
|
23
|
+
# This must not exceed 100 characters.
|
24
|
+
# Table name under custom logs in which the data will be inserted
|
25
|
+
config :custom_log_table_name, :validate => :string, :required => true
|
26
|
+
|
27
|
+
# The service endpoint (Default: ods.opinsights.azure.com)
|
28
|
+
config :endpoint, :validate => :string, :default => 'ods.opinsights.azure.com'
|
29
|
+
|
30
|
+
# The name of the time generated field.
|
31
|
+
# Be careful that the value of field should strictly follow the ISO 8601 format (YYYY-MM-DDThh:mm:ssZ)
|
32
|
+
config :time_generated_field, :validate => :string, :default => ''
|
33
|
+
|
34
|
+
# Subset of keys to send to the Azure Loganalytics workspace
|
35
|
+
config :key_names, :validate => :array, :default => []
|
36
|
+
|
37
|
+
# # Max number of items to buffer before flushing. Default 50.
|
38
|
+
# config :flush_items, :validate => :number, :default => 50
|
39
|
+
|
40
|
+
# Max number of seconds to wait between flushes. Default 5
|
41
|
+
config :plugin_flush_interval, :validate => :number, :default => 5
|
42
|
+
|
43
|
+
# Factor for adding to the amount of messages sent
|
44
|
+
config :decrease_factor, :validate => :number, :default => 100
|
45
|
+
|
46
|
+
# This will trigger message amount resizing in a REST request to LA
|
47
|
+
config :amount_resizing, :validate => :boolean, :default => true
|
48
|
+
|
49
|
+
# Setting the default amount of messages sent
|
50
|
+
# it this is set with amount_resizing=false --> each message will have max_items
|
51
|
+
config :max_items, :validate => :number, :default => 2000
|
52
|
+
|
53
|
+
# Setting proxy to be used for the Azure Loganalytics REST client
|
54
|
+
config :proxy, :validate => :string, :default => ''
|
55
|
+
|
56
|
+
# This will set the amount of time given for retransmitting messages once sending is failed
|
57
|
+
config :retransmission_time, :validate => :number, :default => 10
|
58
|
+
|
59
|
+
# Optional to override the resource ID field on the workspace table.
|
60
|
+
# Resource ID provided must be a valid resource ID on azure
|
61
|
+
config :azure_resource_id, :validate => :string, :default => ''
|
62
|
+
|
63
|
+
public
|
64
|
+
def register
|
65
|
+
@logstash_configuration= build_logstash_configuration()
|
66
|
+
# Validate configuration correctness
|
67
|
+
@logstash_configuration.validate_configuration()
|
68
|
+
@logger.info("Logstash Sentinel Blue Azure Log Analytics output plugin configuration was found valid")
|
69
|
+
|
70
|
+
# Initialize the logstash resizable buffer
|
71
|
+
# This buffer will increase and decrease size according to the amount of messages inserted.
|
72
|
+
# If the buffer reached the max amount of messages the amount will be increased until the limit
|
73
|
+
# @logstash_resizable_event_buffer=LogStashAutoResizeBuffer::new(@logstash_configuration)
|
74
|
+
|
75
|
+
end # def register
|
76
|
+
|
77
|
+
def multi_receive(events)
|
78
|
+
|
79
|
+
# Create a hash table for each buffer. A buffer is needed per table used
|
80
|
+
buffers = Hash.new
|
81
|
+
|
82
|
+
events.each do |event|
|
83
|
+
# creating document from event
|
84
|
+
document = create_event_document(event)
|
85
|
+
# Skip if document doesn't contain any items
|
86
|
+
next if (document.keys).length < 1
|
87
|
+
|
88
|
+
# Get the custom table name
|
89
|
+
custom_table_name = ""
|
90
|
+
|
91
|
+
# Check if the table name is static or dynamic
|
92
|
+
if @custom_log_table_name.match(/^[[:alpha:][:digit:]_]+$/)
|
93
|
+
# Table name is static.
|
94
|
+
custom_table_name = @custom_log_table_name
|
95
|
+
|
96
|
+
# /^%\{((([a-zA-Z]|[0-9]|_)+)|(\[((([a-zA-Z]|[0-9]|_|@)+))\])+)\}$/
|
97
|
+
elsif @custom_log_table_name.match(/^%{((\[[\w_\-@]*\])*)([\w_\-@]*)}$/)
|
98
|
+
# Table name is dynamic
|
99
|
+
custom_table_name = event.sprintf(@custom_log_table_name)
|
100
|
+
|
101
|
+
else
|
102
|
+
# Incorrect format
|
103
|
+
@logger.warn("custom_log_table_name must be either a static name consisting only of numbers, letters, and underscores OR a dynamic table name of the format used by logstash (e.g. %{field_name}, %{[nested][field]}.")
|
104
|
+
break
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
# Check that the table name is a string, exists, and is les than 100 characters
|
109
|
+
if !custom_table_name.is_a?(String)
|
110
|
+
@logger.warn("The custom table name must be a string. If you used a dynamic name from one of the log fields, make sure it is a string.")
|
111
|
+
break
|
112
|
+
|
113
|
+
elsif custom_table_name.empty? or custom_table_name.nil?
|
114
|
+
@logger.warn("The custom table name is empty. If you used a dynamic name from one of the log fields, check that it exists.")
|
115
|
+
break
|
116
|
+
|
117
|
+
elsif custom_table_name.length > 100
|
118
|
+
@logger.warn("The custom table name must not exceed 100 characters")
|
119
|
+
break
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
@logger.info("Custom table name #{custom_table_name} is valid")
|
124
|
+
|
125
|
+
# Determine if there is a buffer for the given table
|
126
|
+
if buffers.keys.include?(custom_table_name)
|
127
|
+
@logger.trace("Adding event document - " + event.to_s)
|
128
|
+
buffers[custom_table_name].add_event_document(document)
|
129
|
+
|
130
|
+
else
|
131
|
+
# If the buffer doesn't exist for the table, create one and add the document
|
132
|
+
buffers[custom_table_name] = LogStashAutoResizeBuffer::new(@logstash_configuration,custom_table_name)
|
133
|
+
@logger.trace("Adding event document - " + event.to_s)
|
134
|
+
buffers[custom_table_name].add_event_document(document)
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end # events.each do
|
139
|
+
end # def multi_receive
|
140
|
+
|
141
|
+
#private
|
142
|
+
private
|
143
|
+
|
144
|
+
# In case that the user has defined key_names meaning that he would like to a subset of the data,
|
145
|
+
# we would like to insert only those keys.
|
146
|
+
# If no keys were defined we will send all the data
|
147
|
+
|
148
|
+
def create_event_document(event)
|
149
|
+
document = {}
|
150
|
+
event_hash = event.to_hash()
|
151
|
+
if @key_names.length > 0
|
152
|
+
# Get the intersection of key_names and keys of event_hash
|
153
|
+
keys_intersection = @key_names & event_hash.keys
|
154
|
+
keys_intersection.each do |key|
|
155
|
+
document[key] = event_hash[key]
|
156
|
+
end
|
157
|
+
if document.keys.length < 1
|
158
|
+
@logger.warn("No keys found, message is dropped. Plugin keys: #{@key_names}, Event keys: #{event_hash}. The event message do not match event expected structre. Please edit key_names section in output plugin and try again.")
|
159
|
+
end
|
160
|
+
else
|
161
|
+
document = event_hash
|
162
|
+
end
|
163
|
+
|
164
|
+
return document
|
165
|
+
end # def create_event_document
|
166
|
+
|
167
|
+
# Building the logstash object configuration from the output configuration provided by the user
|
168
|
+
# Return LogstashLoganalyticsOutputConfiguration populated with the configuration values
|
169
|
+
def build_logstash_configuration()
|
170
|
+
logstash_configuration= LogstashLoganalyticsOutputConfiguration::new(@workspace_id, @workspace_key, @logger)
|
171
|
+
logstash_configuration.endpoint = @endpoint
|
172
|
+
logstash_configuration.time_generated_field = @time_generated_field
|
173
|
+
logstash_configuration.key_names = @key_names
|
174
|
+
logstash_configuration.plugin_flush_interval = @plugin_flush_interval
|
175
|
+
logstash_configuration.decrease_factor = @decrease_factor
|
176
|
+
logstash_configuration.amount_resizing = @amount_resizing
|
177
|
+
logstash_configuration.max_items = @max_items
|
178
|
+
logstash_configuration.azure_resource_id = @azure_resource_id
|
179
|
+
logstash_configuration.proxy = @proxy
|
180
|
+
logstash_configuration.retransmission_time = @retransmission_time
|
181
|
+
|
182
|
+
return logstash_configuration
|
183
|
+
end # def build_logstash_configuration
|
184
|
+
|
185
|
+
end # class LogStash::Outputs::AzureLogAnalytics
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'sentinelblue-logstash-output-azure-loganalytics'
|
3
|
+
s.version = File.read("VERSION").strip
|
4
|
+
s.authors = ["Sentinel Blue"]
|
5
|
+
s.email = "info@sentinelblue.com"
|
6
|
+
s.summary = %q{Sentinel Blue provides a plugin outputing to Azure Sentinel for Logstash. Using this output plugin, you will be able to send any log you want using Logstash to the Azure Sentinel/Log Analytics workspace. You can utilize a dynamic table name during output to simplify complex table schemes.}
|
7
|
+
s.description = s.summary
|
8
|
+
s.homepage = "https://github.com/sentinelblue/sentinelblue-logstash-output-azure-loganalytics"
|
9
|
+
s.licenses = ['Apache License (2.0)']
|
10
|
+
s.require_paths = ["lib"]
|
11
|
+
|
12
|
+
# Files
|
13
|
+
s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT', 'VERSION']
|
14
|
+
# Tests
|
15
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
16
|
+
|
17
|
+
# Special flag to let us know this is actually a logstash plugin
|
18
|
+
s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" }
|
19
|
+
|
20
|
+
# Gem dependencies
|
21
|
+
s.add_runtime_dependency "rest-client", ">= 1.8.0"
|
22
|
+
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
|
23
|
+
s.add_runtime_dependency "logstash-codec-plain"
|
24
|
+
s.add_development_dependency "logstash-devutils"
|
25
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/devutils/rspec/spec_helper"
|
3
|
+
require "logstash/outputs/sentinelblue-logstash-output-azure-loganalytics"
|
4
|
+
require "logstash/codecs/plain"
|
5
|
+
require "logstash/event"
|
6
|
+
|
7
|
+
describe LogStash::Outputs::AzureLogAnalytics do
|
8
|
+
|
9
|
+
let(:workspace_id) { '<Workspace ID identifying your workspace>' }
|
10
|
+
let(:workspace_key) { '<Primary Key for the Azure log analytics workspace>' }
|
11
|
+
let(:custom_log_table_name) { 'ApacheAccessLog' }
|
12
|
+
let(:key_names) { ['logid','date','processing_time','remote','user','method','status','agent','eventtime'] }
|
13
|
+
let(:time_generated_field) { 'eventtime' }
|
14
|
+
let(:amount_resizing) {false}
|
15
|
+
|
16
|
+
# 1 second flush interval
|
17
|
+
let(:plugin_flush_interval) {1}
|
18
|
+
|
19
|
+
let(:azure_loganalytics_config) {
|
20
|
+
{
|
21
|
+
"workspace_id" => workspace_id,
|
22
|
+
"workspace_key" => workspace_key,
|
23
|
+
"custom_log_table_name" => custom_log_table_name,
|
24
|
+
"key_names" => key_names,
|
25
|
+
"time_generated_field" => time_generated_field,
|
26
|
+
"plugin_flush_interval" => plugin_flush_interval,
|
27
|
+
"amount_resizing" => amount_resizing
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
let(:azure_loganalytics) { LogStash::Outputs::AzureLogAnalytics.new(azure_loganalytics_config) }
|
32
|
+
|
33
|
+
before do
|
34
|
+
azure_loganalytics.register
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#flush" do
|
38
|
+
it "Should successfully send the event to Azure Log Analytics" do
|
39
|
+
events = []
|
40
|
+
log1 = {
|
41
|
+
:logid => "5cdad72f-c848-4df0-8aaa-ffe033e75d57",
|
42
|
+
:date => "2017-04-22 09:44:32 JST",
|
43
|
+
:processing_time => "372",
|
44
|
+
:remote => "101.202.74.59",
|
45
|
+
:user => "-",
|
46
|
+
:method => "GET / HTTP/1.1",
|
47
|
+
:status => "304",
|
48
|
+
:size => "-",
|
49
|
+
:referer => "-",
|
50
|
+
:agent => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:27.0) Gecko/20100101 Firefox/27.0",
|
51
|
+
:eventtime => "2017-04-22T01:44:32Z"
|
52
|
+
}
|
53
|
+
|
54
|
+
log2 = {
|
55
|
+
:logid => "7260iswx-8034-4cc3-uirtx-f068dd4cd659",
|
56
|
+
:date => "2017-04-22 09:45:14 JST",
|
57
|
+
:processing_time => "105",
|
58
|
+
:remote => "201.78.74.59",
|
59
|
+
:user => "-",
|
60
|
+
:method => "GET /manager/html HTTP/1.1",
|
61
|
+
:status =>"200",
|
62
|
+
:size => "-",
|
63
|
+
:referer => "-",
|
64
|
+
:agent => "Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0",
|
65
|
+
:eventtime => "2017-04-22T01:45:14Z"
|
66
|
+
}
|
67
|
+
|
68
|
+
event1 = LogStash::Event.new(log1)
|
69
|
+
event2 = LogStash::Event.new(log2)
|
70
|
+
events.push(event1)
|
71
|
+
events.push(event2)
|
72
|
+
expect {azure_loganalytics.multi_receive(events)}.to_not raise_error
|
73
|
+
# Waiting for the data to be sent
|
74
|
+
sleep(plugin_flush_interval + 2)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sentinelblue-logstash-output-azure-loganalytics
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sentinel Blue
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-10-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rest-client
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.8.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.8.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: logstash-core-plugin-api
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.60'
|
34
|
+
- - "<="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '2.99'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.60'
|
44
|
+
- - "<="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2.99'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: logstash-codec-plain
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: logstash-devutils
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
description: Sentinel Blue provides a plugin outputing to Azure Sentinel for Logstash.
|
76
|
+
Using this output plugin, you will be able to send any log you want using Logstash
|
77
|
+
to the Azure Sentinel/Log Analytics workspace. You can utilize a dynamic table name
|
78
|
+
during output to simplify complex table schemes.
|
79
|
+
email: info@sentinelblue.com
|
80
|
+
executables: []
|
81
|
+
extensions: []
|
82
|
+
extra_rdoc_files: []
|
83
|
+
files:
|
84
|
+
- CHANGELOG.md
|
85
|
+
- Gemfile
|
86
|
+
- LICENSE
|
87
|
+
- README.md
|
88
|
+
- VERSION
|
89
|
+
- lib/logstash/logAnalyticsClient/logAnalyticsClient.rb
|
90
|
+
- lib/logstash/logAnalyticsClient/logStashAutoResizeBuffer.rb
|
91
|
+
- lib/logstash/logAnalyticsClient/logstashLoganalyticsConfiguration.rb
|
92
|
+
- lib/logstash/outputs/sentinelblue-logstash-output-azure-loganalytics.rb
|
93
|
+
- sentinelblue-logstash-output-azure-loganalytics.gemspec
|
94
|
+
- spec/outputs/azure_loganalytics_spec.rb
|
95
|
+
homepage: https://github.com/sentinelblue/sentinelblue-logstash-output-azure-loganalytics
|
96
|
+
licenses:
|
97
|
+
- Apache License (2.0)
|
98
|
+
metadata:
|
99
|
+
logstash_plugin: 'true'
|
100
|
+
logstash_group: output
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubygems_version: 3.3.7
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: Sentinel Blue provides a plugin outputing to Azure Sentinel for Logstash.
|
120
|
+
Using this output plugin, you will be able to send any log you want using Logstash
|
121
|
+
to the Azure Sentinel/Log Analytics workspace. You can utilize a dynamic table name
|
122
|
+
during output to simplify complex table schemes.
|
123
|
+
test_files:
|
124
|
+
- spec/outputs/azure_loganalytics_spec.rb
|