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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8458c35bd163ce3c82f8c7e71e9d83450e1063bee51f6f497f3a6714c7269fc1
4
- data.tar.gz: 9ebae5b64a71e61b335f957ec61e3ceb27d766e5e4af1a634247845f082d5503
3
+ metadata.gz: be230126826d58c062ed0493f131ff0a7fdf247e6d76bbc437e95236e49c9a95
4
+ data.tar.gz: 89033092d183a2a1fb45232780900845001faca6c43390ffc5e48446ed70e823
5
5
  SHA512:
6
- metadata.gz: e85a136c8d024082707e7daf0109de9f026ec2916a370c12e8a3b58b0fe7725e09601550fa2bd2d6a366c5e4fa63843589010b87f9642164962e26e24d174c65
7
- data.tar.gz: 4b2e8b6b36c4baa54443068c9af875db9d93e57231bebcb48b0e419bd9fb68704c7da0dd9cd54901916cbf33f820f27f08e8ba82193056335e70495445539c01
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, it will be statically applied to all ingested logs. If left blank, a dynamic Resource Type will be assigned. |
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", "lib/fluent/plugin/environment_detector.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
 
@@ -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
- resource_type = @resource_type || @detector.infer_resource_type(record, tag)
184
- if resource_type.nil? || resource_type.strip.empty? || resource_type == 'Unknown'
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LmLogsFluentPlugin
4
- VERSION = '1.2.7'
4
+ VERSION = '1.2.8'
5
5
  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.7
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