sumologic-query 1.3.3 → 1.3.4
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 +17 -0
- data/README.md +77 -35
- data/lib/sumologic/cli/commands/base_command.rb +6 -0
- data/lib/sumologic/cli/commands/search_command.rb +29 -10
- data/lib/sumologic/cli.rb +22 -9
- data/lib/sumologic/http/client.rb +1 -1
- data/lib/sumologic/http/debug_logger.rb +2 -1
- data/lib/sumologic/utils/time_parser.rb +147 -0
- data/lib/sumologic/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 72c4c0c9c57655df15506b9be19a21de47398ba2a2aedaae8e8d22d3efbd0873
|
|
4
|
+
data.tar.gz: 9cd38107b915187cb699e32305c8af8e6a970c2ea24c2b2340a443cc0a0e2637
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a073e490f4714e8f11c8c495775937dd42bb420535c38d82137a71a940b4b9db2b9fe33d4e6ec4b7745385a7661ec657db5472b9761dbecc110fb473efca757f
|
|
7
|
+
data.tar.gz: 3a9aa83222b99b34fbcdceb45f2e1cdd126c29d28ba4a93db818ea34438b366f7a7b5eb0290f7986e0a9ef9d5deaed95465cca45a72468ec432ec5d6d97a29f4
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@ 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.3.4](https://github.com/patrick204nqh/sumologic-query/compare/v1.3.3...v1.3.4) (2025-11-19)
|
|
10
|
+
|
|
11
|
+
### 🎉 New Features
|
|
12
|
+
|
|
13
|
+
- add time parsing utility and enhance CLI time options for flexible querying
|
|
14
|
+
- enhance debug logging to include request headers for better traceability
|
|
15
|
+
|
|
16
|
+
### 🐛 Bug Fixes
|
|
17
|
+
|
|
18
|
+
- freeze regex for relative time parsing and improve error message formatting
|
|
19
|
+
|
|
20
|
+
### 📚 Documentation
|
|
21
|
+
|
|
22
|
+
- update README and examples to enhance time format usage and add new time format examples
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
9
26
|
## [1.3.3](https://github.com/patrick204nqh/sumologic-query/compare/v1.3.2...v1.3.3) (2025-11-17)
|
|
10
27
|
|
|
11
28
|
### 🎉 New Features
|
data/README.md
CHANGED
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
## Why This Tool?
|
|
10
10
|
|
|
11
|
+
- **Intuitive time parsing**: Use relative times like `-1h`, `-30m`, or `now` - no more calculating timestamps!
|
|
12
|
+
- **Flexible timezone support**: US, Australian, and IANA timezone formats supported
|
|
11
13
|
- **Minimal dependencies**: Uses only Ruby stdlib + Thor for CLI
|
|
12
14
|
- **Fast queries**: Efficient polling and automatic pagination
|
|
15
|
+
- **Interactive mode**: Explore logs with FZF-powered fuzzy search and preview
|
|
13
16
|
- **Simple interface**: Just query, get results, done
|
|
14
17
|
- **Read-only**: No write operations, perfect for safe log access
|
|
15
18
|
- **Modular architecture**: Clean separation of concerns (HTTP, Search, Metadata)
|
|
@@ -62,7 +65,13 @@ export SUMO_DEPLOYMENT="us2" # Optional: us1, us2 (default), eu, au, etc.
|
|
|
62
65
|
### 2. Run Your First Query
|
|
63
66
|
|
|
64
67
|
```bash
|
|
65
|
-
# Search logs
|
|
68
|
+
# Search logs from last hour (easy!)
|
|
69
|
+
sumo-query search --query 'error' --from '-1h' --to 'now' --limit 10
|
|
70
|
+
|
|
71
|
+
# Search logs from last 30 minutes
|
|
72
|
+
sumo-query search --query 'error' --from '-30m' --to 'now'
|
|
73
|
+
|
|
74
|
+
# Or use ISO 8601 format
|
|
66
75
|
sumo-query search --query 'error' \
|
|
67
76
|
--from '2025-11-13T14:00:00' \
|
|
68
77
|
--to '2025-11-13T15:00:00' \
|
|
@@ -108,13 +117,13 @@ sumo-query search --query "YOUR_QUERY" \
|
|
|
108
117
|
Explore your logs interactively with a powerful FZF-based interface:
|
|
109
118
|
|
|
110
119
|
```bash
|
|
111
|
-
# Launch interactive mode
|
|
112
|
-
sumo-query search --query 'error'
|
|
113
|
-
--from '2025-11-13T14:00:00' \
|
|
114
|
-
--to '2025-11-13T15:00:00' \
|
|
115
|
-
--interactive
|
|
120
|
+
# Launch interactive mode - last hour
|
|
121
|
+
sumo-query search --query 'error' --from '-1h' --to 'now' --interactive
|
|
116
122
|
|
|
117
|
-
#
|
|
123
|
+
# Last 30 minutes with shorthand
|
|
124
|
+
sumo-query search -q 'error' -f '-30m' -t 'now' -i
|
|
125
|
+
|
|
126
|
+
# Or use ISO 8601 format
|
|
118
127
|
sumo-query search -q 'error' -f '2025-11-13T14:00:00' -t '2025-11-13T15:00:00' -i
|
|
119
128
|
```
|
|
120
129
|
|
|
@@ -141,6 +150,27 @@ sumo-query search -q 'error' -f '2025-11-13T14:00:00' -t '2025-11-13T15:00:00' -
|
|
|
141
150
|
- Install FZF: `brew install fzf` (macOS) or `apt-get install fzf` (Linux)
|
|
142
151
|
- See: https://github.com/junegunn/fzf#installation
|
|
143
152
|
|
|
153
|
+
### Time Format Examples
|
|
154
|
+
|
|
155
|
+
Combine relative times with timezones for powerful queries:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# Last hour in Sydney time
|
|
159
|
+
sumo-query search -q 'error' -f '-1h' -t 'now' -z AEST
|
|
160
|
+
|
|
161
|
+
# Last 30 minutes in US Eastern time
|
|
162
|
+
sumo-query search -q 'error' -f '-30m' -t 'now' -z EST
|
|
163
|
+
|
|
164
|
+
# Last 7 days with output to file (directories auto-created)
|
|
165
|
+
sumo-query search -q 'error' -f '-7d' -t 'now' -o logs/weekly/errors.json
|
|
166
|
+
|
|
167
|
+
# Mix relative and ISO 8601 formats
|
|
168
|
+
sumo-query search -q 'error' -f '-24h' -t '2025-11-19T12:00:00'
|
|
169
|
+
|
|
170
|
+
# Unix timestamps from last hour to now
|
|
171
|
+
sumo-query search -q 'error' -f '1700000000' -t 'now'
|
|
172
|
+
```
|
|
173
|
+
|
|
144
174
|
### List Collectors
|
|
145
175
|
|
|
146
176
|
```bash
|
|
@@ -157,12 +187,9 @@ sumo-query sources [--output FILE]
|
|
|
157
187
|
|
|
158
188
|
Lists all sources from active collectors.
|
|
159
189
|
|
|
160
|
-
**See [examples/queries.md](examples/queries.md) for more query patterns and examples.**
|
|
161
190
|
|
|
162
191
|
## Ruby Library Usage
|
|
163
192
|
|
|
164
|
-
Use the library directly in your Ruby code:
|
|
165
|
-
|
|
166
193
|
```ruby
|
|
167
194
|
require 'sumologic'
|
|
168
195
|
|
|
@@ -182,35 +209,48 @@ results = client.search(
|
|
|
182
209
|
limit: 1000
|
|
183
210
|
)
|
|
184
211
|
|
|
185
|
-
|
|
186
|
-
puts message['map']['message']
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
# List collectors
|
|
212
|
+
# List collectors and sources
|
|
190
213
|
collectors = client.list_collectors
|
|
191
|
-
|
|
192
|
-
# List all sources
|
|
193
214
|
sources = client.list_all_sources
|
|
194
215
|
```
|
|
195
216
|
|
|
196
|
-
**
|
|
217
|
+
**Time parsing utilities:**
|
|
218
|
+
|
|
219
|
+
```ruby
|
|
220
|
+
require 'sumologic/utils/time_parser'
|
|
221
|
+
|
|
222
|
+
# Parse relative times and timezones
|
|
223
|
+
from_time = Sumologic::Utils::TimeParser.parse('-1h')
|
|
224
|
+
timezone = Sumologic::Utils::TimeParser.parse_timezone('AEST')
|
|
225
|
+
```
|
|
226
|
+
|
|
197
227
|
|
|
198
228
|
## Time Formats
|
|
199
229
|
|
|
200
|
-
|
|
230
|
+
Multiple time formats are supported:
|
|
201
231
|
|
|
202
232
|
```bash
|
|
203
|
-
#
|
|
204
|
-
|
|
233
|
+
# Relative time (easiest!)
|
|
234
|
+
sumo-query search -q 'error' -f '-1h' -t 'now'
|
|
235
|
+
sumo-query search -q 'error' -f '-30m' -t 'now'
|
|
236
|
+
|
|
237
|
+
# ISO 8601
|
|
238
|
+
sumo-query search -q 'error' -f '2025-11-13T14:00:00' -t '2025-11-13T15:00:00'
|
|
205
239
|
|
|
206
|
-
#
|
|
207
|
-
|
|
240
|
+
# Unix timestamps
|
|
241
|
+
sumo-query search -q 'error' -f '1700000000' -t 'now'
|
|
208
242
|
|
|
209
|
-
#
|
|
210
|
-
|
|
211
|
-
|
|
243
|
+
# With timezones
|
|
244
|
+
sumo-query search -q 'error' -f '-1h' -t 'now' -z 'AEST'
|
|
245
|
+
sumo-query search -q 'error' -f '-1h' -t 'now' -z 'America/New_York'
|
|
212
246
|
```
|
|
213
247
|
|
|
248
|
+
**Supported time units:** `s`, `m`, `h`, `d`, `w`, `M`, `now`
|
|
249
|
+
|
|
250
|
+
**Supported timezones:** IANA names (`UTC`, `America/New_York`, `Australia/Sydney`), US abbreviations (`EST`, `PST`), Australian abbreviations (`AEST`, `ACST`, `AWST`), UTC offsets (`+10:00`)
|
|
251
|
+
|
|
252
|
+
See [examples/time-formats.md](examples/time-formats.md) for comprehensive examples.
|
|
253
|
+
|
|
214
254
|
## Output Format
|
|
215
255
|
|
|
216
256
|
Results are returned as JSON:
|
|
@@ -253,11 +293,10 @@ Query execution time depends on data volume:
|
|
|
253
293
|
|
|
254
294
|
## Documentation
|
|
255
295
|
|
|
256
|
-
- **[Quick Reference (tldr)](docs/tldr.md)** - Concise command examples
|
|
257
|
-
- **[Query Examples](examples/queries.md)** - Common query patterns
|
|
258
|
-
- **[
|
|
259
|
-
- **[Architecture](docs/architecture/)** -
|
|
260
|
-
- **[Troubleshooting](docs/troubleshooting.md)** - Common issues and solutions
|
|
296
|
+
- **[Quick Reference (tldr)](docs/tldr.md)** - Concise command examples
|
|
297
|
+
- **[Query Examples](examples/queries.md)** - Common query patterns
|
|
298
|
+
- **[Time Format Examples](examples/time-formats.md)** - Time parsing and timezone options
|
|
299
|
+
- **[Architecture](docs/architecture/)** - Design and architecture decisions
|
|
261
300
|
|
|
262
301
|
## Development
|
|
263
302
|
|
|
@@ -271,16 +310,19 @@ git clone https://github.com/patrick204nqh/sumologic-query.git
|
|
|
271
310
|
cd sumologic-query
|
|
272
311
|
bundle install
|
|
273
312
|
|
|
274
|
-
# Run tests
|
|
313
|
+
# Run tests (73+ specs including time parser tests)
|
|
275
314
|
bundle exec rspec
|
|
276
315
|
|
|
277
316
|
# Run linter
|
|
278
317
|
bundle exec rubocop
|
|
279
318
|
|
|
280
|
-
# Test locally
|
|
319
|
+
# Test locally with new time formats
|
|
320
|
+
bundle exec bin/sumo-query search --query "error" \
|
|
321
|
+
--from "-1h" --to "now"
|
|
322
|
+
|
|
323
|
+
# Test with timezone support
|
|
281
324
|
bundle exec bin/sumo-query search --query "error" \
|
|
282
|
-
--from "
|
|
283
|
-
--to "2025-11-13T15:00:00"
|
|
325
|
+
--from "-30m" --to "now" --time-zone "AEST"
|
|
284
326
|
```
|
|
285
327
|
|
|
286
328
|
## Contributing
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
3
5
|
module Sumologic
|
|
4
6
|
class CLI < Thor
|
|
5
7
|
module Commands
|
|
@@ -19,6 +21,10 @@ module Sumologic
|
|
|
19
21
|
json_output = JSON.pretty_generate(data)
|
|
20
22
|
|
|
21
23
|
if options[:output]
|
|
24
|
+
# Create parent directories if they don't exist
|
|
25
|
+
output_dir = File.dirname(options[:output])
|
|
26
|
+
FileUtils.mkdir_p(output_dir) unless output_dir == '.'
|
|
27
|
+
|
|
22
28
|
File.write(options[:output], json_output)
|
|
23
29
|
warn "\nResults saved to: #{options[:output]}"
|
|
24
30
|
else
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_command'
|
|
4
|
+
require_relative '../../utils/time_parser'
|
|
4
5
|
|
|
5
6
|
module Sumologic
|
|
6
7
|
class CLI < Thor
|
|
@@ -8,6 +9,7 @@ module Sumologic
|
|
|
8
9
|
# Handles the search command execution
|
|
9
10
|
class SearchCommand < BaseCommand
|
|
10
11
|
def execute
|
|
12
|
+
parse_time_options
|
|
11
13
|
log_search_info
|
|
12
14
|
results = perform_search
|
|
13
15
|
|
|
@@ -22,11 +24,26 @@ module Sumologic
|
|
|
22
24
|
|
|
23
25
|
private
|
|
24
26
|
|
|
27
|
+
def parse_time_options
|
|
28
|
+
# Parse time formats and store both original and parsed values
|
|
29
|
+
@original_from = options[:from]
|
|
30
|
+
@original_to = options[:to]
|
|
31
|
+
@parsed_from = Utils::TimeParser.parse(options[:from])
|
|
32
|
+
@parsed_to = Utils::TimeParser.parse(options[:to])
|
|
33
|
+
@parsed_timezone = Utils::TimeParser.parse_timezone(options[:time_zone])
|
|
34
|
+
rescue Utils::TimeParser::ParseError => e
|
|
35
|
+
warn "Error parsing time: #{e.message}"
|
|
36
|
+
exit 1
|
|
37
|
+
end
|
|
38
|
+
|
|
25
39
|
def log_search_info
|
|
26
40
|
warn '=' * 60
|
|
27
41
|
warn 'Sumo Logic Search Query'
|
|
28
42
|
warn '=' * 60
|
|
29
|
-
warn "Time Range: #{
|
|
43
|
+
warn "Time Range: #{@original_from} to #{@original_to}"
|
|
44
|
+
if @original_from != @parsed_from || @original_to != @parsed_to
|
|
45
|
+
warn " (Parsed: #{@parsed_from} to #{@parsed_to})"
|
|
46
|
+
end
|
|
30
47
|
warn "Query: #{options[:query]}"
|
|
31
48
|
warn "Limit: #{options[:limit] || 'unlimited'}"
|
|
32
49
|
warn '-' * 60
|
|
@@ -37,9 +54,9 @@ module Sumologic
|
|
|
37
54
|
def perform_search
|
|
38
55
|
client.search(
|
|
39
56
|
query: options[:query],
|
|
40
|
-
from_time:
|
|
41
|
-
to_time:
|
|
42
|
-
time_zone:
|
|
57
|
+
from_time: @parsed_from,
|
|
58
|
+
to_time: @parsed_to,
|
|
59
|
+
time_zone: @parsed_timezone,
|
|
43
60
|
limit: options[:limit]
|
|
44
61
|
)
|
|
45
62
|
end
|
|
@@ -54,9 +71,11 @@ module Sumologic
|
|
|
54
71
|
def output_search_results(results)
|
|
55
72
|
output_json(
|
|
56
73
|
query: options[:query],
|
|
57
|
-
from:
|
|
58
|
-
to:
|
|
59
|
-
|
|
74
|
+
from: @parsed_from,
|
|
75
|
+
to: @parsed_to,
|
|
76
|
+
from_original: @original_from,
|
|
77
|
+
to_original: @original_to,
|
|
78
|
+
time_zone: @parsed_timezone,
|
|
60
79
|
message_count: results.size,
|
|
61
80
|
messages: results
|
|
62
81
|
)
|
|
@@ -75,9 +94,9 @@ module Sumologic
|
|
|
75
94
|
def build_formatted_results(results)
|
|
76
95
|
{
|
|
77
96
|
'query' => options[:query],
|
|
78
|
-
'from' =>
|
|
79
|
-
'to' =>
|
|
80
|
-
'time_zone' =>
|
|
97
|
+
'from' => @parsed_from,
|
|
98
|
+
'to' => @parsed_to,
|
|
99
|
+
'time_zone' => @parsed_timezone,
|
|
81
100
|
'message_count' => results.size,
|
|
82
101
|
'messages' => results
|
|
83
102
|
}
|
data/lib/sumologic/cli.rb
CHANGED
|
@@ -26,25 +26,38 @@ module Sumologic
|
|
|
26
26
|
long_desc <<~DESC
|
|
27
27
|
Search Sumo Logic logs using a query string.
|
|
28
28
|
|
|
29
|
+
Time Formats:
|
|
30
|
+
--from and --to support multiple formats:
|
|
31
|
+
• 'now' - current time
|
|
32
|
+
• Relative: '-30s', '-5m', '-2h', '-7d', '-1w', '-1M' (sec/min/hour/day/week/month)
|
|
33
|
+
• Unix timestamp: '1700000000' (seconds since epoch)
|
|
34
|
+
• ISO 8601: '2025-11-13T14:00:00'
|
|
35
|
+
|
|
29
36
|
Examples:
|
|
30
|
-
#
|
|
37
|
+
# Last 30 minutes
|
|
38
|
+
sumo-query search --query 'error' --from '-30m' --to 'now'
|
|
39
|
+
|
|
40
|
+
# Last hour with ISO format
|
|
31
41
|
sumo-query search --query 'error | timeslice 5m | count' \\
|
|
32
42
|
--from '2025-11-13T14:00:00' --to '2025-11-13T15:00:00'
|
|
33
43
|
|
|
34
|
-
#
|
|
44
|
+
# Last 7 days
|
|
35
45
|
sumo-query search --query '"connection timeout"' \\
|
|
36
|
-
--from '
|
|
37
|
-
|
|
46
|
+
--from '-7d' --to 'now' --limit 100
|
|
47
|
+
|
|
48
|
+
# Using Unix timestamps
|
|
49
|
+
sumo-query search --query 'error' \\
|
|
50
|
+
--from '1700000000' --to '1700003600'
|
|
38
51
|
|
|
39
52
|
# Interactive mode with FZF
|
|
40
53
|
sumo-query search --query 'error' \\
|
|
41
|
-
--from '
|
|
42
|
-
--interactive
|
|
54
|
+
--from '-1h' --to 'now' --interactive
|
|
43
55
|
DESC
|
|
44
56
|
option :query, type: :string, required: true, aliases: '-q', desc: 'Search query'
|
|
45
|
-
option :from, type: :string, required: true, aliases: '-f', desc: 'Start time (ISO 8601)'
|
|
46
|
-
option :to, type: :string, required: true, aliases: '-t', desc: 'End time (ISO 8601)'
|
|
47
|
-
option :time_zone, type: :string, default: 'UTC', aliases: '-z',
|
|
57
|
+
option :from, type: :string, required: true, aliases: '-f', desc: 'Start time (now, -30m, unix timestamp, ISO 8601)'
|
|
58
|
+
option :to, type: :string, required: true, aliases: '-t', desc: 'End time (now, -30m, unix timestamp, ISO 8601)'
|
|
59
|
+
option :time_zone, type: :string, default: 'UTC', aliases: '-z',
|
|
60
|
+
desc: 'Time zone (UTC, EST, AEST, +00:00, America/New_York, Australia/Sydney)'
|
|
48
61
|
option :limit, type: :numeric, aliases: '-l', desc: 'Maximum messages to return'
|
|
49
62
|
option :interactive, type: :boolean, aliases: '-i', desc: 'Launch interactive browser (requires fzf)'
|
|
50
63
|
def search
|
|
@@ -29,7 +29,7 @@ module Sumologic
|
|
|
29
29
|
uri = @request_builder.build_uri(path, query_params)
|
|
30
30
|
request = @request_builder.build_request(method, uri, body)
|
|
31
31
|
|
|
32
|
-
DebugLogger.log_request(method, uri, body)
|
|
32
|
+
DebugLogger.log_request(method, uri, body, request.to_hash)
|
|
33
33
|
|
|
34
34
|
response = execute_request(uri, request)
|
|
35
35
|
|
|
@@ -9,12 +9,13 @@ module Sumologic
|
|
|
9
9
|
module DebugLogger
|
|
10
10
|
module_function
|
|
11
11
|
|
|
12
|
-
def log_request(method, uri, body)
|
|
12
|
+
def log_request(method, uri, body, headers = {})
|
|
13
13
|
return unless $DEBUG
|
|
14
14
|
|
|
15
15
|
warn "\n[DEBUG] API Request:"
|
|
16
16
|
warn " Method: #{method.to_s.upcase}"
|
|
17
17
|
warn " URL: #{uri}"
|
|
18
|
+
warn " Cookie: #{headers['Cookie']}" if headers['Cookie']
|
|
18
19
|
log_request_body(body) if body
|
|
19
20
|
warn ''
|
|
20
21
|
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'time'
|
|
4
|
+
|
|
5
|
+
module Sumologic
|
|
6
|
+
module Utils
|
|
7
|
+
# Parses various time formats into ISO 8601 strings for the Sumo Logic API
|
|
8
|
+
# Supports:
|
|
9
|
+
# - 'now' - current time
|
|
10
|
+
# - Relative times: '-30s', '-5m', '-2h', '-7d', '-1w', '-1M'
|
|
11
|
+
# - Unix timestamps: '1700000000' or 1700000000
|
|
12
|
+
# - ISO 8601: '2025-11-13T14:00:00'
|
|
13
|
+
class TimeParser
|
|
14
|
+
# Time unit multipliers in seconds
|
|
15
|
+
UNITS = {
|
|
16
|
+
's' => 1, # seconds
|
|
17
|
+
'm' => 60, # minutes
|
|
18
|
+
'h' => 3600, # hours
|
|
19
|
+
'd' => 86_400, # days
|
|
20
|
+
'w' => 604_800, # weeks (7 days)
|
|
21
|
+
'M' => 2_592_000 # months (30 days approximation)
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
RELATIVE_TIME_REGEX = /^([+-])(\d+)([smhdwM])$/.freeze
|
|
25
|
+
|
|
26
|
+
class ParseError < StandardError; end
|
|
27
|
+
|
|
28
|
+
# Parse a time string into ISO 8601 format
|
|
29
|
+
# @param time_str [String, Integer] Time string or Unix timestamp
|
|
30
|
+
# @param _timezone [String] IANA timezone name (default: 'UTC') - Reserved for future use
|
|
31
|
+
# @return [String] ISO 8601 formatted time string
|
|
32
|
+
def self.parse(time_str, _timezone: 'UTC')
|
|
33
|
+
return parse_now if time_str.to_s.downcase == 'now'
|
|
34
|
+
|
|
35
|
+
# Try relative time format (e.g., '-30m', '+1h')
|
|
36
|
+
if time_str.is_a?(String) && (match = time_str.match(RELATIVE_TIME_REGEX))
|
|
37
|
+
return parse_relative_time(match)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Try Unix timestamp (integer or numeric string)
|
|
41
|
+
return parse_unix_timestamp(time_str) if unix_timestamp?(time_str)
|
|
42
|
+
|
|
43
|
+
# Try ISO 8601 format
|
|
44
|
+
begin
|
|
45
|
+
# Parse in UTC context to avoid local timezone conversion
|
|
46
|
+
parsed = Time.parse(time_str.to_s)
|
|
47
|
+
# If the input doesn't have timezone info, treat it as UTC
|
|
48
|
+
parsed = parsed.getutc unless time_str.to_s.match?(/Z|[+-]\d{2}:?\d{2}$/)
|
|
49
|
+
format_time(parsed)
|
|
50
|
+
rescue ArgumentError
|
|
51
|
+
raise ParseError,
|
|
52
|
+
"Invalid time format: '#{time_str}'. " \
|
|
53
|
+
"Supported formats: 'now', relative (e.g., '-30m'), Unix timestamp, or ISO 8601"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Parse timezone string to standard format
|
|
58
|
+
# Accepts IANA names, offset formats, or common abbreviations
|
|
59
|
+
# @param timezone_str [String] Timezone string
|
|
60
|
+
# @return [String] Standardized timezone string
|
|
61
|
+
def self.parse_timezone(timezone_str)
|
|
62
|
+
return 'UTC' if timezone_str.nil? || timezone_str.empty?
|
|
63
|
+
|
|
64
|
+
# Handle offset formats like "+00:00", "-05:00", "+0000"
|
|
65
|
+
if timezone_str.match?(/^[+-]\d{2}:?\d{2}$/)
|
|
66
|
+
# Normalize to format with colon
|
|
67
|
+
normalized = timezone_str.sub(/^([+-]\d{2})(\d{2})$/, '\1:\2')
|
|
68
|
+
return normalized
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Map common abbreviations to IANA names
|
|
72
|
+
timezone_map = {
|
|
73
|
+
# US timezones
|
|
74
|
+
'EST' => 'America/New_York',
|
|
75
|
+
'EDT' => 'America/New_York',
|
|
76
|
+
'CST' => 'America/Chicago',
|
|
77
|
+
'CDT' => 'America/Chicago',
|
|
78
|
+
'MST' => 'America/Denver',
|
|
79
|
+
'MDT' => 'America/Denver',
|
|
80
|
+
'PST' => 'America/Los_Angeles',
|
|
81
|
+
'PDT' => 'America/Los_Angeles',
|
|
82
|
+
# Australian timezones
|
|
83
|
+
'AEST' => 'Australia/Sydney', # Australian Eastern Standard Time
|
|
84
|
+
'AEDT' => 'Australia/Sydney', # Australian Eastern Daylight Time
|
|
85
|
+
'ACST' => 'Australia/Adelaide', # Australian Central Standard Time
|
|
86
|
+
'ACDT' => 'Australia/Adelaide', # Australian Central Daylight Time
|
|
87
|
+
'AWST' => 'Australia/Perth', # Australian Western Standard Time
|
|
88
|
+
'AWDT' => 'Australia/Perth' # Australian Western Daylight Time (rarely used)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
timezone_map[timezone_str.upcase] || timezone_str
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private_class_method def self.parse_now
|
|
95
|
+
format_time(Time.now)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private_class_method def self.parse_relative_time(match)
|
|
99
|
+
sign, amount, unit = match.captures
|
|
100
|
+
amount = amount.to_i
|
|
101
|
+
amount = -amount if sign == '-'
|
|
102
|
+
|
|
103
|
+
seconds_delta = amount * UNITS[unit]
|
|
104
|
+
target_time = Time.now + seconds_delta
|
|
105
|
+
|
|
106
|
+
format_time(target_time)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private_class_method def self.parse_unix_timestamp(timestamp)
|
|
110
|
+
timestamp_int = timestamp.to_i
|
|
111
|
+
|
|
112
|
+
# Handle millisecond timestamps (13 digits) - convert to seconds
|
|
113
|
+
timestamp_int /= 1000 if timestamp.to_s.length == 13
|
|
114
|
+
|
|
115
|
+
# Validate reasonable range (between year 2000 and 2100)
|
|
116
|
+
min_timestamp = 946_684_800 # 2000-01-01
|
|
117
|
+
max_timestamp = 4_102_444_800 # 2100-01-01
|
|
118
|
+
|
|
119
|
+
unless timestamp_int.between?(min_timestamp, max_timestamp)
|
|
120
|
+
raise ParseError, "Unix timestamp out of reasonable range: #{timestamp}"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
time = Time.at(timestamp_int).utc
|
|
124
|
+
format_time(time)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private_class_method def self.unix_timestamp?(value)
|
|
128
|
+
# Check if it's an integer or a string that looks like a Unix timestamp
|
|
129
|
+
# Unix timestamps are typically 10 digits (seconds) or 13 digits (milliseconds)
|
|
130
|
+
return true if value.is_a?(Integer) && value.to_s.length.between?(10, 13)
|
|
131
|
+
|
|
132
|
+
if value.is_a?(String)
|
|
133
|
+
# Must be all digits, and between 10-13 characters
|
|
134
|
+
return value.match?(/^\d{10,13}$/)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
false
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private_class_method def self.format_time(time)
|
|
141
|
+
# Format as ISO 8601 without timezone suffix
|
|
142
|
+
# Sumo Logic API expects format like "2025-11-13T14:00:00"
|
|
143
|
+
time.utc.strftime('%Y-%m-%dT%H:%M:%S')
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
data/lib/sumologic/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sumologic-query
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- patrick204nqh
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-11-
|
|
11
|
+
date: 2025-11-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -123,6 +123,7 @@ files:
|
|
|
123
123
|
- lib/sumologic/search/job.rb
|
|
124
124
|
- lib/sumologic/search/message_fetcher.rb
|
|
125
125
|
- lib/sumologic/search/poller.rb
|
|
126
|
+
- lib/sumologic/utils/time_parser.rb
|
|
126
127
|
- lib/sumologic/utils/worker.rb
|
|
127
128
|
- lib/sumologic/version.rb
|
|
128
129
|
homepage: https://github.com/patrick204nqh/sumologic-query
|