Skip to content

Commit 80e71ed

Browse files
committed
Update spec and steering.
1 parent 38e50d8 commit 80e71ed

File tree

3 files changed

+60
-211
lines changed

3 files changed

+60
-211
lines changed

.doc_gen/metadata/cloudwatch-logs_metadata.yaml

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -314,18 +314,6 @@ cloudwatch-logs_GetQueryResults:
314314
- python.example_code.cloudwatch_logs.get_query_results
315315
services:
316316
cloudwatch-logs: {GetQueryResults}
317-
cloudwatch-logs_PutLogEvents:
318-
languages:
319-
.NET:
320-
versions:
321-
- sdk_version: 4
322-
github: dotnetv4/CloudWatchLogs/LargeQuery
323-
excerpts:
324-
- description:
325-
snippet_tags:
326-
- CloudWatchLogs.dotnetv4.PutLogEvents
327-
services:
328-
cloudwatch-logs: {PutLogEvents}
329317
cloudwatch-logs_StartQuery:
330318
languages:
331319
.NET:

scenarios/features/cloudwatch_logs_large_query/SPECIFICATION.md

Lines changed: 59 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ This feature scenario demonstrates how to perform large-scale queries on Amazon
1111
3. Performing recursive queries to retrieve all logs using binary search
1212
4. Cleaning up all resources
1313

14-
**The scenario must be runnable in both interactive and non-interactive modes** to support:
15-
- Interactive mode: User runs the scenario manually with prompts
16-
- Non-interactive mode: Automated integration tests run the scenario without user input
17-
1814
For an introduction, see the [README.md](README.md).
1915

2016
---
@@ -23,8 +19,9 @@ For an introduction, see the [README.md](README.md).
2319

2420
- [API Actions Used](#api-actions-used)
2521
- [Resources](#resources)
26-
- [Proposed Example Structure](#proposed-example-structure)
27-
- [Implementation Details](#implementation-details)
22+
- [Variables](#variables)
23+
- [Building the queries](#building-the-queries)
24+
- [Example Structure](#example-structure)
2825
- [Output Format](#output-format)
2926
- [Errors](#errors)
3027
- [Metadata](#metadata)
@@ -38,12 +35,6 @@ This scenario uses the following CloudWatch Logs API actions:
3835
- `StartQuery` - Initiates a CloudWatch Logs Insights query
3936
- `GetQueryResults` - Retrieves results from a query, polling until complete
4037

41-
This scenario uses the following CloudFormation API actions:
42-
43-
- `CreateStack` - Deploys the CloudFormation template
44-
- `DescribeStacks` - Checks stack status and retrieves outputs
45-
- `DeleteStack` - Removes the CloudFormation stack
46-
4738
---
4839

4940
## Resources
@@ -56,37 +47,69 @@ This scenario uses the following CloudFormation API actions:
5647
- CloudWatch Logs Log Group: `/workflows/cloudwatch-logs/large-query`
5748
- CloudWatch Logs Log Stream: `stream1`
5849

59-
**Stack Outputs**: None (resources use fixed names)
50+
### Helper files
51+
These files are for reference only. New versions of this example should create and upload logs as part of the scenario.
52+
53+
- [put-log-events](resources/put-log-events.sh) is a bash script that ingests log data and uploads it to CloudWatch.
54+
- [make-log-files.sh](resources/make-log-files.sh) is a bash script that creates log data. **Five minutes of logs, starting at the time of execution, will be created. Wait at least five minutes after running this script before attempting to query.**
55+
---
6056

61-
### Sample Data Generation Scripts
57+
## Variables
6258

63-
**Script 1**: `scenarios/features/cloudwatch_logs_large_query/resources/make-log-files.sh`
64-
- Creates 50,000 log entries divided into 5 JSON files (10,000 entries each)
65-
- Generates timestamps spanning 5 minutes from execution time
66-
- Outputs `QUERY_START_DATE` and `QUERY_END_DATE` environment variables
67-
- Creates files: `file1.json`, `file2.json`, `file3.json`, `file4.json`, `file5.json`
59+
| Variable Name | Description | Type | Default |
60+
|--------------|-------------|------|---------|
61+
| `stackName` | CloudFormation stack name | String | "CloudWatchLargeQueryStack" |
62+
| `queryStartDate` | Query start timestamp | Long/Integer | From script output |
63+
| `queryEndDate` | Query end timestamp | Long/Integer | From script output |
64+
| `queryLimit` | Maximum results per query | Integer | 10000 |
65+
| `logGroupName` | Log group name (if not using stack) | String | "/workflows/cloudwatch-logs/large-query" |
66+
| `logStreamName` | Log stream name (if not using stack) | String | "stream1" |
6867

69-
**Script 2**: `scenarios/features/cloudwatch_logs_large_query/resources/put-log-events.sh`
70-
- Uploads the generated JSON files to CloudWatch Logs
71-
- Uses AWS CLI `put-log-events` command
72-
- Targets log group: `/workflows/cloudwatch-logs/large-query`
73-
- Targets log stream: `stream1`
68+
---
7469

75-
**Python Alternative**: `scenarios/features/cloudwatch_logs_large_query/resources/create_logs.py`
76-
- Python script that combines both generation and upload
77-
- Creates 50,000 log entries and uploads them directly
78-
- Returns start and end timestamps for query configuration
79-
- Preferred for cross-platform compatibility
70+
## Building the queries
71+
72+
### Building and waiting for single query
73+
74+
The query itself is a "CloudWatch Logs Insights query syntax" string. The query must return the `@timestamp` field so follow-up queries can use that information. Here's a sample query string: `fields @timestamp, @message | sort @timestamp asc`. Notice it sorts in ascending order. You can sort in either `asc` or `desc`, but the recursive strategy described later will need to match accordingly.
75+
76+
Queries are jobs. You can start a query with `StartQuery`, but it immediately returns the `queryId`. You must poll a query using `GetQueryResults` until the query has finished. For the purpose of this example, a query has "finished" when `GetQueryResults` has returned a status of one of "Complete", "Failed", "Cancelled", "Timeout", or "Unknown".
77+
78+
`StartQuery` responds with an error if the query's start or end date occurs out of bounds of the log group creation date. The error message starts with "Query's end date and time".
79+
80+
Start the query and wait for it to "finish". Store the `results`. If the count of the results is less than the configured LIMIT, return the results. If the results are greater than or equal to the limit, go to [Recursive queries](#recursive-queries).
8081

8182
---
8283

83-
## Proposed Example Structure
84+
### Recursive queries
85+
86+
If the result count from the previous step is 10000 (or the configured LIMIT), it is very likely that there are more results. **The example must do a binary search of the remaining logs**. To do this, get the date of the last log (earliest or latest, depending on sort order). Use that date as the start date of a new date range. The end date can remain the same.
87+
88+
Split that date range in half, resulting in two new date ranges. Call your query function twice; once for each new date range.
89+
90+
Concatenate the results of the first query with the results of the two new queries.
91+
92+
The following pseudocode illustrates this.
93+
94+
```pseudocode
95+
func large_query(date_range):
96+
query_results = get_query_results(date_range)
97+
98+
if query_results.length < LIMIT
99+
return query_results
100+
else
101+
date_range = [query_results.end, date_range.end]
102+
d1, d2 = split(date_range)
103+
return concat(query_results, large_query(d1), large_query(d2))
104+
```
105+
106+
107+
## Example Structure
84108

85109
### Phase 1: Setup
86110

87111
**Purpose**: Deploy resources and generate sample data as part of the scenario
88112

89-
**Interactive Mode Steps**:
90113
1. Welcome message explaining the scenario
91114
2. Prompt user: "Would you like to deploy the CloudFormation stack and generate sample logs? (y/n)"
92115
3. If yes:
@@ -101,23 +124,17 @@ This scenario uses the following CloudFormation API actions:
101124
- Display message: "Sample logs created. Waiting 5 minutes for logs to be fully ingested..."
102125
- Wait 5 minutes (300 seconds) for log ingestion with countdown display
103126
4. If no:
104-
- Prompt user for existing log group name
105-
- Prompt user for log stream name
127+
- Prompt user for existing log group name, or enter to use the default name
128+
- Prompt user for log stream name, or enter to use the default name
106129
- Prompt user for query start date (ISO 8601 format with milliseconds)
107130
- Prompt user for query end date (ISO 8601 format with milliseconds)
108131

109-
**Non-Interactive Mode Behavior**:
132+
**Fully Self-Contained Behavior**:
110133
- Automatically deploys stack with default name
111134
- Automatically generates 50,000 sample logs
112135
- Waits 5 minutes for log ingestion
113136
- Uses default values for all configuration
114137

115-
**Variables Set**:
116-
- `stackName` - CloudFormation stack name
117-
- `logGroupName` - Log group name (default: `/workflows/cloudwatch-logs/large-query`)
118-
- `logStreamName` - Log stream name (default: `stream1`)
119-
- `queryStartDate` - Start timestamp for query (seconds since epoch)
120-
- `queryEndDate` - End timestamp for query (seconds since epoch)
121138

122139
### Phase 2: Query Execution
123140

@@ -133,7 +150,7 @@ This scenario uses the following CloudFormation API actions:
133150
- Start date
134151
- End date
135152
- Limit
136-
5. Display progress for each query executed (see [Output Format](#output-format))
153+
5. Display progress for each query executed
137154
6. Display total execution time
138155
7. Display total logs found
139156
8. Prompt user: "Would you like to see a sample of the logs? (y/n)"
@@ -153,120 +170,6 @@ This scenario uses the following CloudFormation API actions:
153170
- Display message: "Resources will remain. You can delete them later through the AWS Console."
154171
- Display stack name and log group name for reference
155172

156-
**Non-Interactive Mode Behavior**:
157-
- Automatically deletes the CloudFormation stack
158-
- Waits for deletion to complete
159-
- Ensures cleanup happens even if errors occur during the scenario
160-
161-
---
162-
163-
## Implementation Details
164-
165-
### CloudFormation Stack Deployment
166-
167-
**Deployment**:
168-
```
169-
Stack Name: User-provided or default "CloudWatchLargeQueryStack"
170-
Template: scenarios/features/cloudwatch_logs_large_query/resources/stack.yaml
171-
Capabilities: None required (no IAM resources)
172-
```
173-
174-
**Polling for Completion**:
175-
- Poll `DescribeStacks` every 5-10 seconds
176-
- Success: `StackStatus` = `CREATE_COMPLETE`
177-
- Failure: `StackStatus` = `CREATE_FAILED`, `ROLLBACK_COMPLETE`, or `ROLLBACK_FAILED`
178-
- Timeout: 5 minutes maximum wait time
179-
180-
### Log Generation Execution
181-
182-
**Cross-Platform Considerations**:
183-
- Bash scripts work on Linux, macOS, and Git Bash on Windows
184-
- Python script is preferred for true cross-platform support
185-
- Check for script availability before execution
186-
- Handle script execution errors gracefully
187-
188-
**Capturing Output**:
189-
- Parse stdout for `QUERY_START_DATE` and `QUERY_END_DATE`
190-
- Convert timestamps to appropriate format for SDK
191-
- Store timestamps for query configuration
192-
193-
**Wait Time**:
194-
- CloudWatch Logs requires time to ingest and index logs
195-
- Minimum wait: 5 minutes (300 seconds)
196-
- Display countdown or progress indicator during wait
197-
198-
### Building and Executing Queries
199-
200-
**Query String**:
201-
```
202-
fields @timestamp, @message | sort @timestamp asc
203-
```
204-
205-
**Important**: The query MUST return `@timestamp` field for recursive queries to work.
206-
207-
**StartQuery Parameters**:
208-
- `logGroupName` - The log group to query
209-
- `startTime` - Start of date range (seconds since epoch)
210-
- `endTime` - End of date range (seconds since epoch)
211-
- `queryString` - CloudWatch Logs Insights query syntax
212-
- `limit` - Maximum results (default: 10000, max: 10000)
213-
214-
**GetQueryResults Polling**:
215-
- Poll every 1-2 seconds
216-
- Continue until status is one of: `Complete`, `Failed`, `Cancelled`, `Timeout`, `Unknown`
217-
- Timeout after 60 seconds of polling
218-
219-
**Error Handling**:
220-
- If `StartQuery` returns error starting with "Query's end date and time", the date range is out of bounds
221-
- Handle this by adjusting the date range or informing the user
222-
223-
### Recursive Query Algorithm
224-
225-
**Purpose**: Retrieve more than 10,000 results by splitting date ranges
226-
227-
**Algorithm**:
228-
```
229-
function LargeQuery(startDate, endDate, limit):
230-
results = ExecuteQuery(startDate, endDate, limit)
231-
232-
if results.count < limit:
233-
return results
234-
else:
235-
// Get timestamp of last result
236-
lastTimestamp = results[results.count - 1].timestamp
237-
238-
// Calculate midpoint between last result and end date
239-
midpoint = (lastTimestamp + endDate) / 2
240-
241-
// Query first half
242-
results1 = LargeQuery(lastTimestamp, midpoint, limit)
243-
244-
// Query second half
245-
results2 = LargeQuery(midpoint, endDate, limit)
246-
247-
// Combine results
248-
return Concatenate(results, results1, results2)
249-
```
250-
251-
**Key Points**:
252-
- Use binary search to split remaining date range
253-
- Recursively query each half
254-
- Concatenate all results
255-
- Log each query's date range and result count (see [Output Format](#output-format))
256-
257-
### Stack Deletion
258-
259-
**Deletion**:
260-
```
261-
Stack Name: Same as used during creation
262-
```
263-
264-
**Polling for Completion**:
265-
- Poll `DescribeStacks` every 5-10 seconds
266-
- Success: Stack not found (ValidationError) or `StackStatus` = `DELETE_COMPLETE`
267-
- Failure: `StackStatus` = `DELETE_FAILED`
268-
- If `DELETE_FAILED`, optionally retry with force delete
269-
- Timeout: 5 minutes maximum wait time
270173

271174
---
272175

@@ -316,51 +219,10 @@ Sample logs (first 10 of 50000):
316219

317220
## Errors
318221

319-
### CloudFormation Errors
320-
321-
| Error Code | Error Message Pattern | Handling Strategy |
322-
|------------|----------------------|-------------------|
323-
| `AlreadyExistsException` | Stack already exists | Prompt user for different stack name and retry |
324-
| `ValidationError` | Template validation failed | Display error message and exit setup |
325-
| `InsufficientCapabilitiesException` | Requires capabilities | Should not occur (template has no IAM resources) |
326-
327-
### CloudWatch Logs Errors
328-
329222
| Error Code | Error Message Pattern | Handling Strategy |
330223
|------------|----------------------|-------------------|
331224
| `InvalidParameterException` | "Query's end date and time" | Date range is out of bounds; inform user and adjust dates |
332225
| `ResourceNotFoundException` | Log group not found | Verify log group exists; prompt user to run setup |
333-
| `LimitExceededException` | Too many concurrent queries | Wait and retry after 5 seconds |
334-
| `ServiceUnavailableException` | Service temporarily unavailable | Retry with exponential backoff (max 3 retries) |
335-
336-
### Script Execution Errors
337-
338-
| Error Type | Handling Strategy |
339-
|------------|-------------------|
340-
| Script not found | Display error message; provide manual instructions |
341-
| Script execution failed | Display error output; allow user to retry or skip |
342-
| Permission denied | Suggest making script executable (`chmod +x`) |
343-
| AWS CLI not available | Inform user AWS CLI is required for bash scripts; suggest Python alternative |
344-
345-
---
346-
347-
## User Input Variables
348-
349-
### Required Variables
350-
351-
| Variable Name | Description | Type | Default | Validation |
352-
|--------------|-------------|------|---------|------------|
353-
| `stackName` | CloudFormation stack name | String | "CloudWatchLargeQueryStack" | Must match pattern: `[a-zA-Z][-a-zA-Z0-9]*` |
354-
| `queryStartDate` | Query start timestamp | Long/Integer | From script output | Milliseconds since epoch |
355-
| `queryEndDate` | Query end timestamp | Long/Integer | From script output | Milliseconds since epoch |
356-
| `queryLimit` | Maximum results per query | Integer | 10000 | Min: 1, Max: 10000 |
357-
358-
### Optional Variables
359-
360-
| Variable Name | Description | Type | Default |
361-
|--------------|-------------|------|---------|
362-
| `logGroupName` | Log group name (if not using stack) | String | "/workflows/cloudwatch-logs/large-query" |
363-
| `logStreamName` | Log stream name (if not using stack) | String | "stream1" |
364226

365227
---
366228

@@ -370,4 +232,4 @@ Sample logs (first 10 of 50000):
370232
| ----------------- | ----------------------------- | --------------------------------- |
371233
| `GetQueryResults` | cloudwatch-logs_metadata.yaml | cloudwatch-logs_GetQueryResults |
372234
| `StartQuery` | cloudwatch-logs_metadata.yaml | cloudwatch-logs_StartQuery |
373-
| `Large Query` | cloudwatch-logs_metadata.yaml | cloudwatch-logs_Scenario_LargeQuery |
235+
| `Large Query` | cloudwatch-logs_metadata.yaml | cloudwatch-logs_Scenario_LargeQuery |

steering_docs/dotnet-tech/scenario.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ Generate feature scenarios that demonstrate complete workflows using multiple se
77
**IMPORTANT**: All new feature scenarios MUST be created in the `dotnetv4` directory, NOT `dotnetv3`.
88

99
- **New scenarios**: `dotnetv4/{Service}/`
10-
- **Legacy examples**: `dotnetv3/{Service}/` (Must NOT add new examples here)
1110

1211
## Requirements
1312
- **Specification-Driven**: MUST read the `scenarios/features/{service_feature}/SPECIFICATION.md`
@@ -168,7 +167,7 @@ public class {Service}Workflow
168167
await Cleanup();
169168
}
170169

171-
Console.WriteLine("{AWS Service} scenario completed.");
170+
Console.WriteLine("{AWS Service} feature scenario completed.");
172171
}
173172

174173
/// <summary>

0 commit comments

Comments
 (0)