Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion apps/docs/content/docs/en/tools/athena.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Retrieve the results of a completed Athena query execution
| `awsAccessKeyId` | string | Yes | AWS access key ID |
| `awsSecretAccessKey` | string | Yes | AWS secret access key |
| `queryExecutionId` | string | Yes | Query execution ID to get results for |
| `maxResults` | number | No | Maximum number of rows to return \(1-1000\) |
| `maxResults` | number | No | Maximum number of rows to return \(1-999\) |
| `nextToken` | string | No | Pagination token from a previous request |

#### Output
Expand Down
46 changes: 46 additions & 0 deletions apps/docs/content/docs/en/tools/cloudwatch.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
color="linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)"
/>

{/* MANUAL-CONTENT-START:intro */}
[AWS CloudWatch](https://aws.amazon.com/cloudwatch/) is a monitoring and observability service that provides data and actionable insights for AWS resources, applications, and services. CloudWatch collects monitoring and operational data in the form of logs, metrics, and events, giving you a unified view of your AWS environment.

With the CloudWatch integration, you can:

- **Query Logs (Insights)**: Run CloudWatch Log Insights queries against one or more log groups to analyze log data with a powerful query language
- **Describe Log Groups**: List available CloudWatch log groups in your account, optionally filtered by name prefix
- **Get Log Events**: Retrieve log events from a specific log stream within a log group
- **Describe Log Streams**: List log streams within a log group, ordered by last event time or filtered by name prefix
- **List Metrics**: Browse available CloudWatch metrics, optionally filtered by namespace, metric name, or recent activity
- **Get Metric Statistics**: Retrieve statistical data for a metric over a specified time range with configurable granularity
- **Publish Metric**: Publish custom metric data points to CloudWatch for your own application monitoring
- **Describe Alarms**: List and filter CloudWatch alarms by name prefix, state, or alarm type

In Sim, the CloudWatch integration enables your agents to monitor AWS infrastructure, analyze application logs, track custom metrics, and respond to alarm states as part of automated DevOps and SRE workflows. This is especially powerful when combined with other AWS integrations like CloudFormation and SNS for end-to-end infrastructure management.
{/* MANUAL-CONTENT-END */}


## Usage Instructions

Integrate AWS CloudWatch into workflows. Run Log Insights queries, list log groups, retrieve log events, list and get metrics, and monitor alarms. Requires AWS access key and secret access key.
Expand Down Expand Up @@ -155,6 +173,34 @@ Get statistics for a CloudWatch metric over a time range
| `label` | string | Metric label |
| `datapoints` | array | Datapoints with timestamp and statistics values |

### `cloudwatch_put_metric_data`

Publish a custom metric data point to CloudWatch

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
| `awsAccessKeyId` | string | Yes | AWS access key ID |
| `awsSecretAccessKey` | string | Yes | AWS secret access key |
| `namespace` | string | Yes | Metric namespace \(e.g., Custom/MyApp\) |
| `metricName` | string | Yes | Name of the metric |
| `value` | number | Yes | Metric value to publish |
| `unit` | string | No | Unit of the metric \(e.g., Count, Seconds, Bytes\) |
| `dimensions` | string | No | JSON string of dimension name/value pairs |

#### Output

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Whether the metric was published successfully |
| `namespace` | string | Metric namespace |
| `metricName` | string | Metric name |
| `value` | number | Published metric value |
| `unit` | string | Metric unit |
| `timestamp` | string | Timestamp when the metric was published |

### `cloudwatch_describe_alarms`

List and filter CloudWatch alarms
Expand Down
6 changes: 5 additions & 1 deletion apps/sim/app/(landing)/integrations/data/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -2044,12 +2044,16 @@
"name": "Get Metric Statistics",
"description": "Get statistics for a CloudWatch metric over a time range"
},
{
"name": "Publish Metric",
"description": "Publish a custom metric data point to CloudWatch"
},
{
"name": "Describe Alarms",
"description": "List and filter CloudWatch alarms"
}
],
"operationCount": 7,
"operationCount": 8,
"triggers": [],
"triggerCount": 0,
"authType": "none",
Expand Down
4 changes: 3 additions & 1 deletion apps/sim/app/api/tools/cloudwatch/describe-alarms/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ export async function POST(request: NextRequest) {
const command = new DescribeAlarmsCommand({
...(validatedData.alarmNamePrefix && { AlarmNamePrefix: validatedData.alarmNamePrefix }),
...(validatedData.stateValue && { StateValue: validatedData.stateValue as StateValue }),
...(validatedData.alarmType && { AlarmTypes: [validatedData.alarmType as AlarmType] }),
AlarmTypes: validatedData.alarmType
? [validatedData.alarmType as AlarmType]
: (['MetricAlarm', 'CompositeAlarm'] as AlarmType[]),
...(validatedData.limit !== undefined && { MaxRecords: validatedData.limit }),
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export async function POST(request: NextRequest) {
}))
}
} catch {
throw new Error('Invalid dimensions JSON')
return NextResponse.json({ error: 'Invalid dimensions JSON format' }, { status: 400 })
}
}

Expand Down
136 changes: 136 additions & 0 deletions apps/sim/app/api/tools/cloudwatch/put-metric-data/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import {
CloudWatchClient,
PutMetricDataCommand,
type StandardUnit,
} from '@aws-sdk/client-cloudwatch'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'

const logger = createLogger('CloudWatchPutMetricData')

const VALID_UNITS = [
'Seconds',
'Microseconds',
'Milliseconds',
'Bytes',
'Kilobytes',
'Megabytes',
'Gigabytes',
'Terabytes',
'Bits',
'Kilobits',
'Megabits',
'Gigabits',
'Terabits',
'Percent',
'Count',
'Bytes/Second',
'Kilobytes/Second',
'Megabytes/Second',
'Gigabytes/Second',
'Terabytes/Second',
'Bits/Second',
'Kilobits/Second',
'Megabits/Second',
'Gigabits/Second',
'Terabits/Second',
'Count/Second',
'None',
] as const

const PutMetricDataSchema = z.object({
region: z.string().min(1, 'AWS region is required'),
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
namespace: z.string().min(1, 'Namespace is required'),
metricName: z.string().min(1, 'Metric name is required'),
value: z.number({ coerce: true }).refine((v) => Number.isFinite(v), {
message: 'Metric value must be a finite number',
}),
unit: z.enum(VALID_UNITS).optional(),
dimensions: z
.string()
.optional()
.refine(
(val) => {
if (!val) return true
try {
const parsed = JSON.parse(val)
return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
} catch {
return false
}
},
{ message: 'dimensions must be a valid JSON object string' }
),
})

export async function POST(request: NextRequest) {
try {
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
}

const body = await request.json()
const validatedData = PutMetricDataSchema.parse(body)

const client = new CloudWatchClient({
region: validatedData.region,
credentials: {
accessKeyId: validatedData.accessKeyId,
secretAccessKey: validatedData.secretAccessKey,
},
})

const timestamp = new Date()

const dimensions: { Name: string; Value: string }[] = []
if (validatedData.dimensions) {
const parsed = JSON.parse(validatedData.dimensions)
for (const [name, value] of Object.entries(parsed)) {
dimensions.push({ Name: name, Value: String(value) })
}
}

const command = new PutMetricDataCommand({
Namespace: validatedData.namespace,
MetricData: [
{
MetricName: validatedData.metricName,
Value: validatedData.value,
Timestamp: timestamp,
...(validatedData.unit && { Unit: validatedData.unit as StandardUnit }),
...(dimensions.length > 0 && { Dimensions: dimensions }),
},
],
})

await client.send(command)

return NextResponse.json({
success: true,
output: {
success: true,
namespace: validatedData.namespace,
metricName: validatedData.metricName,
value: validatedData.value,
unit: validatedData.unit ?? 'None',
timestamp: timestamp.toISOString(),
},
})
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: error.errors[0]?.message ?? 'Invalid request' },
{ status: 400 }
)
}
const errorMessage =
error instanceof Error ? error.message : 'Failed to publish CloudWatch metric'
logger.error('PutMetricData failed', { error: errorMessage })
return NextResponse.json({ error: errorMessage }, { status: 500 })
}
}
1 change: 1 addition & 0 deletions apps/sim/blocks/blocks/cloudformation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export const CloudFormationBlock: BlockConfig<
type: 'short-input',
placeholder: '50',
condition: { field: 'operation', value: 'describe_stack_events' },
mode: 'advanced',
},
],
tools: {
Expand Down
Loading
Loading