sumologic-query 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e4adfba9fe6b1417e51cfcd3e1b5697a8ed208687ae17e8a114716c71711cdc0
4
+ data.tar.gz: 331efc0e06d844ea82fada1bf97d5a4183189b9330437e5aab03a8d145a30e57
5
+ SHA512:
6
+ metadata.gz: bacec0ea516f5d0d8d5ba06ffe70887aefb9e4dc8df6fda259d21817d37ecbece1ed57f0806e3c61f1e3a5bc934c1adfb779048e1b57c3ba1ac121b6bb7a2392
7
+ data.tar.gz: 0f6af034374a1b3f417437a8cb691246393161be4f1b194441afc61128c1bf75c239f537a90aab883ed724e84add224c393437a23ef0d3cd23e2596f01f2c665
data/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2025-11-13
9
+
10
+ ### Added
11
+ - Initial release of sumologic-query CLI tool
12
+ - Core `Sumologic::Client` class for Search Job API
13
+ - Command-line interface with query, time range, and output options
14
+ - Automatic job polling with 20-second intervals
15
+ - Automatic pagination for large result sets (10K messages per request)
16
+ - Support for multiple Sumo Logic deployments (us1, us2, eu, au)
17
+ - Environment variable configuration (SUMO_ACCESS_ID, SUMO_ACCESS_KEY, SUMO_DEPLOYMENT)
18
+ - Debug mode for troubleshooting (SUMO_DEBUG)
19
+ - JSON output format with metadata
20
+ - Zero external dependencies (stdlib only)
21
+ - Comprehensive error handling and user-friendly messages
22
+ - MIT license
23
+ - Complete documentation and examples
24
+
25
+ ### Features
26
+ - Query historical logs via Search Job API
27
+ - Time range filtering (ISO 8601 format)
28
+ - Message limiting
29
+ - Timezone support
30
+ - File or stdout output
31
+ - 5-minute default timeout
32
+ - Graceful cleanup of search jobs
33
+
34
+ [1.0.0]: https://github.com/patrick204nqh/sumologic-query/releases/tag/v1.0.0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 patrick204nqh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,368 @@
1
+ # Sumo Logic Query Tool
2
+
3
+ > A lightweight Ruby CLI for querying Sumo Logic logs quickly. Simple, fast, read-only access to your logs.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/sumologic-query.svg)](https://badge.fury.io/rb/sumologic-query)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Why This Tool?
9
+
10
+ - **Zero dependencies**: Uses only Ruby stdlib - no external gems required
11
+ - **Fast queries**: Efficient polling and automatic pagination
12
+ - **Simple interface**: Just query, get results, done
13
+ - **Read-only**: No write operations, perfect for safe log access
14
+ - **Lightweight**: ~300 lines of code total
15
+
16
+ All existing Ruby Sumo Logic gems are unmaintained (2-9 years dormant). This tool provides a fresh, minimal approach focused solely on querying logs.
17
+
18
+ ## Installation
19
+
20
+ ### Via RubyGems
21
+
22
+ ```bash
23
+ gem install sumologic-query
24
+ ```
25
+
26
+ ### Via Homebrew
27
+
28
+ ```bash
29
+ brew tap patrick204nqh/tap
30
+ brew install sumologic-query
31
+ ```
32
+
33
+ ### From Source
34
+
35
+ ```bash
36
+ git clone https://github.com/patrick204nqh/sumologic-query.git
37
+ cd sumologic-query
38
+ bundle install
39
+ bundle exec rake install
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ ### 1. Set Up Credentials
45
+
46
+ Export your Sumo Logic API credentials:
47
+
48
+ ```bash
49
+ export SUMO_ACCESS_ID="your_access_id"
50
+ export SUMO_ACCESS_KEY="your_access_key"
51
+ export SUMO_DEPLOYMENT="us2" # Optional: us1, us2 (default), eu, au
52
+ ```
53
+
54
+ **Getting credentials:**
55
+ 1. Log in to Sumo Logic
56
+ 2. Go to **Administration → Security → Access Keys**
57
+ 3. Create a new access key or use existing
58
+ 4. Copy the Access ID and Access Key
59
+
60
+ ### 2. Run Your First Query
61
+
62
+ ```bash
63
+ sumo-query --query 'error' \
64
+ --from '2025-11-13T14:00:00' \
65
+ --to '2025-11-13T15:00:00' \
66
+ --limit 10
67
+ ```
68
+
69
+ ## Usage
70
+
71
+ ### Basic Command Structure
72
+
73
+ ```bash
74
+ sumo-query --query "YOUR_QUERY" \
75
+ --from "START_TIME" \
76
+ --to "END_TIME" \
77
+ [--output FILE] \
78
+ [--limit N] \
79
+ [--time-zone TZ]
80
+ ```
81
+
82
+ ### Required Options
83
+
84
+ - `-q, --query QUERY` - Sumo Logic query string
85
+ - `-f, --from TIME` - Start time in ISO 8601 format
86
+ - `-t, --to TIME` - End time in ISO 8601 format
87
+
88
+ ### Optional Options
89
+
90
+ - `-z, --time-zone TZ` - Time zone (default: UTC)
91
+ - `-l, --limit N` - Limit number of messages
92
+ - `-o, --output FILE` - Save results to file (default: stdout)
93
+ - `-d, --debug` - Enable debug output
94
+ - `-h, --help` - Show help message
95
+ - `-v, --version` - Show version
96
+
97
+ ## Common Query Patterns
98
+
99
+ ### Error Analysis
100
+
101
+ ```bash
102
+ # Find all errors in a time window
103
+ sumo-query --query 'error' \
104
+ --from '2025-11-13T14:00:00' \
105
+ --to '2025-11-13T15:00:00' \
106
+ --output errors.json
107
+
108
+ # Error timeline with 5-minute buckets
109
+ sumo-query --query 'error | timeslice 5m | count by _timeslice' \
110
+ --from '2025-11-13T14:00:00' \
111
+ --to '2025-11-13T15:00:00'
112
+ ```
113
+
114
+ ### Text Search
115
+
116
+ ```bash
117
+ # Search for specific text
118
+ sumo-query --query '"connection timeout"' \
119
+ --from '2025-11-13T14:00:00' \
120
+ --to '2025-11-13T15:00:00'
121
+
122
+ # Case-insensitive search
123
+ sumo-query --query 'timeout OR failure OR exception' \
124
+ --from '2025-11-13T14:00:00' \
125
+ --to '2025-11-13T15:00:00'
126
+ ```
127
+
128
+ ### Filtering by Source
129
+
130
+ ```bash
131
+ # Filter by source category
132
+ sumo-query --query '_sourceCategory=prod/api error' \
133
+ --from '2025-11-13T14:00:00' \
134
+ --to '2025-11-13T15:00:00'
135
+
136
+ # Multiple sources
137
+ sumo-query --query '(_sourceCategory=prod/api OR _sourceCategory=prod/web) AND error' \
138
+ --from '2025-11-13T14:00:00' \
139
+ --to '2025-11-13T15:00:00'
140
+ ```
141
+
142
+ ### Aggregation Queries
143
+
144
+ ```bash
145
+ # Count by field
146
+ sumo-query --query '* | count by status_code' \
147
+ --from '2025-11-13T14:00:00' \
148
+ --to '2025-11-13T15:00:00'
149
+
150
+ # Top 10 slowest requests
151
+ sumo-query --query 'duration_ms > 1000 | sort by duration_ms desc | limit 10' \
152
+ --from '2025-11-13T14:00:00' \
153
+ --to '2025-11-13T15:00:00'
154
+ ```
155
+
156
+ ### Parsing and Field Extraction
157
+
158
+ ```bash
159
+ # Parse specific fields
160
+ sumo-query --query '* | parse "user_id=* " as user_id | count by user_id' \
161
+ --from '2025-11-13T14:00:00' \
162
+ --to '2025-11-13T15:00:00'
163
+ ```
164
+
165
+ ## Output Format
166
+
167
+ Results are returned as JSON:
168
+
169
+ ```json
170
+ {
171
+ "query": "error",
172
+ "from": "2025-11-13T14:00:00",
173
+ "to": "2025-11-13T15:00:00",
174
+ "time_zone": "UTC",
175
+ "message_count": 42,
176
+ "messages": [
177
+ {
178
+ "map": {
179
+ "_messagetime": "1731506400123",
180
+ "_sourceCategory": "prod/api",
181
+ "_sourceName": "api-server-01",
182
+ "message": "Error processing request: timeout"
183
+ }
184
+ }
185
+ ]
186
+ }
187
+ ```
188
+
189
+ ## Time Formats
190
+
191
+ Use ISO 8601 format for timestamps:
192
+
193
+ ```bash
194
+ # UTC timestamps (default)
195
+ --from "2025-11-13T14:30:00"
196
+ --to "2025-11-13T15:00:00"
197
+
198
+ # With timezone
199
+ --from "2025-11-13T14:30:00" --time-zone "America/New_York"
200
+
201
+ # Alternative: Relative times (in your shell)
202
+ --from "$(date -u -v-1H '+%Y-%m-%dT%H:%M:%S')" # 1 hour ago
203
+ --to "$(date -u '+%Y-%m-%dT%H:%M:%S')" # now
204
+ ```
205
+
206
+ ## Performance
207
+
208
+ Query execution time depends on data volume:
209
+
210
+ - **Small queries** (<10K messages): ~30-60 seconds
211
+ - **Medium queries** (10K-100K): ~1-2 minutes
212
+ - **Large queries** (100K+): ~2-5 minutes
213
+
214
+ Default timeout: 5 minutes
215
+
216
+ To improve performance:
217
+ - Narrow your time range
218
+ - Add specific `_sourceCategory` filters
219
+ - Use `--limit` to cap results
220
+ - Use aggregation queries instead of fetching raw messages
221
+
222
+ ## Troubleshooting
223
+
224
+ ### Authentication Error
225
+
226
+ ```
227
+ Error: SUMO_ACCESS_ID not set
228
+ ```
229
+
230
+ **Solution**: Export your credentials:
231
+ ```bash
232
+ export SUMO_ACCESS_ID="your_access_id"
233
+ export SUMO_ACCESS_KEY="your_access_key"
234
+ ```
235
+
236
+ ### Timeout Error
237
+
238
+ ```
239
+ Timeout Error: Search job timed out after 300 seconds
240
+ ```
241
+
242
+ **Solutions**:
243
+ - Reduce time range
244
+ - Add more specific filters (`_sourceCategory`, `_sourceName`)
245
+ - Use `--limit` to cap results
246
+ - Consider using aggregation instead of raw messages
247
+
248
+ ### Empty Results
249
+
250
+ ```json
251
+ {
252
+ "message_count": 0,
253
+ "messages": []
254
+ }
255
+ ```
256
+
257
+ **Check**:
258
+ - Time range matches your expected data
259
+ - Query syntax is valid (test in Sumo Logic UI first)
260
+ - Source categories are correct
261
+ - Time zone is correct (default is UTC)
262
+
263
+ ### Rate Limit Error
264
+
265
+ ```
266
+ HTTP 429: Rate limit exceeded
267
+ ```
268
+
269
+ **Solution**: Wait 1-2 minutes between queries. Sumo Logic enforces rate limits per account.
270
+
271
+ ## Development
272
+
273
+ ### Running Tests
274
+
275
+ ```bash
276
+ bundle install
277
+ bundle exec rake spec
278
+ ```
279
+
280
+ ### Code Quality
281
+
282
+ ```bash
283
+ bundle exec rubocop
284
+ bundle exec rubocop -A # Auto-fix issues
285
+ ```
286
+
287
+ ### Running Locally
288
+
289
+ ```bash
290
+ # Without installing
291
+ bundle exec bin/sumo-query --query "error" \
292
+ --from "2025-11-13T14:00:00" \
293
+ --to "2025-11-13T15:00:00"
294
+
295
+ # With debug output
296
+ SUMO_DEBUG=1 bundle exec bin/sumo-query --query "error" \
297
+ --from "2025-11-13T14:00:00" \
298
+ --to "2025-11-13T15:00:00"
299
+ ```
300
+
301
+ ## How It Works
302
+
303
+ This tool uses the Sumo Logic Search Job API:
304
+
305
+ 1. **Create Job** - POST to `/api/v1/search/jobs` with your query
306
+ 2. **Poll Status** - GET `/api/v1/search/jobs/:id` every 20 seconds until complete
307
+ 3. **Fetch Messages** - GET `/api/v1/search/jobs/:id/messages` (automatically paginated)
308
+ 4. **Clean Up** - DELETE `/api/v1/search/jobs/:id`
309
+
310
+ All steps are handled automatically. You just provide the query and get results.
311
+
312
+ ## API Reference
313
+
314
+ ### Ruby Library Usage
315
+
316
+ You can also use the library directly in your Ruby code:
317
+
318
+ ```ruby
319
+ require 'sumologic'
320
+
321
+ client = Sumologic::Client.new(
322
+ access_id: 'your_access_id',
323
+ access_key: 'your_access_key',
324
+ deployment: 'us2'
325
+ )
326
+
327
+ results = client.search(
328
+ query: 'error',
329
+ from_time: '2025-11-13T14:00:00',
330
+ to_time: '2025-11-13T15:00:00',
331
+ time_zone: 'UTC',
332
+ limit: 1000
333
+ )
334
+
335
+ results.each do |message|
336
+ puts message['map']['message']
337
+ end
338
+ ```
339
+
340
+ ## Contributing
341
+
342
+ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
343
+
344
+ 1. Fork the repository
345
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
346
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
347
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
348
+ 5. Open a Pull Request
349
+
350
+ ## License
351
+
352
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
353
+
354
+ ## Resources
355
+
356
+ - **Sumo Logic API Docs**: https://help.sumologic.com/docs/api/search-job/
357
+ - **Query Language**: https://help.sumologic.com/docs/search/
358
+ - **Bug Reports**: https://github.com/patrick204nqh/sumologic-query/issues
359
+ - **Feature Requests**: https://github.com/patrick204nqh/sumologic-query/issues
360
+
361
+ ## Support
362
+
363
+ - **Issues**: [GitHub Issues](https://github.com/patrick204nqh/sumologic-query/issues)
364
+ - **Discussions**: [GitHub Discussions](https://github.com/patrick204nqh/sumologic-query/discussions)
365
+
366
+ ---
367
+
368
+ **Note**: This tool provides read-only access to Sumo Logic logs. It does not modify any data or configuration.
data/bin/sumo-query ADDED
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Simple CLI wrapper for querying Sumo Logic logs
5
+ # Usage: sumo-query --query "error" --from "2025-11-13T14:00:00" --to "2025-11-13T15:00:00"
6
+
7
+ require_relative '../lib/sumologic'
8
+ require 'optparse'
9
+ require 'json'
10
+
11
+ options = {
12
+ time_zone: 'UTC'
13
+ }
14
+
15
+ OptionParser.new do |opts|
16
+ opts.banner = <<~BANNER
17
+ Sumo Logic Query Tool - Fast, read-only log access
18
+
19
+ Usage: sumo-query [options]
20
+
21
+ Examples:
22
+ # Error timeline with 5-minute buckets
23
+ sumo-query --query 'error | timeslice 5m | count' \\
24
+ --from '2025-11-13T14:00:00' --to '2025-11-13T15:00:00'
25
+
26
+ # Search for specific text
27
+ sumo-query --query '"connection timeout"' \\
28
+ --from '2025-11-13T14:00:00' --to '2025-11-13T15:00:00' \\
29
+ --limit 100
30
+
31
+ # Filter by source category
32
+ sumo-query --query '_sourceCategory=prod/api error' \\
33
+ --from '2025-11-13T14:00:00' --to '2025-11-13T15:00:00' \\
34
+ --output results.json
35
+
36
+ Options:
37
+ BANNER
38
+
39
+ opts.on('-q', '--query QUERY', 'Search query (required)') { |v| options[:query] = v }
40
+ opts.on('-f', '--from TIME', 'Start time in ISO 8601 format (required)') { |v| options[:from] = v }
41
+ opts.on('-t', '--to TIME', 'End time in ISO 8601 format (required)') { |v| options[:to] = v }
42
+ opts.on('-z', '--time-zone TZ', 'Time zone (default: UTC)') { |v| options[:time_zone] = v }
43
+ opts.on('-l', '--limit N', Integer, 'Limit number of messages') { |v| options[:limit] = v }
44
+ opts.on('-o', '--output FILE', 'Output file (default: stdout)') { |v| options[:output] = v }
45
+ opts.on('-d', '--debug', 'Enable debug output') { $DEBUG = true }
46
+ opts.on('-h', '--help', 'Show this help') do
47
+ puts opts
48
+ exit
49
+ end
50
+ opts.on('-v', '--version', 'Show version') do
51
+ puts "sumologic-query v#{Sumologic::VERSION}"
52
+ exit
53
+ end
54
+ end.parse!
55
+
56
+ # Validate required options
57
+ unless options[:query] && options[:from] && options[:to]
58
+ warn 'Error: --query, --from, and --to are required'
59
+ warn 'Run with --help for usage information'
60
+ exit 1
61
+ end
62
+
63
+ begin
64
+ # Create client
65
+ client = Sumologic::Client.new
66
+
67
+ warn "Querying Sumo Logic: #{options[:from]} to #{options[:to]}"
68
+ warn "Query: #{options[:query]}"
69
+ warn 'This may take 1-3 minutes depending on data volume...'
70
+ $stderr.puts
71
+
72
+ # Execute search
73
+ results = client.search(
74
+ query: options[:query],
75
+ from_time: options[:from],
76
+ to_time: options[:to],
77
+ time_zone: options[:time_zone],
78
+ limit: options[:limit]
79
+ )
80
+
81
+ # Format output
82
+ output = {
83
+ query: options[:query],
84
+ from: options[:from],
85
+ to: options[:to],
86
+ time_zone: options[:time_zone],
87
+ message_count: results.size,
88
+ messages: results
89
+ }
90
+
91
+ json_output = JSON.pretty_generate(output)
92
+
93
+ # Write to file or stdout
94
+ if options[:output]
95
+ File.write(options[:output], json_output)
96
+ warn "\nResults saved to: #{options[:output]}"
97
+ warn "Message count: #{results.size}"
98
+ else
99
+ puts json_output
100
+ end
101
+ rescue Sumologic::AuthenticationError => e
102
+ warn "\nAuthentication Error: #{e.message}"
103
+ warn "\nPlease set environment variables:"
104
+ warn " export SUMO_ACCESS_ID='your_access_id'"
105
+ warn " export SUMO_ACCESS_KEY='your_access_key'"
106
+ warn " export SUMO_DEPLOYMENT='us2' # Optional, defaults to us2"
107
+ exit 1
108
+ rescue Sumologic::TimeoutError => e
109
+ warn "\nTimeout Error: #{e.message}"
110
+ warn "\nTry:"
111
+ warn ' - Reducing time range'
112
+ warn ' - Using more specific filters in your query'
113
+ warn ' - Adding --limit to cap results'
114
+ exit 1
115
+ rescue Sumologic::Error => e
116
+ warn "\nError: #{e.message}"
117
+ exit 1
118
+ rescue Interrupt
119
+ warn "\nInterrupted by user"
120
+ exit 130
121
+ end
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'uri'
6
+ require 'base64'
7
+
8
+ module Sumologic
9
+ # Lightweight Sumo Logic Search Job API client
10
+ # Handles historical log queries with automatic polling and pagination
11
+ class Client
12
+ API_VERSION = 'v1'
13
+ DEFAULT_POLL_INTERVAL = 20 # seconds
14
+ DEFAULT_TIMEOUT = 300 # seconds (5 minutes)
15
+ MAX_MESSAGES_PER_REQUEST = 10_000
16
+
17
+ attr_reader :access_id, :access_key, :deployment, :base_url
18
+
19
+ def initialize(access_id: nil, access_key: nil, deployment: nil)
20
+ @access_id = access_id || ENV.fetch('SUMO_ACCESS_ID', nil)
21
+ @access_key = access_key || ENV.fetch('SUMO_ACCESS_KEY', nil)
22
+ @deployment = deployment || ENV['SUMO_DEPLOYMENT'] || 'us2'
23
+ @base_url = deployment_url(@deployment)
24
+
25
+ validate_credentials!
26
+ end
27
+
28
+ # Main search method
29
+ # Returns array of messages/records as JSON
30
+ def search(query:, from_time:, to_time:, time_zone: 'UTC', limit: nil)
31
+ job_id = create_job(query, from_time, to_time, time_zone)
32
+ poll_until_complete(job_id)
33
+ messages = fetch_all_messages(job_id, limit)
34
+ delete_job(job_id)
35
+ messages
36
+ rescue StandardError => e
37
+ delete_job(job_id) if job_id
38
+ raise Error, "Search failed: #{e.message}"
39
+ end
40
+
41
+ private
42
+
43
+ def validate_credentials!
44
+ raise AuthenticationError, 'SUMO_ACCESS_ID not set' unless @access_id
45
+ raise AuthenticationError, 'SUMO_ACCESS_KEY not set' unless @access_key
46
+ end
47
+
48
+ def deployment_url(deployment)
49
+ case deployment
50
+ when /^http/
51
+ deployment # Full URL provided
52
+ when 'us1'
53
+ 'https://api.sumologic.com/api/v1'
54
+ when 'us2'
55
+ 'https://api.us2.sumologic.com/api/v1'
56
+ when 'eu'
57
+ 'https://api.eu.sumologic.com/api/v1'
58
+ when 'au'
59
+ 'https://api.au.sumologic.com/api/v1'
60
+ else
61
+ "https://api.#{deployment}.sumologic.com/api/v1"
62
+ end
63
+ end
64
+
65
+ def auth_header
66
+ encoded = Base64.strict_encode64("#{@access_id}:#{@access_key}")
67
+ "Basic #{encoded}"
68
+ end
69
+
70
+ def create_job(query, from_time, to_time, time_zone)
71
+ uri = URI("#{@base_url}/search/jobs")
72
+ request = Net::HTTP::Post.new(uri)
73
+ request['Authorization'] = auth_header
74
+ request['Content-Type'] = 'application/json'
75
+ request['Accept'] = 'application/json'
76
+
77
+ body = {
78
+ query: query,
79
+ from: from_time,
80
+ to: to_time,
81
+ timeZone: time_zone
82
+ }
83
+ request.body = body.to_json
84
+
85
+ response = http_request(uri, request)
86
+ data = JSON.parse(response.body)
87
+
88
+ raise Error, "Failed to create job: #{data['message']}" unless data['id']
89
+
90
+ log_info "Created search job: #{data['id']}"
91
+ data['id']
92
+ end
93
+
94
+ def poll_until_complete(job_id, timeout: DEFAULT_TIMEOUT)
95
+ uri = URI("#{@base_url}/search/jobs/#{job_id}")
96
+ start_time = Time.now
97
+ interval = DEFAULT_POLL_INTERVAL
98
+
99
+ loop do
100
+ raise TimeoutError, "Search job timed out after #{timeout} seconds" if Time.now - start_time > timeout
101
+
102
+ request = Net::HTTP::Get.new(uri)
103
+ request['Authorization'] = auth_header
104
+ request['Accept'] = 'application/json'
105
+
106
+ response = http_request(uri, request)
107
+ data = JSON.parse(response.body)
108
+
109
+ state = data['state']
110
+ log_info "Job state: #{state} (#{data['messageCount']} messages, #{data['recordCount']} records)"
111
+
112
+ case state
113
+ when 'DONE GATHERING RESULTS'
114
+ return data
115
+ when 'CANCELLED', 'FORCE PAUSED'
116
+ raise Error, "Search job #{state.downcase}"
117
+ end
118
+
119
+ sleep interval
120
+ end
121
+ end
122
+
123
+ def fetch_all_messages(job_id, limit = nil)
124
+ messages = []
125
+ offset = 0
126
+ total_fetched = 0
127
+
128
+ loop do
129
+ batch_limit = if limit
130
+ [MAX_MESSAGES_PER_REQUEST, limit - total_fetched].min
131
+ else
132
+ MAX_MESSAGES_PER_REQUEST
133
+ end
134
+
135
+ break if batch_limit <= 0
136
+
137
+ uri = URI("#{@base_url}/search/jobs/#{job_id}/messages")
138
+ uri.query = URI.encode_www_form(offset: offset, limit: batch_limit)
139
+
140
+ request = Net::HTTP::Get.new(uri)
141
+ request['Authorization'] = auth_header
142
+ request['Accept'] = 'application/json'
143
+
144
+ response = http_request(uri, request)
145
+ data = JSON.parse(response.body)
146
+
147
+ batch = data['messages'] || []
148
+ messages.concat(batch)
149
+ total_fetched += batch.size
150
+
151
+ log_info "Fetched #{batch.size} messages (total: #{total_fetched})"
152
+
153
+ break if batch.size < batch_limit # No more messages
154
+ break if limit && total_fetched >= limit
155
+
156
+ offset += batch.size
157
+ end
158
+
159
+ messages
160
+ end
161
+
162
+ def delete_job(job_id)
163
+ return unless job_id
164
+
165
+ uri = URI("#{@base_url}/search/jobs/#{job_id}")
166
+ request = Net::HTTP::Delete.new(uri)
167
+ request['Authorization'] = auth_header
168
+
169
+ http_request(uri, request)
170
+ log_info "Deleted search job: #{job_id}"
171
+ rescue StandardError => e
172
+ log_error "Failed to delete job #{job_id}: #{e.message}"
173
+ end
174
+
175
+ def http_request(uri, request)
176
+ http = Net::HTTP.new(uri.host, uri.port)
177
+ http.use_ssl = true
178
+ http.read_timeout = 60
179
+ http.open_timeout = 10
180
+
181
+ response = http.request(request)
182
+
183
+ case response.code.to_i
184
+ when 200..299
185
+ response
186
+ when 401, 403
187
+ raise AuthenticationError, "Authentication failed: #{response.body}"
188
+ when 429
189
+ raise Error, "Rate limit exceeded: #{response.body}"
190
+ else
191
+ raise Error, "HTTP #{response.code}: #{response.body}"
192
+ end
193
+ end
194
+
195
+ def log_info(message)
196
+ warn "[Sumologic::Client] #{message}" if ENV['SUMO_DEBUG'] || $DEBUG
197
+ end
198
+
199
+ def log_error(message)
200
+ warn "[Sumologic::Client ERROR] #{message}"
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sumologic
4
+ VERSION = '1.0.0'
5
+ end
data/lib/sumologic.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'sumologic/version'
4
+ require_relative 'sumologic/client'
5
+
6
+ module Sumologic
7
+ class Error < StandardError; end
8
+ class TimeoutError < Error; end
9
+ class AuthenticationError < Error; end
10
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sumologic-query
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - patrick204nqh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-11-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base64
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.21'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.21'
69
+ description: |
70
+ Simple, fast, read-only access to Sumo Logic logs via the Search Job API.
71
+ No complex features, just quick log queries with automatic pagination and polling.
72
+ Perfect for DevOps, incident investigation, and log analysis workflows.
73
+ email:
74
+ - patrick204nqh@gmail.com
75
+ executables:
76
+ - sumo-query
77
+ extensions: []
78
+ extra_rdoc_files: []
79
+ files:
80
+ - CHANGELOG.md
81
+ - LICENSE
82
+ - README.md
83
+ - bin/sumo-query
84
+ - lib/sumologic.rb
85
+ - lib/sumologic/client.rb
86
+ - lib/sumologic/version.rb
87
+ homepage: https://github.com/patrick204nqh/sumologic-query
88
+ licenses:
89
+ - MIT
90
+ metadata:
91
+ homepage_uri: https://github.com/patrick204nqh/sumologic-query
92
+ source_code_uri: https://github.com/patrick204nqh/sumologic-query
93
+ bug_tracker_uri: https://github.com/patrick204nqh/sumologic-query/issues
94
+ changelog_uri: https://github.com/patrick204nqh/sumologic-query/blob/main/CHANGELOG.md
95
+ rubygems_mfa_required: 'true'
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: 2.7.0
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubygems_version: 3.5.22
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: A lightweight Ruby CLI for querying Sumo Logic logs quickly
115
+ test_files: []