sumologic-query 1.4.0 → 1.4.2
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 +36 -0
- data/README.md +3 -3
- data/lib/sumologic/cli/commands/base_command.rb +12 -0
- data/lib/sumologic/cli/commands/discover_source_metadata_command.rb +4 -1
- data/lib/sumologic/cli/commands/export_content_command.rb +3 -5
- data/lib/sumologic/cli/commands/get_content_command.rb +3 -5
- data/lib/sumologic/cli/commands/get_dashboard_command.rb +3 -5
- data/lib/sumologic/cli/commands/get_lookup_command.rb +3 -5
- data/lib/sumologic/cli/commands/get_monitor_command.rb +3 -5
- data/lib/sumologic/cli/commands/list_apps_command.rb +1 -7
- data/lib/sumologic/cli/commands/list_collectors_command.rb +6 -7
- data/lib/sumologic/cli/commands/list_dashboards_command.rb +3 -7
- data/lib/sumologic/cli/commands/list_health_events_command.rb +3 -7
- data/lib/sumologic/cli/commands/list_monitors_command.rb +7 -11
- data/lib/sumologic/cli/commands/list_sources_command.rb +7 -3
- data/lib/sumologic/cli/commands/search_command.rb +14 -0
- data/lib/sumologic/cli.rb +23 -13
- data/lib/sumologic/client.rb +15 -9
- data/lib/sumologic/configuration.rb +21 -0
- data/lib/sumologic/metadata/collector.rb +21 -3
- data/lib/sumologic/metadata/source.rb +44 -4
- data/lib/sumologic/metadata/source_metadata_discovery.rb +17 -2
- data/lib/sumologic/utils/time_parser.rb +12 -12
- data/lib/sumologic/version.rb +1 -1
- metadata +1 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 17737642aa1c9244133b4442fd6dccc156cf39b5a35cc05071a4769800ad0501
|
|
4
|
+
data.tar.gz: 29037e867772bbc70e2cda259879e8b6d55e51b70611b7ef5167c79ab2eee00e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 67bd5ba57a5a3ed5739d274a25c29e9ba4c1cd2415976a5bb7cbdfb075276c67b614917a14a312e33fa618c15a665ccb7bf82e34b20602ab800e3c00e65bed8c
|
|
7
|
+
data.tar.gz: 2c3fff993dcf6d5a2d14b5719a3fd23331477dece5a43cad0dc5d42e4ae7065fb94bda7741ead8e9a678839d8b5d063bb237c9c301585008fdba427737e1abd8
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,42 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
6
6
|
|
|
7
7
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
8
8
|
and release notes are automatically generated from commit messages.
|
|
9
|
+
## [1.4.2](https://github.com/patrick204nqh/sumologic-query/compare/v1.4.1...v1.4.2) (2026-02-11)
|
|
10
|
+
|
|
11
|
+
### 🎉 New Features
|
|
12
|
+
|
|
13
|
+
- add source discovery guidance to skills
|
|
14
|
+
- add filtering options to list-sources
|
|
15
|
+
- add keyword and limit filters to discover-source-metadata
|
|
16
|
+
- add -q/--query and -l/--limit flags to list-collectors
|
|
17
|
+
|
|
18
|
+
### 🐛 Bug Fixes
|
|
19
|
+
|
|
20
|
+
- support compound relative time expressions like -1h30m
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## [1.4.1](https://github.com/patrick204nqh/sumologic-query/compare/v1.4.0...v1.4.1) (2026-02-11)
|
|
25
|
+
|
|
26
|
+
### 🎉 New Features
|
|
27
|
+
|
|
28
|
+
- add "Open in Sumo Logic" URL to search output
|
|
29
|
+
- add command recipe and CLI command specs
|
|
30
|
+
|
|
31
|
+
### 🐛 Bug Fixes
|
|
32
|
+
|
|
33
|
+
- update source_code_uri metadata to use homepage
|
|
34
|
+
|
|
35
|
+
### 🔧 Refactoring
|
|
36
|
+
|
|
37
|
+
- resolve ADR-006, fix README links, extract command helpers
|
|
38
|
+
|
|
39
|
+
### 📚 Documentation
|
|
40
|
+
|
|
41
|
+
- consolidate query examples and document v1/v2 API split
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
9
45
|
## [1.4.0](https://github.com/patrick204nqh/sumologic-query/compare/v1.3.5...v1.4.0) (2026-02-11)
|
|
10
46
|
|
|
11
47
|
### 🎉 New Features
|
data/README.md
CHANGED
|
@@ -152,9 +152,9 @@ client.list_all_sources
|
|
|
152
152
|
## Documentation
|
|
153
153
|
|
|
154
154
|
- [Query Examples](examples/queries.md) - Query patterns and examples
|
|
155
|
-
- [Quick Reference](docs/tldr.md) - Command cheat sheet
|
|
156
|
-
- [Rate Limiting](docs/rate-limiting.md) - Performance tuning
|
|
157
|
-
- [Architecture](docs/
|
|
155
|
+
- [Quick Reference](docs/sdlc/7-maintain/tldr.md) - Command cheat sheet
|
|
156
|
+
- [Rate Limiting](docs/sdlc/4-develop/rate-limiting.md) - Performance tuning
|
|
157
|
+
- [Architecture](docs/sdlc/3-design/overview.md) - Design decisions
|
|
158
158
|
|
|
159
159
|
## Contributing
|
|
160
160
|
|
|
@@ -17,6 +17,18 @@ module Sumologic
|
|
|
17
17
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
|
+
def list_resource(label:, key:)
|
|
21
|
+
warn "Fetching #{label}..."
|
|
22
|
+
items = yield
|
|
23
|
+
output_json(total: items.size, key => items)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def get_resource(label:, id:)
|
|
27
|
+
warn "Fetching #{label} #{id}..."
|
|
28
|
+
result = yield
|
|
29
|
+
output_json(result)
|
|
30
|
+
end
|
|
31
|
+
|
|
20
32
|
def output_json(data)
|
|
21
33
|
json_output = JSON.pretty_generate(data)
|
|
22
34
|
|
|
@@ -40,6 +40,7 @@ module Sumologic
|
|
|
40
40
|
end
|
|
41
41
|
warn "Time Zone: #{@parsed_timezone}"
|
|
42
42
|
warn "Filter: #{options[:filter] || 'none (all sources)'}"
|
|
43
|
+
warn "Keyword: #{options[:keyword]}" if options[:keyword]
|
|
43
44
|
warn '-' * 60
|
|
44
45
|
warn 'Running aggregation query to discover sources...'
|
|
45
46
|
$stderr.puts
|
|
@@ -50,7 +51,9 @@ module Sumologic
|
|
|
50
51
|
from_time: @parsed_from,
|
|
51
52
|
to_time: @parsed_to,
|
|
52
53
|
time_zone: @parsed_timezone,
|
|
53
|
-
filter: options[:filter]
|
|
54
|
+
filter: options[:filter],
|
|
55
|
+
keyword: options[:keyword],
|
|
56
|
+
limit: options[:limit]
|
|
54
57
|
)
|
|
55
58
|
end
|
|
56
59
|
end
|
|
@@ -8,11 +8,9 @@ module Sumologic
|
|
|
8
8
|
# Handles the export-content command execution
|
|
9
9
|
class ExportContentCommand < BaseCommand
|
|
10
10
|
def execute
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
output_json(result)
|
|
11
|
+
get_resource(label: 'content', id: options[:content_id]) do
|
|
12
|
+
client.export_content(content_id: options[:content_id])
|
|
13
|
+
end
|
|
16
14
|
end
|
|
17
15
|
end
|
|
18
16
|
end
|
|
@@ -8,11 +8,9 @@ module Sumologic
|
|
|
8
8
|
# Handles the get-content command execution
|
|
9
9
|
class GetContentCommand < BaseCommand
|
|
10
10
|
def execute
|
|
11
|
-
path
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
output_json(content)
|
|
11
|
+
get_resource(label: 'content at path:', id: options[:path]) do
|
|
12
|
+
client.get_content(path: options[:path])
|
|
13
|
+
end
|
|
16
14
|
end
|
|
17
15
|
end
|
|
18
16
|
end
|
|
@@ -8,11 +8,9 @@ module Sumologic
|
|
|
8
8
|
# Handles the get-dashboard command execution
|
|
9
9
|
class GetDashboardCommand < BaseCommand
|
|
10
10
|
def execute
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
output_json(dashboard)
|
|
11
|
+
get_resource(label: 'dashboard', id: options[:dashboard_id]) do
|
|
12
|
+
client.get_dashboard(dashboard_id: options[:dashboard_id])
|
|
13
|
+
end
|
|
16
14
|
end
|
|
17
15
|
end
|
|
18
16
|
end
|
|
@@ -8,11 +8,9 @@ module Sumologic
|
|
|
8
8
|
# Handles the get-lookup command execution
|
|
9
9
|
class GetLookupCommand < BaseCommand
|
|
10
10
|
def execute
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
output_json(lookup)
|
|
11
|
+
get_resource(label: 'lookup table', id: options[:lookup_id]) do
|
|
12
|
+
client.get_lookup(lookup_id: options[:lookup_id])
|
|
13
|
+
end
|
|
16
14
|
end
|
|
17
15
|
end
|
|
18
16
|
end
|
|
@@ -8,11 +8,9 @@ module Sumologic
|
|
|
8
8
|
# Handles the get-monitor command execution
|
|
9
9
|
class GetMonitorCommand < BaseCommand
|
|
10
10
|
def execute
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
output_json(monitor)
|
|
11
|
+
get_resource(label: 'monitor', id: options[:monitor_id]) do
|
|
12
|
+
client.get_monitor(monitor_id: options[:monitor_id])
|
|
13
|
+
end
|
|
16
14
|
end
|
|
17
15
|
end
|
|
18
16
|
end
|
|
@@ -8,13 +8,7 @@ module Sumologic
|
|
|
8
8
|
# Handles the list-apps command execution
|
|
9
9
|
class ListAppsCommand < BaseCommand
|
|
10
10
|
def execute
|
|
11
|
-
|
|
12
|
-
apps = client.list_apps
|
|
13
|
-
|
|
14
|
-
output_json(
|
|
15
|
-
total: apps.size,
|
|
16
|
-
apps: apps
|
|
17
|
-
)
|
|
11
|
+
list_resource(label: 'app catalog', key: :apps) { client.list_apps }
|
|
18
12
|
end
|
|
19
13
|
end
|
|
20
14
|
end
|
|
@@ -8,13 +8,12 @@ module Sumologic
|
|
|
8
8
|
# Handles the list-collectors command execution
|
|
9
9
|
class ListCollectorsCommand < BaseCommand
|
|
10
10
|
def execute
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
11
|
+
list_resource(label: 'collectors', key: :collectors) do
|
|
12
|
+
client.list_collectors(
|
|
13
|
+
query: options[:query],
|
|
14
|
+
limit: options[:limit]
|
|
15
|
+
)
|
|
16
|
+
end
|
|
18
17
|
end
|
|
19
18
|
end
|
|
20
19
|
end
|
|
@@ -8,13 +8,9 @@ module Sumologic
|
|
|
8
8
|
# Handles the list-dashboards command execution
|
|
9
9
|
class ListDashboardsCommand < BaseCommand
|
|
10
10
|
def execute
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
output_json(
|
|
15
|
-
total: dashboards.size,
|
|
16
|
-
dashboards: dashboards
|
|
17
|
-
)
|
|
11
|
+
list_resource(label: 'dashboards', key: :dashboards) do
|
|
12
|
+
client.list_dashboards(limit: options[:limit] || 100)
|
|
13
|
+
end
|
|
18
14
|
end
|
|
19
15
|
end
|
|
20
16
|
end
|
|
@@ -8,13 +8,9 @@ module Sumologic
|
|
|
8
8
|
# Handles the list-health-events command execution
|
|
9
9
|
class ListHealthEventsCommand < BaseCommand
|
|
10
10
|
def execute
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
output_json(
|
|
15
|
-
total: events.size,
|
|
16
|
-
healthEvents: events
|
|
17
|
-
)
|
|
11
|
+
list_resource(label: 'health events', key: :healthEvents) do
|
|
12
|
+
client.list_health_events(limit: options[:limit] || 100)
|
|
13
|
+
end
|
|
18
14
|
end
|
|
19
15
|
end
|
|
20
16
|
end
|
|
@@ -9,17 +9,13 @@ module Sumologic
|
|
|
9
9
|
# Uses the monitors search API for flat, filterable results
|
|
10
10
|
class ListMonitorsCommand < BaseCommand
|
|
11
11
|
def execute
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
output_json(
|
|
20
|
-
total: monitors.size,
|
|
21
|
-
monitors: monitors
|
|
22
|
-
)
|
|
12
|
+
list_resource(label: 'monitors', key: :monitors) do
|
|
13
|
+
client.list_monitors(
|
|
14
|
+
query: options[:query],
|
|
15
|
+
status: options[:status],
|
|
16
|
+
limit: options[:limit] || 100
|
|
17
|
+
)
|
|
18
|
+
end
|
|
23
19
|
end
|
|
24
20
|
end
|
|
25
21
|
end
|
|
@@ -29,10 +29,14 @@ module Sumologic
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def list_all_sources
|
|
32
|
-
warn 'Fetching
|
|
33
|
-
warn 'This may take a minute...'
|
|
32
|
+
warn 'Fetching sources from collectors...'
|
|
34
33
|
|
|
35
|
-
all_sources = client.list_all_sources
|
|
34
|
+
all_sources = client.list_all_sources(
|
|
35
|
+
collector: options[:collector],
|
|
36
|
+
name: options[:name],
|
|
37
|
+
category: options[:category],
|
|
38
|
+
limit: options[:limit]
|
|
39
|
+
)
|
|
36
40
|
|
|
37
41
|
output_json(
|
|
38
42
|
total_collectors: all_sources.size,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'erb'
|
|
3
4
|
require_relative 'base_command'
|
|
4
5
|
require_relative '../../utils/time_parser'
|
|
5
6
|
|
|
@@ -46,6 +47,7 @@ module Sumologic
|
|
|
46
47
|
end
|
|
47
48
|
warn "Query: #{options[:query]}"
|
|
48
49
|
warn "Limit: #{options[:limit] || 'unlimited'}"
|
|
50
|
+
warn "Open in Sumo: #{build_search_url}"
|
|
49
51
|
warn '-' * 60
|
|
50
52
|
warn 'Creating search job...'
|
|
51
53
|
$stderr.puts
|
|
@@ -96,6 +98,7 @@ module Sumologic
|
|
|
96
98
|
from_original: @original_from,
|
|
97
99
|
to_original: @original_to,
|
|
98
100
|
time_zone: @parsed_timezone,
|
|
101
|
+
search_url: build_search_url,
|
|
99
102
|
record_count: results.size,
|
|
100
103
|
records: results
|
|
101
104
|
)
|
|
@@ -107,6 +110,7 @@ module Sumologic
|
|
|
107
110
|
from_original: @original_from,
|
|
108
111
|
to_original: @original_to,
|
|
109
112
|
time_zone: @parsed_timezone,
|
|
113
|
+
search_url: build_search_url,
|
|
110
114
|
message_count: results.size,
|
|
111
115
|
messages: results
|
|
112
116
|
)
|
|
@@ -135,10 +139,20 @@ module Sumologic
|
|
|
135
139
|
'from' => @parsed_from,
|
|
136
140
|
'to' => @parsed_to,
|
|
137
141
|
'time_zone' => @parsed_timezone,
|
|
142
|
+
'search_url' => build_search_url,
|
|
138
143
|
'message_count' => results.size,
|
|
139
144
|
'messages' => results
|
|
140
145
|
}
|
|
141
146
|
end
|
|
147
|
+
|
|
148
|
+
def build_search_url
|
|
149
|
+
from_ms = (Time.parse("#{@parsed_from}Z").to_f * 1000).to_i
|
|
150
|
+
to_ms = (Time.parse("#{@parsed_to}Z").to_f * 1000).to_i
|
|
151
|
+
encoded_query = ERB::Util.url_encode(options[:query])
|
|
152
|
+
base = client.config.web_ui_base_url
|
|
153
|
+
|
|
154
|
+
"#{base}/ui/#/search/create?query=#{encoded_query}&startTime=#{from_ms}&endTime=#{to_ms}"
|
|
155
|
+
end
|
|
142
156
|
end
|
|
143
157
|
end
|
|
144
158
|
end
|
data/lib/sumologic/cli.rb
CHANGED
|
@@ -41,7 +41,7 @@ module Sumologic
|
|
|
41
41
|
Time Formats:
|
|
42
42
|
--from and --to support multiple formats:
|
|
43
43
|
• 'now' - current time
|
|
44
|
-
• Relative: '-30s', '-5m', '-2h', '-7d', '-1w', '-1M' (
|
|
44
|
+
• Relative: '-30s', '-5m', '-2h', '-1h30m', '-7d', '-1w', '-1M' (compound supported)
|
|
45
45
|
• Unix timestamp: '1700000000' (seconds since epoch)
|
|
46
46
|
• ISO 8601: '2025-11-13T14:00:00'
|
|
47
47
|
|
|
@@ -89,29 +89,36 @@ module Sumologic
|
|
|
89
89
|
Commands::SearchCommand.new(options, create_client).execute
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
desc 'list-collectors', 'List
|
|
92
|
+
desc 'list-collectors', 'List Sumo Logic collectors with optional filters'
|
|
93
93
|
long_desc <<~DESC
|
|
94
|
-
List
|
|
94
|
+
List collectors in your Sumo Logic account.
|
|
95
|
+
Supports filtering by name/category and limiting results.
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
Examples:
|
|
98
|
+
sumo-query list-collectors -q "my-service" -l 20
|
|
97
99
|
sumo-query list-collectors --output collectors.json
|
|
98
100
|
DESC
|
|
101
|
+
option :query, type: :string, aliases: '-q', desc: 'Filter by name or category (case-insensitive)'
|
|
102
|
+
option :limit, type: :numeric, aliases: '-l', desc: 'Maximum collectors to return'
|
|
99
103
|
def list_collectors
|
|
100
104
|
Commands::ListCollectorsCommand.new(options, create_client).execute
|
|
101
105
|
end
|
|
102
106
|
|
|
103
|
-
desc 'list-sources', 'List sources from collectors'
|
|
107
|
+
desc 'list-sources', 'List sources from collectors with optional filters'
|
|
104
108
|
long_desc <<~DESC
|
|
105
|
-
List
|
|
109
|
+
List sources from all collectors, or from a specific collector.
|
|
110
|
+
Supports filtering by collector name, source name, and category.
|
|
106
111
|
|
|
107
112
|
Examples:
|
|
108
|
-
|
|
109
|
-
sumo-query list-sources
|
|
110
|
-
|
|
111
|
-
# List sources for specific collector
|
|
113
|
+
sumo-query list-sources --collector "my-service" --name "nginx" -l 20
|
|
114
|
+
sumo-query list-sources --category "production"
|
|
112
115
|
sumo-query list-sources --collector-id 12345
|
|
113
116
|
DESC
|
|
114
117
|
option :collector_id, type: :string, desc: 'Collector ID to list sources for'
|
|
118
|
+
option :collector, type: :string, desc: 'Filter by collector name (case-insensitive)'
|
|
119
|
+
option :name, type: :string, aliases: '-n', desc: 'Filter by source name (case-insensitive)'
|
|
120
|
+
option :category, type: :string, desc: 'Filter by source category (case-insensitive)'
|
|
121
|
+
option :limit, type: :numeric, aliases: '-l', desc: 'Maximum total sources to return'
|
|
115
122
|
def list_sources
|
|
116
123
|
Commands::ListSourcesCommand.new(options, create_client).execute
|
|
117
124
|
end
|
|
@@ -131,7 +138,7 @@ module Sumologic
|
|
|
131
138
|
Time Formats:
|
|
132
139
|
--from and --to support multiple formats:
|
|
133
140
|
• 'now' - current time
|
|
134
|
-
• Relative: '-30s', '-5m', '-2h', '-7d', '-1w', '-1M' (
|
|
141
|
+
• Relative: '-30s', '-5m', '-2h', '-1h30m', '-7d', '-1w', '-1M' (compound supported)
|
|
135
142
|
• Unix timestamp: '1700000000' (seconds since epoch)
|
|
136
143
|
• ISO 8601: '2025-11-13T14:00:00'
|
|
137
144
|
|
|
@@ -145,8 +152,9 @@ module Sumologic
|
|
|
145
152
|
# Filter by source category (ECS only)
|
|
146
153
|
sumo-query discover-source-metadata --filter '_sourceCategory=*ecs*'
|
|
147
154
|
|
|
148
|
-
#
|
|
149
|
-
sumo-query discover-source-metadata --
|
|
155
|
+
# Filter results by keyword (matches name or category)
|
|
156
|
+
sumo-query discover-source-metadata --keyword nginx
|
|
157
|
+
sumo-query discover-source-metadata --keyword nginx -l 10
|
|
150
158
|
|
|
151
159
|
# Save to file
|
|
152
160
|
sumo-query discover-source-metadata --output discovered-sources.json
|
|
@@ -158,6 +166,8 @@ module Sumologic
|
|
|
158
166
|
option :time_zone, type: :string, default: 'UTC', aliases: '-z',
|
|
159
167
|
desc: 'Time zone (UTC, EST, AEST, +00:00, America/New_York, Australia/Sydney)'
|
|
160
168
|
option :filter, type: :string, desc: 'Optional filter query (e.g., _sourceCategory=*ecs*)'
|
|
169
|
+
option :keyword, type: :string, aliases: '-k', desc: 'Filter results by keyword (matches name or category)'
|
|
170
|
+
option :limit, type: :numeric, aliases: '-l', desc: 'Maximum number of sources to return'
|
|
161
171
|
def discover_source_metadata
|
|
162
172
|
Commands::DiscoverSourceMetadataCommand.new(options, create_client).execute
|
|
163
173
|
end
|
data/lib/sumologic/client.rb
CHANGED
|
@@ -83,11 +83,13 @@ module Sumologic
|
|
|
83
83
|
)
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
# List
|
|
86
|
+
# List collectors with optional filtering
|
|
87
87
|
#
|
|
88
|
+
# @param query [String, nil] Filter by name or category (case-insensitive)
|
|
89
|
+
# @param limit [Integer, nil] Maximum number of collectors to return
|
|
88
90
|
# @return [Array<Hash>] Array of collector hashes
|
|
89
|
-
def list_collectors
|
|
90
|
-
@collector.list
|
|
91
|
+
def list_collectors(query: nil, limit: nil)
|
|
92
|
+
@collector.list(query: query, limit: limit)
|
|
91
93
|
end
|
|
92
94
|
|
|
93
95
|
# List sources for a specific collector
|
|
@@ -98,11 +100,15 @@ module Sumologic
|
|
|
98
100
|
@source.list(collector_id: collector_id)
|
|
99
101
|
end
|
|
100
102
|
|
|
101
|
-
# List all sources from all collectors
|
|
103
|
+
# List all sources from all collectors with optional filtering
|
|
102
104
|
#
|
|
105
|
+
# @param collector [String, nil] Filter collectors by name
|
|
106
|
+
# @param name [String, nil] Filter sources by name
|
|
107
|
+
# @param category [String, nil] Filter sources by category
|
|
108
|
+
# @param limit [Integer, nil] Maximum total sources to return
|
|
103
109
|
# @return [Array<Hash>] Array of { 'collector' => Hash, 'sources' => Array<Hash> }
|
|
104
|
-
def list_all_sources
|
|
105
|
-
@source.list_all
|
|
110
|
+
def list_all_sources(collector: nil, name: nil, category: nil, limit: nil)
|
|
111
|
+
@source.list_all(collector: collector, name: name, category: category, limit: limit)
|
|
106
112
|
end
|
|
107
113
|
|
|
108
114
|
# Discover source metadata from actual log data
|
|
@@ -111,14 +117,14 @@ module Sumologic
|
|
|
111
117
|
# @param from_time [String] Start time (ISO 8601, unix timestamp, or relative)
|
|
112
118
|
# @param to_time [String] End time
|
|
113
119
|
# @param time_zone [String] Time zone (default: UTC)
|
|
114
|
-
# @param
|
|
120
|
+
# @param options [Hash] Optional filters — :filter, :keyword, :limit
|
|
115
121
|
# @return [Hash] Discovery results with source metadata
|
|
116
|
-
def discover_source_metadata(from_time:, to_time:, time_zone: 'UTC',
|
|
122
|
+
def discover_source_metadata(from_time:, to_time:, time_zone: 'UTC', **options)
|
|
117
123
|
@source_metadata_discovery.discover(
|
|
118
124
|
from_time: from_time,
|
|
119
125
|
to_time: to_time,
|
|
120
126
|
time_zone: time_zone,
|
|
121
|
-
|
|
127
|
+
**options
|
|
122
128
|
)
|
|
123
129
|
end
|
|
124
130
|
|
|
@@ -45,6 +45,10 @@ module Sumologic
|
|
|
45
45
|
@base_url_v2 ||= build_base_url('v2')
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def web_ui_base_url
|
|
49
|
+
@web_ui_base_url ||= build_web_ui_base_url
|
|
50
|
+
end
|
|
51
|
+
|
|
48
52
|
def validate!
|
|
49
53
|
raise AuthenticationError, 'SUMO_ACCESS_ID not set' unless @access_id
|
|
50
54
|
raise AuthenticationError, 'SUMO_ACCESS_KEY not set' unless @access_key
|
|
@@ -52,6 +56,23 @@ module Sumologic
|
|
|
52
56
|
|
|
53
57
|
private
|
|
54
58
|
|
|
59
|
+
def build_web_ui_base_url
|
|
60
|
+
case @deployment
|
|
61
|
+
when /^http/
|
|
62
|
+
@deployment.sub('api.', 'service.').sub(%r{/api/v\d+.*}, '')
|
|
63
|
+
when 'us1'
|
|
64
|
+
'https://service.sumologic.com'
|
|
65
|
+
when 'us2'
|
|
66
|
+
'https://service.us2.sumologic.com'
|
|
67
|
+
when 'eu'
|
|
68
|
+
'https://service.eu.sumologic.com'
|
|
69
|
+
when 'au'
|
|
70
|
+
'https://service.au.sumologic.com'
|
|
71
|
+
else
|
|
72
|
+
"https://service.#{@deployment}.sumologic.com"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
55
76
|
def build_base_url(version = API_VERSION)
|
|
56
77
|
case @deployment
|
|
57
78
|
when /^http/
|
|
@@ -13,9 +13,12 @@ module Sumologic
|
|
|
13
13
|
@http = http_client
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
# List
|
|
17
|
-
#
|
|
18
|
-
|
|
16
|
+
# List collectors with optional client-side filtering
|
|
17
|
+
#
|
|
18
|
+
# @param query [String, nil] Filter by name or category (case-insensitive substring match)
|
|
19
|
+
# @param limit [Integer, nil] Maximum number of collectors to return
|
|
20
|
+
# @return [Array<Hash>] Array of collector objects
|
|
21
|
+
def list(query: nil, limit: nil)
|
|
19
22
|
data = @http.request(
|
|
20
23
|
method: :get,
|
|
21
24
|
path: '/collectors'
|
|
@@ -23,10 +26,25 @@ module Sumologic
|
|
|
23
26
|
|
|
24
27
|
collectors = data['collectors'] || []
|
|
25
28
|
log_info "Found #{collectors.size} collectors"
|
|
29
|
+
|
|
30
|
+
collectors = filter_by_query(collectors, query) if query
|
|
31
|
+
collectors = collectors.take(limit) if limit
|
|
32
|
+
|
|
26
33
|
collectors
|
|
27
34
|
rescue StandardError => e
|
|
28
35
|
raise Error, "Failed to list collectors: #{e.message}"
|
|
29
36
|
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def filter_by_query(collectors, query)
|
|
41
|
+
pattern = query.downcase
|
|
42
|
+
collectors.select do |c|
|
|
43
|
+
name = (c['name'] || '').downcase
|
|
44
|
+
category = (c['category'] || '').downcase
|
|
45
|
+
name.include?(pattern) || category.include?(pattern)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
30
48
|
end
|
|
31
49
|
end
|
|
32
50
|
end
|
|
@@ -31,19 +31,28 @@ module Sumologic
|
|
|
31
31
|
raise Error, "Failed to list sources for collector #{collector_id}: #{e.message}"
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
# List all sources from all collectors
|
|
34
|
+
# List all sources from all collectors with optional filtering
|
|
35
35
|
# Returns array of hashes with collector info and their sources
|
|
36
36
|
# Uses parallel fetching with thread pool for better performance
|
|
37
|
-
|
|
37
|
+
#
|
|
38
|
+
# @param collector [String, nil] Filter collectors by name (case-insensitive substring)
|
|
39
|
+
# @param name [String, nil] Filter sources by name (case-insensitive substring)
|
|
40
|
+
# @param category [String, nil] Filter sources by category (case-insensitive substring)
|
|
41
|
+
# @param limit [Integer, nil] Maximum total sources to return
|
|
42
|
+
def list_all(collector: nil, name: nil, category: nil, limit: nil)
|
|
38
43
|
collectors = @collector_client.list
|
|
39
44
|
active_collectors = collectors.select { |c| c['alive'] }
|
|
45
|
+
active_collectors = filter_collectors(active_collectors, collector) if collector
|
|
40
46
|
|
|
41
47
|
log_info "Fetching sources for #{active_collectors.size} active collectors in parallel..."
|
|
42
48
|
|
|
43
|
-
result = @fetcher.fetch_all(active_collectors) do |
|
|
44
|
-
fetch_collector_sources(
|
|
49
|
+
result = @fetcher.fetch_all(active_collectors) do |c|
|
|
50
|
+
fetch_collector_sources(c)
|
|
45
51
|
end
|
|
46
52
|
|
|
53
|
+
result = filter_sources(result, name: name, category: category)
|
|
54
|
+
result = apply_source_limit(result, limit) if limit
|
|
55
|
+
|
|
47
56
|
log_info "Total: #{result.size} collectors with sources"
|
|
48
57
|
result
|
|
49
58
|
rescue StandardError => e
|
|
@@ -52,6 +61,37 @@ module Sumologic
|
|
|
52
61
|
|
|
53
62
|
private
|
|
54
63
|
|
|
64
|
+
def filter_collectors(collectors, pattern)
|
|
65
|
+
pattern = pattern.downcase
|
|
66
|
+
collectors.select { |c| (c['name'] || '').downcase.include?(pattern) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def filter_sources(result, name:, category:)
|
|
70
|
+
matcher = source_matcher(name&.downcase, category&.downcase)
|
|
71
|
+
result.filter_map do |entry|
|
|
72
|
+
filtered = entry['sources'].select(&matcher)
|
|
73
|
+
{ 'collector' => entry['collector'], 'sources' => filtered } unless filtered.empty?
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def source_matcher(name_pattern, cat_pattern)
|
|
78
|
+
lambda do |s|
|
|
79
|
+
(!name_pattern || (s['name'] || '').downcase.include?(name_pattern)) &&
|
|
80
|
+
(!cat_pattern || (s['category'] || '').downcase.include?(cat_pattern))
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def apply_source_limit(result, limit)
|
|
85
|
+
remaining = limit
|
|
86
|
+
result.each_with_object([]) do |entry, acc|
|
|
87
|
+
break acc if remaining <= 0
|
|
88
|
+
|
|
89
|
+
sources = entry['sources'].take(remaining)
|
|
90
|
+
acc << { 'collector' => entry['collector'], 'sources' => sources }
|
|
91
|
+
remaining -= sources.size
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
55
95
|
# Fetch sources for a single collector
|
|
56
96
|
# @return [Hash] collector and sources data
|
|
57
97
|
def fetch_collector_sources(collector)
|
|
@@ -22,8 +22,12 @@ module Sumologic
|
|
|
22
22
|
# @param from_time [String] Start time (ISO 8601, unix timestamp, or relative)
|
|
23
23
|
# @param to_time [String] End time
|
|
24
24
|
# @param time_zone [String] Time zone (default: UTC)
|
|
25
|
-
# @param
|
|
26
|
-
def discover(from_time:, to_time:, time_zone: 'UTC',
|
|
25
|
+
# @param options [Hash] Optional filters — :filter, :keyword, :limit
|
|
26
|
+
def discover(from_time:, to_time:, time_zone: 'UTC', **options)
|
|
27
|
+
filter = options[:filter]
|
|
28
|
+
keyword = options[:keyword]
|
|
29
|
+
limit = options[:limit]
|
|
30
|
+
|
|
27
31
|
query = build_query(filter)
|
|
28
32
|
log_info "Discovering source metadata with query: #{query}"
|
|
29
33
|
log_info "Time range: #{from_time} to #{to_time} (#{time_zone})"
|
|
@@ -39,6 +43,8 @@ module Sumologic
|
|
|
39
43
|
)
|
|
40
44
|
|
|
41
45
|
source_models = parse_aggregation_results(records)
|
|
46
|
+
source_models = filter_by_keyword(source_models, keyword) if keyword
|
|
47
|
+
source_models = source_models.take(limit) if limit
|
|
42
48
|
|
|
43
49
|
{
|
|
44
50
|
'time_range' => {
|
|
@@ -47,6 +53,7 @@ module Sumologic
|
|
|
47
53
|
'time_zone' => time_zone
|
|
48
54
|
},
|
|
49
55
|
'filter' => filter,
|
|
56
|
+
'keyword' => keyword,
|
|
50
57
|
'total_sources' => source_models.size,
|
|
51
58
|
'sources' => source_models.map(&:to_h)
|
|
52
59
|
}
|
|
@@ -56,6 +63,14 @@ module Sumologic
|
|
|
56
63
|
|
|
57
64
|
private
|
|
58
65
|
|
|
66
|
+
def filter_by_keyword(source_models, keyword)
|
|
67
|
+
pattern = keyword.downcase
|
|
68
|
+
source_models.select do |s|
|
|
69
|
+
(s.name || '').downcase.include?(pattern) ||
|
|
70
|
+
(s.category || '').downcase.include?(pattern)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
59
74
|
# Build aggregation query to discover sources
|
|
60
75
|
def build_query(filter)
|
|
61
76
|
base = filter || '*'
|
|
@@ -7,7 +7,7 @@ module Sumologic
|
|
|
7
7
|
# Parses various time formats into ISO 8601 strings for the Sumo Logic API
|
|
8
8
|
# Supports:
|
|
9
9
|
# - 'now' - current time
|
|
10
|
-
# - Relative times: '-30s', '-5m', '-2h', '-7d', '-1w', '-1M'
|
|
10
|
+
# - Relative times: '-30s', '-5m', '-2h', '-7d', '-1w', '-1M', '-1h30m'
|
|
11
11
|
# - Unix timestamps: '1700000000' or 1700000000
|
|
12
12
|
# - ISO 8601: '2025-11-13T14:00:00'
|
|
13
13
|
class TimeParser
|
|
@@ -21,7 +21,9 @@ module Sumologic
|
|
|
21
21
|
'M' => 2_592_000 # months (30 days approximation)
|
|
22
22
|
}.freeze
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
# Matches single or compound relative times: -30m, -1h30m, -2d3h15m
|
|
25
|
+
RELATIVE_TIME_REGEX = /^([+-])(\d+[smhdwM])+$/.freeze
|
|
26
|
+
RELATIVE_COMPONENT_REGEX = /(\d+)([smhdwM])/.freeze
|
|
25
27
|
|
|
26
28
|
class ParseError < StandardError; end
|
|
27
29
|
|
|
@@ -32,10 +34,8 @@ module Sumologic
|
|
|
32
34
|
def self.parse(time_str, _timezone: 'UTC')
|
|
33
35
|
return parse_now if time_str.to_s.downcase == 'now'
|
|
34
36
|
|
|
35
|
-
# Try relative time format (e.g., '-30m', '+1h')
|
|
36
|
-
if time_str.is_a?(String) &&
|
|
37
|
-
return parse_relative_time(match)
|
|
38
|
-
end
|
|
37
|
+
# Try relative time format (e.g., '-30m', '+1h', '-1h30m')
|
|
38
|
+
return parse_relative_time(time_str) if time_str.is_a?(String) && time_str.match?(RELATIVE_TIME_REGEX)
|
|
39
39
|
|
|
40
40
|
# Try Unix timestamp (integer or numeric string)
|
|
41
41
|
return parse_unix_timestamp(time_str) if unix_timestamp?(time_str)
|
|
@@ -95,14 +95,14 @@ module Sumologic
|
|
|
95
95
|
format_time(Time.now)
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
private_class_method def self.parse_relative_time(
|
|
99
|
-
sign
|
|
100
|
-
|
|
101
|
-
amount = -amount if sign == '-'
|
|
98
|
+
private_class_method def self.parse_relative_time(time_str)
|
|
99
|
+
sign = time_str[0]
|
|
100
|
+
components = time_str.scan(RELATIVE_COMPONENT_REGEX)
|
|
102
101
|
|
|
103
|
-
seconds_delta = amount * UNITS[unit]
|
|
104
|
-
|
|
102
|
+
seconds_delta = components.sum { |amount, unit| amount.to_i * UNITS[unit] }
|
|
103
|
+
seconds_delta = -seconds_delta if sign == '-'
|
|
105
104
|
|
|
105
|
+
target_time = Time.now + seconds_delta
|
|
106
106
|
format_time(target_time)
|
|
107
107
|
end
|
|
108
108
|
|
data/lib/sumologic/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sumologic-query
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.4.
|
|
4
|
+
version: 1.4.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- patrick204nqh
|
|
@@ -154,7 +154,6 @@ homepage: https://github.com/patrick204nqh/sumologic-query
|
|
|
154
154
|
licenses:
|
|
155
155
|
- MIT
|
|
156
156
|
metadata:
|
|
157
|
-
homepage_uri: https://github.com/patrick204nqh/sumologic-query
|
|
158
157
|
source_code_uri: https://github.com/patrick204nqh/sumologic-query
|
|
159
158
|
bug_tracker_uri: https://github.com/patrick204nqh/sumologic-query/issues
|
|
160
159
|
changelog_uri: https://github.com/patrick204nqh/sumologic-query/blob/main/CHANGELOG.md
|