fluent-plugin-lm-logs 1.2.7 → 1.2.8
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 +2 -30
- data/fluent-plugin-lm-logs.gemspec +1 -1
- data/lib/fluent/plugin/out_lm.rb +2 -39
- data/lib/fluent/plugin/version.rb +1 -1
- metadata +1 -2
- data/lib/fluent/plugin/environment_detector.rb +0 -201
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be230126826d58c062ed0493f131ff0a7fdf247e6d76bbc437e95236e49c9a95
|
4
|
+
data.tar.gz: 89033092d183a2a1fb45232780900845001faca6c43390ffc5e48446ed70e823
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90d81328fba7ccdc1849e70cd3db32be01a2b078d8b54c6ae33e463e3d2dd0df2d966681efdb77b753653ad3bea6f002dcadb5701d809ed3318296eeb12258ee
|
7
|
+
data.tar.gz: 7dc4861de063df78f03836b773fab750b629c0d221ccf4585b9599b1064aeafece3bd922161ac796bbc5705d3bde34c8315108b1f32e57ee7f7b85145cb5ecea
|
data/README.md
CHANGED
@@ -34,34 +34,6 @@ Create a custom `fluent.conf` or edit the existing one to specify which logs sho
|
|
34
34
|
debug false
|
35
35
|
</match>
|
36
36
|
```
|
37
|
-
### Dynamic resource type
|
38
|
-
|
39
|
-
If you want to use a dynamic resource type, you can leave the `resource_type` field empty. The plugin will then automatically assign the resource type based on either of below:
|
40
|
-
* If user assigns source-specific tags as below:
|
41
|
-
```
|
42
|
-
Tag windows.server1.logs
|
43
|
-
Tag linux.vm02.logs
|
44
|
-
```
|
45
|
-
* In the fluentd conf file:
|
46
|
-
```
|
47
|
-
<filter **>
|
48
|
-
@type record_transformer
|
49
|
-
enable_ruby true
|
50
|
-
<record>
|
51
|
-
resource_type ${record["resource_type"] || "Unknown"}
|
52
|
-
</record>
|
53
|
-
</filter>
|
54
|
-
```
|
55
|
-
* If the remote agent includes a host field (many do), we can use heuristics:
|
56
|
-
```
|
57
|
-
|
58
|
-
host = record['host'] || record['hostname'] || ''
|
59
|
-
return 'AWS/VirtualMachine' if host.start_with?('ip-')
|
60
|
-
return 'GCP/VirtualMachine' if host.include?('.c.') || host.include?('gcp')
|
61
|
-
return 'WindowsServer' if host.include?('win')
|
62
|
-
return 'LinuxServer' if host.include?('linux')
|
63
|
-
'Unknown'
|
64
|
-
```
|
65
37
|
|
66
38
|
### Request example
|
67
39
|
|
@@ -97,8 +69,8 @@ See the [LogicMonitor Helm repository](https://github.com/logicmonitor/k8s-helm-
|
|
97
69
|
| `resource_mapping` | The mapping that defines the source of the log event to the LM resource. In this case, the `<event_key>` in the incoming event is mapped to the value of `<lm_property>`.|
|
98
70
|
| `access_id` | LM API Token access ID. |
|
99
71
|
| `access_key` | LM API Token access key. |
|
100
|
-
| `resource_type` | If a Resource Type is specified,
|
101
|
-
| `bearer_token` | LM API Bearer Token. Either specify `access_id` and `access_key` both or `bearer_token`. If all specified, LMv1 token(`access_id` and `access_key`) will be used for authentication with Logicmonitor.
|
72
|
+
| `resource_type` | If a Resource Type is explicitly specified, that value will be statically applied to all ingested logs. If set to `##predef.externalResourceType##`, the Resource Type will be assigned dynamically based on the log context or configuration. If left blank, the Resource Type field will remain unset in the ingested logs. |
|
73
|
+
| `bearer_token` | LM API Bearer Token. Either specify `access_id` and `access_key` both or `bearer_token`. If all specified, LMv1 token(`access_id` and `access_key`) will be used for authentication with Logicmonitor. |
|
102
74
|
| `flush_interval` | Defines the time in seconds to wait before sending batches of logs to LogicMonitor. Default is `60s`. |
|
103
75
|
| `debug` | When `true`, logs more information to the fluentd console. |
|
104
76
|
| `force_encoding` | Specify charset when logs contains invalid utf-8 characters. |
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.metadata["source_code_uri"] = "https://github.com/logicmonitor/lm-logs-fluentd"
|
22
22
|
spec.metadata["documentation_uri"] = "https://www.rubydoc.info/gems/lm-logs-fluentd"
|
23
23
|
|
24
|
-
spec.files = [".gitignore", "Gemfile", "LICENSE", "README.md", "Rakefile", "fluent-plugin-lm-logs.gemspec", "lib/fluent/plugin/version.rb", "lib/fluent/plugin/out_lm.rb"
|
24
|
+
spec.files = [".gitignore", "Gemfile", "LICENSE", "README.md", "Rakefile", "fluent-plugin-lm-logs.gemspec", "lib/fluent/plugin/version.rb", "lib/fluent/plugin/out_lm.rb"]
|
25
25
|
spec.require_paths = ["lib"]
|
26
26
|
spec.required_ruby_version = '>= 2.0.0'
|
27
27
|
|
data/lib/fluent/plugin/out_lm.rb
CHANGED
@@ -10,7 +10,6 @@ require 'net/http'
|
|
10
10
|
require 'net/http/persistent'
|
11
11
|
require 'net/https'
|
12
12
|
require('zlib')
|
13
|
-
require_relative 'environment_detector'
|
14
13
|
|
15
14
|
require_relative "version"
|
16
15
|
|
@@ -84,11 +83,6 @@ module Fluent
|
|
84
83
|
@http_client.override_headers["User-Agent"] = log_source + "/" + LmLogsFluentPlugin::VERSION
|
85
84
|
@url = "https://#{@company_name}.#{@company_domain}/rest/log/ingest"
|
86
85
|
@uri = URI.parse(@url)
|
87
|
-
@detector = EnvironmentDetector.new
|
88
|
-
@environment_info = @detector.detect
|
89
|
-
@local_env_str = format_environment(@environment_info)
|
90
|
-
|
91
|
-
log.info("Environment detected: #{@environment_info}")
|
92
86
|
end
|
93
87
|
|
94
88
|
def configure_auth
|
@@ -180,13 +174,10 @@ module Fluent
|
|
180
174
|
end
|
181
175
|
lm_event["message"] = encode_if_necessary(record["message"])
|
182
176
|
|
183
|
-
|
184
|
-
|
185
|
-
resource_type = @local_env_str
|
177
|
+
if !is_blank(@resource_type)
|
178
|
+
lm_event['_resource.type'] = resource_type
|
186
179
|
end
|
187
180
|
|
188
|
-
lm_event['_resource.type'] = resource_type
|
189
|
-
|
190
181
|
return lm_event
|
191
182
|
end
|
192
183
|
|
@@ -273,34 +264,6 @@ module Fluent
|
|
273
264
|
end
|
274
265
|
end
|
275
266
|
|
276
|
-
def format_environment(env_info)
|
277
|
-
runtime = env_info[:runtime]
|
278
|
-
provider = env_info[:provider] if env_info.key?(:provider)
|
279
|
-
|
280
|
-
case runtime
|
281
|
-
when 'kubernetes'
|
282
|
-
'Kubernetes/Node'
|
283
|
-
when 'docker'
|
284
|
-
'Docker/Host'
|
285
|
-
when 'vm'
|
286
|
-
case provider&.downcase
|
287
|
-
when 'azure'
|
288
|
-
'Azure/VirtualMachine'
|
289
|
-
when 'aws'
|
290
|
-
'AWS/EC2'
|
291
|
-
when 'gcp'
|
292
|
-
'GCP/ComputeEngine'
|
293
|
-
else
|
294
|
-
'Unknown/VirtualMachine'
|
295
|
-
end
|
296
|
-
when 'physical'
|
297
|
-
os = env_info[:os] || 'UnknownOS'
|
298
|
-
product = env_info[:product] || 'UnknownHardware'
|
299
|
-
"#{os} / #{product}"
|
300
|
-
else
|
301
|
-
'UnknownEnvironment'
|
302
|
-
end
|
303
|
-
end
|
304
267
|
end
|
305
268
|
end
|
306
269
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-lm-logs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- LogicMonitor
|
@@ -56,7 +56,6 @@ files:
|
|
56
56
|
- README.md
|
57
57
|
- Rakefile
|
58
58
|
- fluent-plugin-lm-logs.gemspec
|
59
|
-
- lib/fluent/plugin/environment_detector.rb
|
60
59
|
- lib/fluent/plugin/out_lm.rb
|
61
60
|
- lib/fluent/plugin/version.rb
|
62
61
|
homepage: https://www.logicmonitor.com
|
@@ -1,201 +0,0 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
require 'timeout'
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
class EnvironmentDetector
|
6
|
-
METADATA_TIMEOUT = 1
|
7
|
-
|
8
|
-
def detect
|
9
|
-
if running_in_kubernetes?
|
10
|
-
{ runtime: 'kubernetes' }.merge(detect_node_info)
|
11
|
-
elsif running_in_docker?
|
12
|
-
{ runtime: 'docker' }.merge(detect_node_info)
|
13
|
-
else
|
14
|
-
detect_host_environment
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def format_environment(env_info)
|
19
|
-
runtime = env_info[:runtime]
|
20
|
-
provider = env_info[:provider] if env_info.key?(:provider)
|
21
|
-
|
22
|
-
case runtime
|
23
|
-
when 'kubernetes'
|
24
|
-
'Kubernetes/Node'
|
25
|
-
when 'docker'
|
26
|
-
'Docker/Host'
|
27
|
-
when 'vm'
|
28
|
-
case provider&.downcase
|
29
|
-
when 'azure'
|
30
|
-
'Azure/VirtualMachine'
|
31
|
-
when 'aws'
|
32
|
-
'AWS/EC2'
|
33
|
-
when 'gcp'
|
34
|
-
'GCP/ComputeEngine'
|
35
|
-
else
|
36
|
-
'Unknown/VirtualMachine'
|
37
|
-
end
|
38
|
-
when 'physical'
|
39
|
-
os = env_info[:os] || 'UnknownOS'
|
40
|
-
product = env_info[:product] || 'UnknownHardware'
|
41
|
-
"#{os} / #{product}"
|
42
|
-
else
|
43
|
-
'UnknownEnvironment'
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def infer_resource_type(record, tag = nil)
|
48
|
-
return record['resource_type'] if record['resource_type']
|
49
|
-
|
50
|
-
host = (record['host'] || record['hostname'] || '').to_s
|
51
|
-
msg = (record['message'] || '').to_s
|
52
|
-
program = (record['syslog_program'] || '').to_s
|
53
|
-
tags = record['tags'] || []
|
54
|
-
tag_down = tag&.downcase || ''
|
55
|
-
|
56
|
-
host_down = host.downcase
|
57
|
-
msg_down = msg.downcase
|
58
|
-
program_down = program.downcase
|
59
|
-
# From tag pattern (case-insensitive)
|
60
|
-
return 'WindowsServer' if tag_down.include?('windows')
|
61
|
-
return 'LinuxServer' if tag_down.include?('linux')
|
62
|
-
return 'Kubernetes/Node' if tag_down.include?('k8s') || tag_down.include?('kubernetes')
|
63
|
-
return 'Docker/Host' if tag_down.include?('docker')
|
64
|
-
|
65
|
-
# Structured metadata
|
66
|
-
return 'Kubernetes/Node' if record.key?('kubernetes')
|
67
|
-
return 'Docker/Host' if record.key?('container_id') || record.dig('docker', 'container_id')
|
68
|
-
return 'AWS/VirtualMachine' if host_down.start_with?('ip-') || msg_down.include?('amazon')
|
69
|
-
return 'GCP/VirtualMachine' if host_down.include?('.c.') || host_down.include?('gcp')
|
70
|
-
return 'Azure/VirtualMachine' if host_down.include?('cloudapp.net') || msg_down.include?('azure')
|
71
|
-
return 'VMware/VirtualMachine' if msg_down.include?('vmware') || host_down.include?('vmware')
|
72
|
-
|
73
|
-
return 'WindowsServer' if record.key?('EventID') || record.key?('ProviderName') || record.key?('Computer')
|
74
|
-
return 'LinuxServer' if record.key?('syslog_facility') || program_down != ''
|
75
|
-
|
76
|
-
return 'Firewall' if program_down.downcase.include?('firewalld') || msg_down.downcase.include?('iptables') || msg_down.include?('blocked by policy')
|
77
|
-
return 'ACMEServer' if host_down.include?('acme') || msg_down.include?('ACME-Request') || tags.include?('acme')
|
78
|
-
return 'WebServer' if msg_down.include?('nginx') || msg_down.include?('apache')
|
79
|
-
return 'DatabaseServer' if msg_down.include?('mysql') || msg_down.include?('postgres') || msg_down.include?('oracle')
|
80
|
-
|
81
|
-
'Unknown'
|
82
|
-
end
|
83
|
-
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
def running_in_kubernetes?
|
88
|
-
ENV.key?('KUBERNETES_SERVICE_HOST') || ENV.key?('KUBERNETES_PORT')
|
89
|
-
end
|
90
|
-
|
91
|
-
def running_in_docker?
|
92
|
-
return true if ENV['container'] == 'docker'
|
93
|
-
cgroup = File.read('/proc/1/cgroup') rescue ''
|
94
|
-
return true if cgroup.include?('docker') || cgroup.include?('containerd')
|
95
|
-
File.exist?('/.dockerenv')
|
96
|
-
end
|
97
|
-
|
98
|
-
def detect_host_environment
|
99
|
-
provider_info = detect_cloud_provider
|
100
|
-
return { runtime: 'vm', provider: provider_info[:provider], details: provider_info[:details] } if provider_info
|
101
|
-
|
102
|
-
os = detect_os
|
103
|
-
product = detect_product_info
|
104
|
-
|
105
|
-
if product.downcase.include?('xen hvm domu') && os.downcase.include?('amazon')
|
106
|
-
return { runtime: 'vm', provider: 'aws', details: { os: os, product: product } }
|
107
|
-
end
|
108
|
-
|
109
|
-
{ runtime: 'physical', os: os, product: product }
|
110
|
-
end
|
111
|
-
|
112
|
-
def detect_node_info
|
113
|
-
{ node_os: detect_os, node_product: detect_product_info }
|
114
|
-
end
|
115
|
-
|
116
|
-
def detect_cloud_provider
|
117
|
-
azure_metadata || aws_metadata || gcp_metadata
|
118
|
-
end
|
119
|
-
|
120
|
-
def azure_metadata
|
121
|
-
url = 'http://169.254.169.254/metadata/instance?api-version=2021-02-01'
|
122
|
-
headers = { 'Metadata' => 'true' }
|
123
|
-
response = fetch_metadata(url, headers)
|
124
|
-
return unless response
|
125
|
-
json = JSON.parse(response) rescue {}
|
126
|
-
{
|
127
|
-
provider: 'azure',
|
128
|
-
details: {
|
129
|
-
vm_id: json.dig('compute', 'vmId'),
|
130
|
-
location: json.dig('compute', 'location'),
|
131
|
-
name: json.dig('compute', 'name'),
|
132
|
-
vm_size: json.dig('compute', 'vmSize')
|
133
|
-
}
|
134
|
-
}
|
135
|
-
end
|
136
|
-
|
137
|
-
def aws_metadata
|
138
|
-
url = 'http://169.254.169.254/latest/meta-data/instance-id'
|
139
|
-
response = fetch_metadata(url)
|
140
|
-
return unless response
|
141
|
-
{ provider: 'aws', details: { instance_id: response.strip } }
|
142
|
-
end
|
143
|
-
|
144
|
-
def gcp_metadata
|
145
|
-
url = 'http://169.254.169.254/computeMetadata/v1/instance/id'
|
146
|
-
headers = { 'Metadata-Flavor' => 'Google' }
|
147
|
-
response = fetch_metadata(url, headers)
|
148
|
-
return unless response
|
149
|
-
{ provider: 'gcp', details: { instance_id: response.strip } }
|
150
|
-
end
|
151
|
-
|
152
|
-
def fetch_metadata(url, headers = {}, timeout_sec = METADATA_TIMEOUT)
|
153
|
-
uri = URI(url)
|
154
|
-
Timeout.timeout(timeout_sec) do
|
155
|
-
req = Net::HTTP::Get.new(uri)
|
156
|
-
headers.each { |k, v| req[k] = v }
|
157
|
-
res = Net::HTTP.start(uri.host, uri.port, open_timeout: timeout_sec, read_timeout: timeout_sec) { |http| http.request(req) }
|
158
|
-
return res.body if res.is_a?(Net::HTTPSuccess)
|
159
|
-
end
|
160
|
-
rescue Timeout::Error, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, EOFError
|
161
|
-
nil
|
162
|
-
end
|
163
|
-
|
164
|
-
def detect_os
|
165
|
-
if File.exist?('/etc/os-release')
|
166
|
-
os_info = {}
|
167
|
-
File.foreach('/etc/os-release') do |line|
|
168
|
-
key, value = line.strip.split('=', 2)
|
169
|
-
os_info[key] = value&.gsub('"', '')
|
170
|
-
end
|
171
|
-
"#{os_info['NAME']} #{os_info['VERSION']}"
|
172
|
-
elsif RUBY_PLATFORM.include?('darwin')
|
173
|
-
product_name = `sw_vers -productName`.strip
|
174
|
-
product_version = `sw_vers -productVersion`.strip
|
175
|
-
"#{product_name} #{product_version}"
|
176
|
-
else
|
177
|
-
`uname -a`.strip
|
178
|
-
end
|
179
|
-
rescue
|
180
|
-
'unknown'
|
181
|
-
end
|
182
|
-
|
183
|
-
def detect_product_info
|
184
|
-
if File.exist?('/sys/class/dmi/id/sys_vendor') && File.exist?('/sys/class/dmi/id/product_name')
|
185
|
-
vendor = read_file('/sys/class/dmi/id/sys_vendor')
|
186
|
-
product = read_file('/sys/class/dmi/id/product_name')
|
187
|
-
"#{vendor} #{product}".strip
|
188
|
-
elsif RUBY_PLATFORM.include?('darwin')
|
189
|
-
model = `system_profiler SPHardwareDataType | awk '/Model Identifier/ { print $3 }'`.strip
|
190
|
-
model.empty? ? 'Mac' : model
|
191
|
-
else
|
192
|
-
'unknown'
|
193
|
-
end
|
194
|
-
rescue
|
195
|
-
'unknown'
|
196
|
-
end
|
197
|
-
|
198
|
-
def read_file(path)
|
199
|
-
File.read(path).strip if File.exist?(path)
|
200
|
-
end
|
201
|
-
end
|