Skip to content

CloudWatch

Floci supports both CloudWatch Logs and CloudWatch Metrics.


CloudWatch Logs

Protocol: JSON 1.1 (X-Amz-Target: Logs.*) Endpoint: POST http://localhost:4566/

Supported Actions

Action Description
CreateLogGroup Create a log group
DeleteLogGroup Delete a log group
DescribeLogGroups List log groups
CreateLogStream Create a log stream inside a log group
DeleteLogStream Delete a log stream
DescribeLogStreams List log streams in a group
PutLogEvents Write log events to a stream
GetLogEvents Read log events from a stream
FilterLogEvents Search log events with a filter pattern
PutRetentionPolicy Set log retention (days)
DeleteRetentionPolicy Remove log retention policy
TagLogGroup Tag a log group
UntagLogGroup Remove tags
ListTagsLogGroup List tags
TagResource Tag a log group by ARN
UntagResource Remove tags from a log group by ARN
ListTagsForResource List tags for a log group ARN
PutSubscriptionFilter Create or update a subscription filter (stored only, see note below)
DescribeSubscriptionFilters List subscription filters on a log group
DeleteSubscriptionFilter Delete a subscription filter
GetDataProtectionPolicy Return the resolved log group identifier (see note below)
StartQuery Start a Logs Insights query (see Logs Insights)
GetQueryResults Get the status and results of a Logs Insights query
StopQuery Stop a query that has not completed yet

Two actions are currently simplified:

  • PutSubscriptionFilter stores the filter so that DescribeSubscriptionFilters returns it, but log events are not forwarded to the destination ARN. Lambda, Kinesis, and Firehose subscription destinations are not wired up.
  • GetDataProtectionPolicy does not model data-protection policies. It returns HTTP 200 with the resolved logGroupIdentifier and no policyDocument — including for a log group that does not exist, where real AWS returns ResourceNotFoundException.

Logs Insights

StartQuery / GetQueryResults / StopQuery run a subset of the Logs Insights query language. Supported commands:

Command Notes
fields a, b, ... Projection. Defaults to @timestamp, @message. display is an alias
filter <field> = 'v' Equality. != and == are also accepted; where is an alias
sort <field> [asc\|desc] order is an alias
dedup <field, ...> Keeps the first row per unique tuple, applied after sorting
limit N The effective cap is the smallest of this value, the StartQuery limit parameter, and FLOCI_SERVICES_CLOUDWATCHLOGS_MAX_EVENTS_PER_QUERY

Fields may be @timestamp, @message, @ingestionTime, @ptr, or a dotted path into a JSON log message (for example level or params.job_id). A @ptr column is always included in each result row, appended unless fields already names it.

Unsupported syntax never fails the query, so it is worth knowing how each case degrades:

Input Result
An unsupported command (stats, parse, ...) Skipped with a warning in the server log. No aggregation happens
A filter whose operator is not =, != or == — for example like /ERROR/ or =~ /ERROR/ The whole stage is dropped with a warning, so every row is returned
A filter using <, <=, > or >= The = is taken as the operator and the rest of the token becomes part of the field name, which then resolves to nothing — so the row never matches and you get no rows. No warning is logged
A projected field that does not exist Rendered as an empty string. No warning
A sort direction other than asc / desc Treated as ascending. No warning

In short, a query can come back either wider or narrower than intended without any error. When a result set looks wrong, check the server log for Ignoring unsupported Logs Insights ... — and note that the >= case above produces no log line at all.

For simple substring matching, FilterLogEvents is the more predictable option today. Note that Floci matches --filter-pattern as a plain substring of the message; the real filter-pattern syntax (?ERROR ?WARN, { $.level = "ERROR" }, and so on) is not parsed.

Configuration

Variable Default Description
FLOCI_SERVICES_CLOUDWATCHLOGS_ENABLED true Enable or disable the CloudWatch Logs service
FLOCI_SERVICES_CLOUDWATCHLOGS_MAX_EVENTS_PER_QUERY 10000 Maximum events returned per FilterLogEvents / GetLogEvents call, and the upper bound for a Logs Insights limit
FLOCI_SERVICES_CLOUDWATCHLOGS_QUERY_COMPLETION_DELAY_MS 0 Artificial Logs Insights query delay. With 0 a query completes immediately. A positive value emulates the real asynchronous lifecycle (RunningComplete after the delay), which also makes StopQuery on a still-running query return success=true

Examples

export AWS_ENDPOINT_URL=http://localhost:4566

# Create a log group and stream
aws logs create-log-group --log-group-name /app/backend --endpoint-url $AWS_ENDPOINT_URL
aws logs create-log-stream \
  --log-group-name /app/backend \
  --log-stream-name 2025/01/app-1 \
  --endpoint-url $AWS_ENDPOINT_URL

# Write log events
TIMESTAMP=$(date +%s%3N)   # milliseconds
aws logs put-log-events \
  --log-group-name /app/backend \
  --log-stream-name 2025/01/app-1 \
  --log-events "[{\"timestamp\":$TIMESTAMP,\"message\":\"Service started\"}]" \
  --endpoint-url $AWS_ENDPOINT_URL

# Read log events
aws logs get-log-events \
  --log-group-name /app/backend \
  --log-stream-name 2025/01/app-1 \
  --endpoint-url $AWS_ENDPOINT_URL

# Search logs
aws logs filter-log-events \
  --log-group-name /app/backend \
  --filter-pattern "ERROR" \
  --endpoint-url $AWS_ENDPOINT_URL

# Run a Logs Insights query
QUERY_ID=$(aws logs start-query \
  --log-group-name /app/backend \
  --start-time $(($(date +%s) - 3600)) \
  --end-time $(date +%s) \
  --query-string 'fields @timestamp, @message | sort @timestamp desc | limit 20' \
  --query queryId --output text \
  --endpoint-url $AWS_ENDPOINT_URL)

aws logs get-query-results \
  --query-id "$QUERY_ID" \
  --endpoint-url $AWS_ENDPOINT_URL

# Set retention
aws logs put-retention-policy \
  --log-group-name /app/backend \
  --retention-in-days 30 \
  --endpoint-url $AWS_ENDPOINT_URL

CloudWatch Metrics

Protocol: Query (XML) and JSON 1.1 (both supported) Endpoint: POST http://localhost:4566/

Supported Actions

Action Description
PutMetricData Publish custom metrics
ListMetrics List available metrics
GetMetricStatistics Get metric statistics (Average, Sum, etc.)
GetMetricData Query metrics with math expressions
PutMetricAlarm Create a metric alarm
DescribeAlarms List alarms
DeleteAlarms Delete alarms
SetAlarmState Manually set alarm state

Examples

export AWS_ENDPOINT_URL=http://localhost:4566

# Publish a custom metric
aws cloudwatch put-metric-data \
  --namespace MyApp \
  --metric-data '[{
    "MetricName": "RequestCount",
    "Value": 42,
    "Unit": "Count",
    "Dimensions": [{"Name":"Service","Value":"api"}]
  }]' \
  --endpoint-url $AWS_ENDPOINT_URL

# List metrics
aws cloudwatch list-metrics \
  --namespace MyApp \
  --endpoint-url $AWS_ENDPOINT_URL

# Get statistics
aws cloudwatch get-metric-statistics \
  --namespace MyApp \
  --metric-name RequestCount \
  --dimensions Name=Service,Value=api \
  --start-time $(date -u -v-1H +%Y-%m-%dT%H:%M:%SZ) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
  --period 300 \
  --statistics Sum \
  --endpoint-url $AWS_ENDPOINT_URL

# Create an alarm
aws cloudwatch put-metric-alarm \
  --alarm-name high-error-rate \
  --metric-name ErrorCount \
  --namespace MyApp \
  --statistic Sum \
  --period 60 \
  --threshold 10 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 1 \
  --endpoint-url $AWS_ENDPOINT_URL