Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7023483
fix(logs): discover and display all container logs from multi-contain…
drernie Nov 21, 2025
31664a6
spec: paging log streams
drernie Nov 21, 2025
6d1f771
feat(config): capture and store Quilt stack version
drernie Nov 22, 2025
ba4df3c
fix(logs): implement cross-stream pagination for ECS task restarts
drernie Nov 22, 2025
702e7a4
docs: mark log stream pagination spec as implemented
drernie Nov 22, 2025
c47a6ff
fix(logs): remove orderBy when using logStreamNamePrefix in DescribeL…
drernie Nov 22, 2025
27f9b4f
docs(spec): document AWS API constraint fix for log stream discovery
drernie Nov 22, 2025
a9ab17b
feat(logs): redesign UX with caching, parallel fetching, and progress…
drernie Nov 22, 2025
504702b
fix(logs): resolve two critical issues preventing log discovery and d…
drernie Nov 22, 2025
68f81b3
rethink-logs
drernie Nov 24, 2025
bba371f
feat(logs): implement interactive dashboard with blessed UI
drernie Nov 24, 2025
e20c484
feat(logs): make dashboard mode the default
drernie Nov 24, 2025
472594a
feat(stack): add support for new integrated architecture CloudFormati…
drernie Nov 24, 2025
1be081a
feat(logs): filter non-Benchling containers and improve log discovery
drernie Nov 24, 2025
2db3bf8
fix(setup): save discovered log groups in standalone mode
drernie Nov 25, 2025
e7910aa
improve dashboard resilience
drernie Nov 25, 2025
6cba1dc
Merge branch 'main' into blessed-logs
drernie Nov 25, 2025
79a19ff
chore: bump version to 0.8.9
drernie Nov 25, 2025
8143472
fix(logs): resolve linting errors in dashboard components
drernie Nov 25, 2025
7201d7e
fix(deps): add blessed dependency for terminal UI
drernie Nov 25, 2025
04c6195
logging specs
drernie Nov 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 82 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,10 +464,91 @@ npm run deploy:prod -- \

### Logs

#### Using the Built-in Logs Command (Recommended)

```bash
# Interactive dashboard with auto-refresh (default)
benchling-webhook logs --profile sales

# Show only application logs (filters out bucket_scanner, etc.)
benchling-webhook logs --profile sales --type ecs

# Show ALL containers (including non-Benchling services)
benchling-webhook logs --profile sales --all-containers

# Text mode with custom refresh interval
benchling-webhook logs --profile sales --no-dashboard --timer 5

# Single snapshot (no auto-refresh)
benchling-webhook logs --profile sales --timer 0

# Filter for errors in last hour
benchling-webhook logs --profile sales --since 1h --filter ERROR
```

#### Using AWS CLI Directly

For advanced use cases or when you need more control:

```bash
aws logs tail /ecs/benchling-webhook --follow
# Get application logs only (most relevant for debugging)
aws logs tail tf-dev-bench \
--follow \
--filter-pattern "benchling/benchling" \
--region us-east-2

# Get recent application logs with timestamps
aws logs filter-log-events \
--log-group-name tf-dev-bench \
--log-stream-name-prefix "benchling/benchling" \
--start-time $(($(date +%s) - 1800))000 \
--region us-east-2 \
--output json | jq -r '.events[] | "\(.timestamp | todate) \(.message)"'

# List all unique container stream prefixes in a log group
aws logs describe-log-streams \
--log-group-name tf-dev-bench \
--region us-east-2 \
--max-items 1000 \
--query 'logStreams[*].logStreamName' \
--output text | cut -d/ -f1-2 | sort -u

# Get nginx proxy logs
aws logs filter-log-events \
--log-group-name tf-dev-bench \
--log-stream-name-prefix "benchling-nginx/nginx" \
--start-time $(($(date +%s) - 3600))000 \
--region us-east-2

# Check if API Gateway execution logs exist
aws logs describe-log-groups \
--log-group-name-prefix "API-Gateway-Execution-Logs_" \
--region us-east-2

# Tail API Gateway execution logs (if enabled)
aws logs tail "API-Gateway-Execution-Logs_72569ezcng/prod" \
--region us-east-2 \
--follow

# Check API Gateway stage configuration for logging
aws apigateway get-stage \
--rest-api-id 72569ezcng \
--stage-name prod \
--region us-east-2 \
--query '{accessLogs: accessLogSettings.destinationArn, executionLogs: methodSettings}'
```

#### Container Filtering

By default, the `benchling-webhook logs` command filters log output to show only Benchling-related containers:
- `benchling/benchling` - Application logs (Flask webhook processor)
- `benchling-nginx/nginx` - Proxy logs (nginx reverse proxy)
- API Gateway logs (if available)

**Filtered out by default** (use `--all-containers` to see):
- `bulk_loader/bucket_scanner` - Quilt bulk loader (not relevant for webhook debugging)
- `registry/nginx-catalog` - Quilt catalog nginx (not relevant for webhook debugging)

### Health Checks

- `/health` - General health
Expand Down
11 changes: 8 additions & 3 deletions bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ program
program
.command("status")
.description(
"Check CloudFormation stack status and BenchlingIntegration parameter",
"Check CloudFormation stack status and BenchlingWebhook parameter",
)
.option(
"--profile <name>",
Expand Down Expand Up @@ -187,8 +187,10 @@ program
"5m",
)
.option("--filter <pattern>", "Filter logs by pattern (example: ERROR)")
.option("--limit <n>", "Number of log entries to show per log group (default: 5)", "5")
.option("--limit <n>", "Number of log entries to show per log group (default: 20)", "20")
.option("--timer <seconds>", "Auto-refresh interval in seconds (default: 10, use 0 to disable)", "10")
.option("--no-dashboard", "Disable interactive dashboard UI (use text mode)")
.option("--all-containers", "Show all containers (including bucket_scanner, registry, etc.)")
.addHelpText(
"after",
`
Expand All @@ -200,9 +202,12 @@ Log Types:
api-exec API Gateway execution logs (detailed debugging)

Examples:
View all logs (auto-refreshes every 10 seconds):
View all logs with interactive dashboard (default):
$ npx @quiltdata/benchling-webhook logs --profile sales

Use text mode instead of dashboard:
$ npx @quiltdata/benchling-webhook logs --profile sales --no-dashboard

View only ECS logs:
$ npx @quiltdata/benchling-webhook logs --profile sales --type ecs

Expand Down
85 changes: 83 additions & 2 deletions bin/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import {
} from "../../lib/utils/service-resolver";
import { checkCdkBootstrap } from "../benchling-webhook";
import { XDGConfig } from "../../lib/xdg-config";
import { ProfileConfig } from "../../lib/types/config";
import { CloudFormationClient, DescribeStacksCommand } from "@aws-sdk/client-cloudformation";
import { ProfileConfig, LogGroupInfo } from "../../lib/types/config";
import { CloudFormationClient, DescribeStacksCommand, type Stack } from "@aws-sdk/client-cloudformation";
import { syncSecretsToAWS } from "./sync-secrets";
import { discoverECSServiceLogGroups } from "../../lib/utils/ecs-service-discovery";
import * as fs from "fs";
import * as path from "path";

Expand All @@ -27,6 +28,73 @@ function suggestSetup(profileName: string, message: string): void {
console.log();
}

/**
* Discover log groups from CloudFormation stack
* Works for both standalone and integrated modes
*/
async function discoverLogGroups(
stack: Stack,
stackName: string,
region: string,
integratedMode: boolean,
): Promise<LogGroupInfo[]> {
const logGroups: LogGroupInfo[] = [];

if (integratedMode) {
// Integrated mode: Discover ECS services dynamically
const serviceLogGroups = await discoverECSServiceLogGroups(stackName, region);

for (const [serviceName, logGroupName] of Object.entries(serviceLogGroups)) {
// Create friendly display name from service name
const displayName = serviceName
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");

logGroups.push({
name: logGroupName,
type: "ecs",
displayName: `${displayName} (ECS)`,
streamPrefix: "benchling-webhook",
});
}
} else {
// Standalone mode: Read from stack outputs
const outputs = stack.Outputs || [];

const ecsLogGroup = outputs.find((o) => o.OutputKey === "EcsLogGroup")?.OutputValue;
const apiLogGroup = outputs.find((o) => o.OutputKey === "ApiGatewayLogGroup")?.OutputValue;
const apiExecLogGroup = outputs.find((o) => o.OutputKey === "ApiGatewayExecutionLogGroup")?.OutputValue;

if (ecsLogGroup) {
logGroups.push({
name: ecsLogGroup,
type: "ecs",
displayName: "ECS Container Logs",
streamPrefix: "benchling-webhook",
});
}

if (apiLogGroup) {
logGroups.push({
name: apiLogGroup,
type: "api",
displayName: "API Gateway Access Logs",
});
}

if (apiExecLogGroup) {
logGroups.push({
name: apiExecLogGroup,
type: "api-exec",
displayName: "API Gateway Execution Logs",
});
}
}

return logGroups;
}

/**
* Get the most recent dev version tag (without 'v' prefix)
* Returns null if no dev tags found
Expand Down Expand Up @@ -487,6 +555,14 @@ export async function deploy(
// Remove trailing slash to avoid double slashes in test URLs
const cleanEndpoint = webhookUrl.replace(/\/$/, "");

// Discover log groups from deployment
const logGroups = await discoverLogGroups(
stack,
stackName,
deployRegion,
config.integratedStack || false,
);

// Record deployment in profile
const xdg = new XDGConfig();
xdg.recordDeployment(options.profileName, {
Expand All @@ -499,7 +575,12 @@ export async function deploy(
deployedBy: process.env.USER || process.env.USERNAME,
});

// Update profile config with discovered log groups
config.deployment.logGroups = logGroups;
xdg.writeProfile(options.profileName, config);

console.log(`βœ… Recorded deployment to profile '${options.profileName}' stage '${options.stage}'`);
console.log(`βœ… Discovered and saved ${logGroups.length} log group(s)`);

// Success message with webhook URL
console.log();
Expand Down
Loading