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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f674ac09efecc2c167df4d3853be9d9bd5c1c043bf6e0d574f068770e9979a8b
4
- data.tar.gz: 4e43daf71638156ad4ac9f72b862daf501f4714037b3cee603aac1a6e8de1efb
3
+ metadata.gz: 72c4c0c9c57655df15506b9be19a21de47398ba2a2aedaae8e8d22d3efbd0873
4
+ data.tar.gz: 9cd38107b915187cb699e32305c8af8e6a970c2ea24c2b2340a443cc0a0e2637
5
5
  SHA512:
6
- metadata.gz: cc1c1a801d4c0dcf282ae281cc4da00eb125095b036f45489cda7c74638e4116fde5ad5c317896fb8e05988fd143887d7b470f31d02fba10426cc3c3c7bd78a0
7
- data.tar.gz: 3b360acf874598ea9ca2513d1ddd507c617e970b94f79efb5029fc3a2e37e807b1f8aac2e3e9cbe95793ef0d74f210fd2ab13e9d7d3f0d25fad446014f6f46cb
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
- # Or use the shorthand
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
- results.each do |message|
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
- **See [docs/api-reference.md](docs/api-reference.md) for complete API documentation.**
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
- Use ISO 8601 format for timestamps:
230
+ Multiple time formats are supported:
201
231
 
202
232
  ```bash
203
- # UTC timestamps (default)
204
- --from "2025-11-13T14:30:00" --to "2025-11-13T15:00:00"
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
- # With timezone
207
- --from "2025-11-13T14:30:00" --time-zone "America/New_York"
240
+ # Unix timestamps
241
+ sumo-query search -q 'error' -f '1700000000' -t 'now'
208
242
 
209
- # Using shell helpers
210
- --from "$(date -u -v-1H '+%Y-%m-%dT%H:%M:%S')" # 1 hour ago
211
- --to "$(date -u '+%Y-%m-%dT%H:%M:%S')" # now
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 in tldr format
257
- - **[Query Examples](examples/queries.md)** - Common query patterns and use cases
258
- - **[API Reference](docs/api-reference.md)** - Complete Ruby library documentation
259
- - **[Architecture](docs/architecture/)** - System design and architecture decisions
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 "2025-11-13T14:00:00" \
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: #{options[:from]} to #{options[:to]}"
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: options[:from],
41
- to_time: options[:to],
42
- time_zone: options[: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: options[:from],
58
- to: options[:to],
59
- time_zone: options[:time_zone],
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' => options[:from],
79
- 'to' => options[:to],
80
- 'time_zone' => options[: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
- # Error timeline with 5-minute buckets
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
- # Search for specific text
44
+ # Last 7 days
35
45
  sumo-query search --query '"connection timeout"' \\
36
- --from '2025-11-13T14:00:00' --to '2025-11-13T15:00:00' \\
37
- --limit 100
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 '2025-11-13T14:00:00' --to '2025-11-13T15:00:00' \\
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', desc: 'Time zone'
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sumologic
4
- VERSION = '1.3.3'
4
+ VERSION = '1.3.4'
5
5
  end
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.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-17 00:00:00.000000000 Z
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