fluent-plugin-jfrog-metrics 0.2.13 → 0.2.15
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/CHANGELOG.md +24 -0
- data/fluent-plugin-jfrog-metrics.gemspec +4 -7
- data/lib/fluent/plugin/datadog_metrics_parser.rb +14 -9
- data/lib/fluent/plugin/metrics_helper.rb +7 -0
- data/lib/fluent/plugin/newrelic_metrics_parser.rb +1 -1
- data/lib/fluent/plugin/proxy_helper.rb +88 -0
- data/lib/fluent/plugin/splunk_metrics_parser.rb +1 -1
- data/spec/fixtures/files/sample_metrics_with_nested_braces.txt +12 -0
- data/spec/lib/datadog_metrics_parser_spec.rb +107 -0
- data/spec/lib/newrelic_metrics_parser_spec.rb +40 -0
- data/spec/lib/splunk_metrics_parser_spec.rb +34 -0
- metadata +19 -16
- data/.gitignore +0 -3
- data/.rspec +0 -1
- data/Gemfile.lock +0 -81
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e129c6f7bc3ddd71533676e19f0684500cbceb9785db59e514d3e18a6d42a856
|
|
4
|
+
data.tar.gz: 1ae603b9e3ed77ed7d66ec8ba92eea76461b25b4b064f168d562b222c0d59234
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2338a45c27238e28b806916812acf7c33e64d7df4d1b8af67447e6b05ab0a7db5e58ada412dccd8ec4e95a27fe14037ec34f798cf0a4847ff3911980de2ef13a
|
|
7
|
+
data.tar.gz: f2ac0b3afca4d3301455ba5ae565e585f6eba3fcb930a34db5b488069fa83e3411c7bdb08892268ee632a4e9772889fcb4cce45956c6bd6ee4ea9f1da7b10c09
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.2.15] - 2026-03-24
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **JOBS-2011**: Fixed regex parsing issue that caused metrics shipping to fail when Artifactory emits metrics with nested curly braces in label values
|
|
9
|
+
- Changed regex from greedy `/(.*){(.*)}(.*)/i` to non-greedy `/(.*?){(.*)}(.*)/i` in all three parser files:
|
|
10
|
+
- `datadog_metrics_parser.rb`
|
|
11
|
+
- `splunk_metrics_parser.rb`
|
|
12
|
+
- `newrelic_metrics_parser.rb`
|
|
13
|
+
- This fix resolves the "no implicit conversion of nil into String" error when parsing metrics like:
|
|
14
|
+
```
|
|
15
|
+
db_query_large_total{action="/artifactory/api/nuget/{repoKey}/Download/{packageId}/{packageVersion}",application="Artifactory"} 1.0
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- Unit tests for metrics with nested curly braces to prevent regression
|
|
20
|
+
|
|
21
|
+
## [0.2.14] - 2026-01-19
|
|
22
|
+
|
|
23
|
+
### Previous Release
|
|
24
|
+
- See previous release notes for earlier changes
|
|
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |spec|
|
|
5
5
|
spec.name = 'fluent-plugin-jfrog-metrics'
|
|
6
|
-
spec.version = '0.2.
|
|
6
|
+
spec.version = '0.2.15'
|
|
7
7
|
spec.authors = ['MahithaB, BenHarosh']
|
|
8
8
|
spec.email = ['cpe-support@jfrog.com']
|
|
9
9
|
|
|
@@ -13,12 +13,9 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
spec.homepage = 'https://github.com/jfrog/jfrog-fluentd-plugins/tree/main/fluent-plugin-jfrog-metrics'
|
|
14
14
|
spec.license = 'Apache-2.0'
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
spec.files = files
|
|
20
|
-
spec.executables = files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
21
|
-
spec.test_files = test_files
|
|
16
|
+
spec.files = Dir['lib/**/*', 'README.md', 'LICENSE', 'CHANGELOG.md', 'Gemfile', 'Rakefile', '*.gemspec']
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = Dir['test/**/*', 'spec/**/*']
|
|
22
19
|
spec.require_paths = ['lib']
|
|
23
20
|
|
|
24
21
|
spec.add_development_dependency 'bundler', '~> 1.14'
|
|
@@ -19,7 +19,7 @@ class DatadogMetricsParser < BaseMetricsParser
|
|
|
19
19
|
point = {}
|
|
20
20
|
points = []
|
|
21
21
|
if interim_data =~ /{/ && interim_data =~ /}/
|
|
22
|
-
metric_name, additional_dims, metric_val_and_time = interim_data.match(/(
|
|
22
|
+
metric_name, additional_dims, metric_val_and_time = interim_data.match(/(.*?){(.*)}(.*)/i).captures
|
|
23
23
|
additional_dims.split("\",").each do |interim_data|
|
|
24
24
|
pair_data = interim_data.gsub("\"", "").gsub("{", "").gsub("}", "")
|
|
25
25
|
interim_data_value = pair_data.split("=", 2)[1]
|
|
@@ -27,15 +27,20 @@ class DatadogMetricsParser < BaseMetricsParser
|
|
|
27
27
|
interim_data_tag = interim_data_key + ':' + interim_data_value
|
|
28
28
|
tags << interim_data_tag
|
|
29
29
|
end
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
parts = metric_val_and_time.strip.split
|
|
31
|
+
metrics_hash['metric'] = prefix + separator + metric_name
|
|
32
|
+
if parts.length >= 2
|
|
33
|
+
# Metric WITH timestamp - use existing timestamp
|
|
34
|
+
metric_timestamp = parts[1]
|
|
35
|
+
point['timestamp'] = metric_timestamp[0,10].to_i
|
|
36
|
+
else
|
|
37
|
+
# Metric WITHOUT timestamp - use current time
|
|
38
|
+
point['timestamp'] = Time.now.to_i
|
|
38
39
|
end
|
|
40
|
+
point['value'] = parts[0].to_f
|
|
41
|
+
points << point
|
|
42
|
+
metrics_hash['points'] = points
|
|
43
|
+
metrics_hash['tags'] = tags
|
|
39
44
|
else
|
|
40
45
|
metrics_hash['metric'], metric_value, metric_timestamp = interim_data.split
|
|
41
46
|
metrics_hash['metric'] = prefix + separator + metrics_hash['metric']
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'rest-client'
|
|
4
|
+
require_relative 'proxy_helper'
|
|
4
5
|
|
|
5
6
|
class MetricsHelper
|
|
6
7
|
@@obs_endpoint_exists = false
|
|
@@ -54,6 +55,9 @@ class MetricsHelper
|
|
|
54
55
|
|
|
55
56
|
def check_endpoint(url, token, verify_ssl, request_timeout)
|
|
56
57
|
@logger.debug("Checking connectivity to endpoint: #{url} started")
|
|
58
|
+
# Configure proxy with NO_PROXY support
|
|
59
|
+
ProxyHelper.configure_rest_client_proxy(url, nil, @logger)
|
|
60
|
+
|
|
57
61
|
request = RestClient::Request.new(
|
|
58
62
|
method: :get,
|
|
59
63
|
url: url,
|
|
@@ -76,6 +80,9 @@ class MetricsHelper
|
|
|
76
80
|
|
|
77
81
|
def execute_rest_call(url, user, password, token, use_token, verify_ssl, request_timeout)
|
|
78
82
|
@logger.debug("Rest call to fetch metrics started")
|
|
83
|
+
# Configure proxy with NO_PROXY support
|
|
84
|
+
ProxyHelper.configure_rest_client_proxy(url, nil, @logger)
|
|
85
|
+
|
|
79
86
|
request = if use_token == true
|
|
80
87
|
@logger.debug("Using token for authentication")
|
|
81
88
|
RestClient::Request.new(
|
|
@@ -17,7 +17,7 @@ class NewRelicMetricsParser < BaseMetricsParser
|
|
|
17
17
|
metrics_hash = {}
|
|
18
18
|
if interim_data =~ /{/ && interim_data =~ /}/
|
|
19
19
|
attributes = {}
|
|
20
|
-
metric_name, additional_dims, metric_val_and_time = interim_data.match(/(
|
|
20
|
+
metric_name, additional_dims, metric_val_and_time = interim_data.match(/(.*?){(.*)}(.*)/i).captures
|
|
21
21
|
additional_dims.split("\",").each do |interim_data|
|
|
22
22
|
pair_data = interim_data.gsub("\"", "").gsub("{", "").gsub("}", "")
|
|
23
23
|
interim_data_value = pair_data.split("=", 2)[1]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
module ProxyHelper
|
|
6
|
+
# Check if a host should bypass the proxy based on NO_PROXY environment variable
|
|
7
|
+
def self.should_bypass_proxy?(url, logger = nil)
|
|
8
|
+
no_proxy = ENV['NO_PROXY'] || ENV['no_proxy']
|
|
9
|
+
return false if no_proxy.nil? || no_proxy.empty?
|
|
10
|
+
|
|
11
|
+
begin
|
|
12
|
+
target_host = URI.parse(url).host
|
|
13
|
+
return false if target_host.nil?
|
|
14
|
+
|
|
15
|
+
no_proxy_hosts = no_proxy.split(',').map(&:strip)
|
|
16
|
+
|
|
17
|
+
no_proxy_hosts.each do |pattern|
|
|
18
|
+
next if pattern.empty?
|
|
19
|
+
|
|
20
|
+
# Remove leading dot if present (e.g., ".example.com" -> "example.com")
|
|
21
|
+
# Downcase both for case-insensitive matching (domain names are case-insensitive per RFC 1035)
|
|
22
|
+
pattern = pattern.sub(/^\./, '').downcase
|
|
23
|
+
target_host_lower = target_host.downcase
|
|
24
|
+
|
|
25
|
+
# Check for exact match or subdomain match
|
|
26
|
+
if target_host_lower == pattern || target_host_lower.end_with?(".#{pattern}")
|
|
27
|
+
logger&.debug("Host '#{target_host}' matches NO_PROXY pattern '#{pattern}', bypassing proxy")
|
|
28
|
+
return true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Check for wildcard
|
|
32
|
+
if pattern == '*'
|
|
33
|
+
logger&.debug("NO_PROXY contains '*', bypassing proxy for all hosts")
|
|
34
|
+
return true
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
rescue URI::InvalidURIError => e
|
|
38
|
+
logger&.warn("Failed to parse URL '#{url}': #{e.message}")
|
|
39
|
+
return false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get the proxy URL to use for a given target URL
|
|
46
|
+
# Returns nil if proxy should be bypassed, otherwise returns the proxy URL
|
|
47
|
+
def self.get_proxy_for_url(url, http_proxy_param = nil, logger = nil)
|
|
48
|
+
# Check if this URL should bypass proxy
|
|
49
|
+
if should_bypass_proxy?(url, logger)
|
|
50
|
+
return nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Return proxy URL in order of precedence
|
|
54
|
+
if http_proxy_param && !http_proxy_param.empty?
|
|
55
|
+
logger&.debug("Using http_proxy param for request. Proxy url: #{http_proxy_param}")
|
|
56
|
+
return http_proxy_param
|
|
57
|
+
elsif ENV['HTTP_PROXY'] && !ENV['HTTP_PROXY'].empty?
|
|
58
|
+
logger&.debug("Using 'HTTP_PROXY' environment variable for request. Proxy url: #{ENV['HTTP_PROXY']}")
|
|
59
|
+
return ENV['HTTP_PROXY']
|
|
60
|
+
elsif ENV['http_proxy'] && !ENV['http_proxy'].empty?
|
|
61
|
+
logger&.debug("Using 'http_proxy' environment variable for request. Proxy url: #{ENV['http_proxy']}")
|
|
62
|
+
return ENV['http_proxy']
|
|
63
|
+
elsif ENV['HTTPS_PROXY'] && !ENV['HTTPS_PROXY'].empty?
|
|
64
|
+
logger&.debug("Using 'HTTPS_PROXY' environment variable for request. Proxy url: #{ENV['HTTPS_PROXY']}")
|
|
65
|
+
return ENV['HTTPS_PROXY']
|
|
66
|
+
elsif ENV['https_proxy'] && !ENV['https_proxy'].empty?
|
|
67
|
+
logger&.debug("Using 'https_proxy' environment variable for request. Proxy url: #{ENV['https_proxy']}")
|
|
68
|
+
return ENV['https_proxy']
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Configure RestClient proxy for a given URL
|
|
75
|
+
def self.configure_rest_client_proxy(url, http_proxy_param = nil, logger = nil)
|
|
76
|
+
proxy_url = get_proxy_for_url(url, http_proxy_param, logger)
|
|
77
|
+
|
|
78
|
+
if proxy_url.nil?
|
|
79
|
+
RestClient.proxy = nil
|
|
80
|
+
logger&.debug("Proxy disabled for URL: #{url}")
|
|
81
|
+
else
|
|
82
|
+
RestClient.proxy = proxy_url
|
|
83
|
+
logger&.debug("Proxy enabled for URL: #{url}, using: #{proxy_url}")
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
|
|
@@ -20,7 +20,7 @@ class SplunkMetricsParser < BaseMetricsParser
|
|
|
20
20
|
def generate_hash_from_data(data = '', prefix = '', separator = '')
|
|
21
21
|
metrics_hash = {}
|
|
22
22
|
if data =~ /{/ && data =~ /}/
|
|
23
|
-
metric_name, additional_dims, metric_val_and_time = data.match(/(
|
|
23
|
+
metric_name, additional_dims, metric_val_and_time = data.match(/(.*?){(.*)}(.*)/i).captures
|
|
24
24
|
if metric_val_and_time =~ / /
|
|
25
25
|
metrics_hash['metric_name'] = prefix + separator + metric_name
|
|
26
26
|
metrics_hash['value'] =
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# HELP db_query_large_total Total large queries
|
|
2
|
+
# UPDATED db_query_large_total 1711100784000
|
|
3
|
+
# TYPE db_query_large_total counter
|
|
4
|
+
db_query_large_total{action="/artifactory/api/nuget/{repoKey}/Download/{packageId}/{packageVersion}",application="Artifactory",group="download",package="nuget"} 2.0 1711100784000
|
|
5
|
+
# HELP db_query_large_total_alt Alternative large queries with different template
|
|
6
|
+
# UPDATED db_query_large_total_alt 1711100784000
|
|
7
|
+
# TYPE db_query_large_total_alt counter
|
|
8
|
+
db_query_large_total{action="/artifactory/api/artifactproperties{name:(/[^/]+?)?}",application="Artifactory",group="",package=""} 1.0 1711100784000
|
|
9
|
+
# HELP jfrt_http_connections_available_total Available HTTP Connections
|
|
10
|
+
# UPDATED jfrt_http_connections_available_total 1711100784000
|
|
11
|
+
# TYPE jfrt_http_connections_available_total gauge
|
|
12
|
+
jfrt_http_connections_available_total{instance="node1",port="8081"} 50 1711100784000
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
[
|
|
3
|
+
File.join(File.dirname(__FILE__), '..'),
|
|
4
|
+
File.join(File.dirname(__FILE__), '..', 'lib/fluent/plugin'),
|
|
5
|
+
File.join(File.dirname(__FILE__), '..', 'spec'),
|
|
6
|
+
].each do |dir|
|
|
7
|
+
$LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
require 'datadog_metrics_parser'
|
|
11
|
+
require 'date'
|
|
12
|
+
require 'rspec'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
RSpec.describe DatadogMetricsParser do
|
|
16
|
+
describe "#emit_parsed_metrics" do
|
|
17
|
+
it 'should read sample Artifactory metrics data and verify the size of parsed data > 1' do
|
|
18
|
+
platform_metrics = File.read('./spec/fixtures/files/sample_artifactory_metrics.txt')
|
|
19
|
+
expect(platform_metrics.size).to be > 1
|
|
20
|
+
|
|
21
|
+
parser = DatadogMetricsParser.new('jfrog.artifactory', '', 'jfrog.artifactory.metrics')
|
|
22
|
+
|
|
23
|
+
normalized_data = parser.normalise_data(platform_metrics)
|
|
24
|
+
expect(normalized_data.size).to be > 1
|
|
25
|
+
|
|
26
|
+
cleaned_data = parser.clean_data(normalized_data)
|
|
27
|
+
expect(cleaned_data.size).to be > 1
|
|
28
|
+
|
|
29
|
+
hash_data_array = parser.format_data(cleaned_data, 'jfrog.artifactory', '.')
|
|
30
|
+
expect(hash_data_array.size).to be 1
|
|
31
|
+
|
|
32
|
+
serialized_data = parser.serialize_data(hash_data_array)
|
|
33
|
+
expect(serialized_data.size).to be 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'should read sample Xray metrics data and verify the size of parsed data > 1' do
|
|
37
|
+
platform_metrics = File.read('./spec/fixtures/files/sample_xray_metrics.txt')
|
|
38
|
+
expect(platform_metrics.size).to be > 1
|
|
39
|
+
|
|
40
|
+
parser = DatadogMetricsParser.new('jfrog.xray', '', 'jfrog.xray.metrics')
|
|
41
|
+
|
|
42
|
+
normalized_data = parser.normalise_data(platform_metrics)
|
|
43
|
+
expect(normalized_data.size).to be > 1
|
|
44
|
+
|
|
45
|
+
cleaned_data = parser.clean_data(normalized_data)
|
|
46
|
+
expect(cleaned_data.size).to be > 1
|
|
47
|
+
|
|
48
|
+
hash_data_array = parser.format_data(cleaned_data, 'jfrog.xray', '.')
|
|
49
|
+
expect(hash_data_array.size).to be 1
|
|
50
|
+
|
|
51
|
+
serialized_data = parser.serialize_data(hash_data_array)
|
|
52
|
+
expect(serialized_data.size).to be 1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'should correctly parse metrics with nested curly braces in labels (JOBS-2011)' do
|
|
56
|
+
platform_metrics = File.read('./spec/fixtures/files/sample_metrics_with_nested_braces.txt')
|
|
57
|
+
expect(platform_metrics.size).to be > 1
|
|
58
|
+
|
|
59
|
+
parser = DatadogMetricsParser.new('jfrog.artifactory', '', 'jfrog.artifactory.metrics')
|
|
60
|
+
|
|
61
|
+
normalized_data = parser.normalise_data(platform_metrics)
|
|
62
|
+
expect(normalized_data.size).to be > 1
|
|
63
|
+
|
|
64
|
+
cleaned_data = parser.clean_data(normalized_data)
|
|
65
|
+
expect(cleaned_data.size).to be > 1
|
|
66
|
+
|
|
67
|
+
hash_data_array = parser.format_data(cleaned_data, 'jfrog.artifactory', '.')
|
|
68
|
+
expect(hash_data_array.size).to be 1
|
|
69
|
+
|
|
70
|
+
series_array = hash_data_array[0]['series']
|
|
71
|
+
expect(series_array.size).to be > 1
|
|
72
|
+
|
|
73
|
+
series_array.each do |metric|
|
|
74
|
+
expect(metric['metric']).to start_with('jfrog.artifactory.')
|
|
75
|
+
expect(metric['metric']).not_to include('{')
|
|
76
|
+
expect(metric['points']).not_to be_nil
|
|
77
|
+
expect(metric['points'].size).to be >= 1
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'should parse single metric with nested braces correctly' do
|
|
82
|
+
metric_line = 'db_query_large_total{action="/artifactory/api/nuget/{repoKey}/Download/{packageId}/{packageVersion}",application="Artifactory",group="download",package="nuget"} 2.0 1711100784000'
|
|
83
|
+
|
|
84
|
+
parser = DatadogMetricsParser.new('jfrog.artifactory', '', 'jfrog.artifactory.metrics')
|
|
85
|
+
result = parser.format_data([metric_line], 'jfrog.artifactory', '.')
|
|
86
|
+
|
|
87
|
+
expect(result.size).to eq(1)
|
|
88
|
+
series = result[0]['series']
|
|
89
|
+
expect(series.size).to eq(1)
|
|
90
|
+
expect(series[0]['metric']).to eq('jfrog.artifactory.db_query_large_total')
|
|
91
|
+
expect(series[0]['points'][0]['value']).to eq(2.0)
|
|
92
|
+
expect(series[0]['tags']).to include('action:/artifactory/api/nuget/{repoKey}/Download/{packageId}/{packageVersion}')
|
|
93
|
+
expect(series[0]['tags']).to include('application:Artifactory')
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'should not crash with nil values when parsing nested braces (regression test)' do
|
|
97
|
+
metric_with_complex_braces = 'db_query_large_total{action="/artifactory/api/artifactproperties{name:(/[^/]+?)?}",application="Artifactory",group="",package=""} 1.0 1711100784000'
|
|
98
|
+
|
|
99
|
+
parser = DatadogMetricsParser.new('jfrog.artifactory', '', 'jfrog.artifactory.metrics')
|
|
100
|
+
|
|
101
|
+
expect {
|
|
102
|
+
result = parser.format_data([metric_with_complex_braces], 'jfrog.artifactory', '.')
|
|
103
|
+
expect(result[0]['series'][0]['metric']).to eq('jfrog.artifactory.db_query_large_total')
|
|
104
|
+
}.not_to raise_error
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -51,5 +51,45 @@ RSpec.describe NewRelicMetricsParser do
|
|
|
51
51
|
serialized_data = parser.serialize_data(hash_data_array)
|
|
52
52
|
expect(serialized_data.size).to be 1
|
|
53
53
|
end
|
|
54
|
+
|
|
55
|
+
it 'should correctly parse metrics with nested curly braces in labels (JOBS-2011)' do
|
|
56
|
+
platform_metrics = File.read('./spec/fixtures/files/sample_metrics_with_nested_braces.txt')
|
|
57
|
+
expect(platform_metrics.size).to be > 1
|
|
58
|
+
|
|
59
|
+
parser = NewRelicMetricsParser.new('jfrog.artifactory', '', 'jfrog.artifactory.metrics')
|
|
60
|
+
|
|
61
|
+
normalized_data = parser.normalise_data(platform_metrics)
|
|
62
|
+
expect(normalized_data.size).to be > 1
|
|
63
|
+
|
|
64
|
+
cleaned_data = parser.clean_data(normalized_data)
|
|
65
|
+
expect(cleaned_data.size).to be > 1
|
|
66
|
+
|
|
67
|
+
hash_data_array = parser.format_data(cleaned_data, 'jfrog.artifactory', '.')
|
|
68
|
+
expect(hash_data_array.size).to be 1
|
|
69
|
+
|
|
70
|
+
metrics_array = hash_data_array[0]['metrics']
|
|
71
|
+
expect(metrics_array.size).to be > 1
|
|
72
|
+
|
|
73
|
+
metrics_array.each do |metric|
|
|
74
|
+
expect(metric['name']).to start_with('jfrog.artifactory.')
|
|
75
|
+
expect(metric['name']).not_to include('{')
|
|
76
|
+
expect(metric['value']).not_to be_nil
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'should parse single metric with nested braces correctly' do
|
|
81
|
+
metric_line = 'db_query_large_total{action="/artifactory/api/nuget/{repoKey}/Download/{packageId}/{packageVersion}",application="Artifactory",group="download",package="nuget"} 2.0 1711100784000'
|
|
82
|
+
|
|
83
|
+
parser = NewRelicMetricsParser.new('jfrog.artifactory', '', 'jfrog.artifactory.metrics')
|
|
84
|
+
result = parser.format_data([metric_line], 'jfrog.artifactory', '.')
|
|
85
|
+
|
|
86
|
+
expect(result.size).to eq(1)
|
|
87
|
+
metrics = result[0]['metrics']
|
|
88
|
+
expect(metrics.size).to eq(1)
|
|
89
|
+
expect(metrics[0]['name']).to eq('jfrog.artifactory.db_query_large_total')
|
|
90
|
+
expect(metrics[0]['value']).to eq(2.0)
|
|
91
|
+
expect(metrics[0]['attributes']['action']).to eq('/artifactory/api/nuget/{repoKey}/Download/{packageId}/{packageVersion}')
|
|
92
|
+
expect(metrics[0]['attributes']['application']).to eq('Artifactory')
|
|
93
|
+
end
|
|
54
94
|
end
|
|
55
95
|
end
|
|
@@ -51,5 +51,39 @@ RSpec.describe SplunkMetricsParser do
|
|
|
51
51
|
serialized_data = parser.serialize_data(hash_data_array)
|
|
52
52
|
expect(serialized_data.size).to be > 1
|
|
53
53
|
end
|
|
54
|
+
|
|
55
|
+
it 'should correctly parse metrics with nested curly braces in labels (JOBS-2011)' do
|
|
56
|
+
platform_metrics = File.read('./spec/fixtures/files/sample_metrics_with_nested_braces.txt')
|
|
57
|
+
expect(platform_metrics.size).to be > 1
|
|
58
|
+
|
|
59
|
+
parser = SplunkMetricsParser.new('jfrog.artifactory', '', 'jfrog.artifactory.metrics')
|
|
60
|
+
|
|
61
|
+
normalized_data = parser.normalise_data(platform_metrics)
|
|
62
|
+
expect(normalized_data.size).to be > 1
|
|
63
|
+
|
|
64
|
+
cleaned_data = parser.clean_data(normalized_data)
|
|
65
|
+
expect(cleaned_data.size).to be > 1
|
|
66
|
+
|
|
67
|
+
hash_data_array = parser.format_data(cleaned_data, 'jfrog.artifactory', '.')
|
|
68
|
+
expect(hash_data_array.size).to be > 1
|
|
69
|
+
|
|
70
|
+
hash_data_array.each do |metric_hash|
|
|
71
|
+
expect(metric_hash['metric_name']).to start_with('jfrog.artifactory.')
|
|
72
|
+
expect(metric_hash['metric_name']).not_to include('{')
|
|
73
|
+
expect(metric_hash['value']).not_to be_nil
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'should parse single metric with nested braces correctly' do
|
|
78
|
+
metric_line = 'db_query_large_total{action="/artifactory/api/nuget/{repoKey}/Download/{packageId}/{packageVersion}",application="Artifactory",group="download",package="nuget"} 2.0 1711100784000'
|
|
79
|
+
|
|
80
|
+
parser = SplunkMetricsParser.new('jfrog.artifactory', '', 'jfrog.artifactory.metrics')
|
|
81
|
+
result = parser.generate_hash_from_data(metric_line, 'jfrog.artifactory', '.')
|
|
82
|
+
|
|
83
|
+
expect(result['metric_name']).to eq('jfrog.artifactory.db_query_large_total')
|
|
84
|
+
expect(result['value']).to eq(2.0)
|
|
85
|
+
expect(result['action']).to eq('/artifactory/api/nuget/{repoKey}/Download/{packageId}/{packageVersion}')
|
|
86
|
+
expect(result['application']).to eq('Artifactory')
|
|
87
|
+
end
|
|
54
88
|
end
|
|
55
89
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fluent-plugin-jfrog-metrics
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- MahithaB, BenHarosh
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-03-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -109,10 +109,8 @@ executables: []
|
|
|
109
109
|
extensions: []
|
|
110
110
|
extra_rdoc_files: []
|
|
111
111
|
files:
|
|
112
|
-
-
|
|
113
|
-
- ".rspec"
|
|
112
|
+
- CHANGELOG.md
|
|
114
113
|
- Gemfile
|
|
115
|
-
- Gemfile.lock
|
|
116
114
|
- LICENSE
|
|
117
115
|
- README.md
|
|
118
116
|
- Rakefile
|
|
@@ -122,10 +120,13 @@ files:
|
|
|
122
120
|
- lib/fluent/plugin/in_jfrog_metrics.rb
|
|
123
121
|
- lib/fluent/plugin/metrics_helper.rb
|
|
124
122
|
- lib/fluent/plugin/newrelic_metrics_parser.rb
|
|
123
|
+
- lib/fluent/plugin/proxy_helper.rb
|
|
125
124
|
- lib/fluent/plugin/splunk_metrics_parser.rb
|
|
126
125
|
- spec/fixtures/files/creds.rb
|
|
127
126
|
- spec/fixtures/files/sample_artifactory_metrics.txt
|
|
127
|
+
- spec/fixtures/files/sample_metrics_with_nested_braces.txt
|
|
128
128
|
- spec/fixtures/files/sample_xray_metrics.txt
|
|
129
|
+
- spec/lib/datadog_metrics_parser_spec.rb
|
|
129
130
|
- spec/lib/metrics_helper_spec.rb
|
|
130
131
|
- spec/lib/newrelic_metrics_parser_spec.rb
|
|
131
132
|
- spec/lib/splunk_metrics_parser_spec.rb
|
|
@@ -136,7 +137,7 @@ homepage: https://github.com/jfrog/jfrog-fluentd-plugins/tree/main/fluent-plugin
|
|
|
136
137
|
licenses:
|
|
137
138
|
- Apache-2.0
|
|
138
139
|
metadata: {}
|
|
139
|
-
post_install_message:
|
|
140
|
+
post_install_message:
|
|
140
141
|
rdoc_options: []
|
|
141
142
|
require_paths:
|
|
142
143
|
- lib
|
|
@@ -151,19 +152,21 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
151
152
|
- !ruby/object:Gem::Version
|
|
152
153
|
version: '0'
|
|
153
154
|
requirements: []
|
|
154
|
-
rubygems_version: 3.
|
|
155
|
-
signing_key:
|
|
155
|
+
rubygems_version: 3.0.3.1
|
|
156
|
+
signing_key:
|
|
156
157
|
specification_version: 4
|
|
157
158
|
summary: Fluentd Plugin for converting JFrog Artifactory, Xray generated metrics (Prometheus
|
|
158
159
|
Exposition Format) to target observability platform format (Splunk HEC, New Relic,
|
|
159
160
|
DataDog)
|
|
160
161
|
test_files:
|
|
161
|
-
- spec/fixtures/files/creds.rb
|
|
162
|
-
- spec/fixtures/files/sample_artifactory_metrics.txt
|
|
163
|
-
- spec/fixtures/files/sample_xray_metrics.txt
|
|
164
|
-
- spec/lib/metrics_helper_spec.rb
|
|
165
|
-
- spec/lib/newrelic_metrics_parser_spec.rb
|
|
166
|
-
- spec/lib/splunk_metrics_parser_spec.rb
|
|
167
|
-
- spec/spec_helper.rb
|
|
168
162
|
- test/helper.rb
|
|
169
163
|
- test/plugin/test_in_jfrog_metrics.rb
|
|
164
|
+
- spec/spec_helper.rb
|
|
165
|
+
- spec/lib/datadog_metrics_parser_spec.rb
|
|
166
|
+
- spec/lib/splunk_metrics_parser_spec.rb
|
|
167
|
+
- spec/lib/metrics_helper_spec.rb
|
|
168
|
+
- spec/lib/newrelic_metrics_parser_spec.rb
|
|
169
|
+
- spec/fixtures/files/sample_xray_metrics.txt
|
|
170
|
+
- spec/fixtures/files/sample_metrics_with_nested_braces.txt
|
|
171
|
+
- spec/fixtures/files/creds.rb
|
|
172
|
+
- spec/fixtures/files/sample_artifactory_metrics.txt
|
data/.gitignore
DELETED
data/.rspec
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
--require spec_helper
|
data/Gemfile.lock
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
PATH
|
|
2
|
-
remote: .
|
|
3
|
-
specs:
|
|
4
|
-
fluent-plugin-jfrog-metrics (0.2.13)
|
|
5
|
-
fluentd (>= 0.14.10, < 2)
|
|
6
|
-
rest-client (~> 2.1.0)
|
|
7
|
-
|
|
8
|
-
GEM
|
|
9
|
-
remote: https://rubygems.org/
|
|
10
|
-
specs:
|
|
11
|
-
ansi (1.5.0)
|
|
12
|
-
builder (3.2.4)
|
|
13
|
-
concurrent-ruby (1.2.2)
|
|
14
|
-
cool.io (1.7.1)
|
|
15
|
-
domain_name (0.5.20190701)
|
|
16
|
-
unf (>= 0.0.5, < 1.0.0)
|
|
17
|
-
fluentd (1.16.1)
|
|
18
|
-
bundler
|
|
19
|
-
cool.io (>= 1.4.5, < 2.0.0)
|
|
20
|
-
http_parser.rb (>= 0.5.1, < 0.9.0)
|
|
21
|
-
msgpack (>= 1.3.1, < 2.0.0)
|
|
22
|
-
serverengine (>= 2.3.2, < 3.0.0)
|
|
23
|
-
sigdump (~> 0.2.2)
|
|
24
|
-
strptime (>= 0.2.4, < 1.0.0)
|
|
25
|
-
tzinfo (>= 1.0, < 3.0)
|
|
26
|
-
tzinfo-data (~> 1.0)
|
|
27
|
-
webrick (~> 1.4)
|
|
28
|
-
yajl-ruby (~> 1.0)
|
|
29
|
-
http-accept (1.7.0)
|
|
30
|
-
http-cookie (1.0.4)
|
|
31
|
-
domain_name (~> 0.5)
|
|
32
|
-
http_parser.rb (0.8.0)
|
|
33
|
-
mime-types (3.4.1)
|
|
34
|
-
mime-types-data (~> 3.2015)
|
|
35
|
-
mime-types-data (3.2021.1115)
|
|
36
|
-
minitest (5.14.4)
|
|
37
|
-
minitest-reporters (1.4.3)
|
|
38
|
-
ansi
|
|
39
|
-
builder
|
|
40
|
-
minitest (>= 5.0)
|
|
41
|
-
ruby-progressbar
|
|
42
|
-
msgpack (1.7.1)
|
|
43
|
-
netrc (0.11.0)
|
|
44
|
-
power_assert (2.0.1)
|
|
45
|
-
rake (12.3.3)
|
|
46
|
-
rest-client (2.1.0)
|
|
47
|
-
http-accept (>= 1.7.0, < 2.0)
|
|
48
|
-
http-cookie (>= 1.0.2, < 2.0)
|
|
49
|
-
mime-types (>= 1.16, < 4.0)
|
|
50
|
-
netrc (~> 0.8)
|
|
51
|
-
ruby-progressbar (1.11.0)
|
|
52
|
-
serverengine (2.3.2)
|
|
53
|
-
sigdump (~> 0.2.2)
|
|
54
|
-
sigdump (0.2.4)
|
|
55
|
-
strptime (0.2.5)
|
|
56
|
-
test-unit (3.5.0)
|
|
57
|
-
power_assert
|
|
58
|
-
tzinfo (2.0.6)
|
|
59
|
-
concurrent-ruby (~> 1.0)
|
|
60
|
-
tzinfo-data (1.2023.3)
|
|
61
|
-
tzinfo (>= 1.0.0)
|
|
62
|
-
unf (0.1.4)
|
|
63
|
-
unf_ext
|
|
64
|
-
unf_ext (0.0.8)
|
|
65
|
-
webrick (1.8.1)
|
|
66
|
-
yajl-ruby (1.4.3)
|
|
67
|
-
|
|
68
|
-
PLATFORMS
|
|
69
|
-
ruby
|
|
70
|
-
|
|
71
|
-
DEPENDENCIES
|
|
72
|
-
bundler (~> 1.14)
|
|
73
|
-
fluent-plugin-jfrog-metrics!
|
|
74
|
-
minitest
|
|
75
|
-
minitest-reporters (>= 0.5.0)
|
|
76
|
-
rake (~> 12.0)
|
|
77
|
-
rest-client (>= 2.1.0)
|
|
78
|
-
test-unit (~> 3.0)
|
|
79
|
-
|
|
80
|
-
BUNDLED WITH
|
|
81
|
-
1.17.3
|