Skip to content

AWS.Logs reference

Source: src/AWS/Logs/CreateLogStream.ts

Runtime binding for logs:CreateLogStream.

Bind this operation to a LogGroup inside a function runtime to create log streams dynamically (e.g. one stream per tenant or per day) before writing to them with PutLogEvents, automatically injecting the log group name. For a fixed stream known at deploy time, declare an AWS.Logs.LogStream resource instead.

const createLogStream = yield* AWS.Logs.CreateLogStream(logGroup);
const putLogEvents = yield* AWS.Logs.PutLogEvents(logGroup);
yield* createLogStream({ logStreamName: `tenant-${tenantId}` }).pipe(
Effect.catchTag("ResourceAlreadyExistsException", () => Effect.void),
);
yield* putLogEvents({
logStreamName: `tenant-${tenantId}`,
logEvents: [{ timestamp, message }],
});

Source: src/AWS/Logs/DeleteLogStream.ts

Runtime binding for logs:DeleteLogStream.

Bind this operation to a LogGroup inside a function runtime to delete dynamically-created log streams (e.g. cleaning up per-tenant streams created with CreateLogStream), automatically injecting the log group name.

const deleteLogStream = yield* AWS.Logs.DeleteLogStream(logGroup);
yield* deleteLogStream({ logStreamName: `tenant-${tenantId}` }).pipe(
Effect.catchTag("ResourceNotFoundException", () => Effect.void),
);

Source: src/AWS/Logs/DescribeLogStreams.ts

Runtime binding for logs:DescribeLogStreams.

Bind this operation to a LogGroup inside a function runtime to list the streams of the group (e.g. to discover the most recently written stream before reading with GetLogEvents), automatically injecting the log group name.

const describeLogStreams = yield* AWS.Logs.DescribeLogStreams(logGroup);
const { logStreams } = yield* describeLogStreams({
orderBy: "LastEventTime",
descending: true,
limit: 1,
});

Source: src/AWS/Logs/Destination.ts

A CloudWatch Logs destination — a cross-account subscription target that forwards log events to a Kinesis stream. Producers in other accounts create subscription filters whose destinationArn points at this destination; the accessPolicy controls which accounts may subscribe.

const destination = yield* Destination("CentralLogs", {
targetArn: stream.streamArn,
roleArn: role.roleArn,
accessPolicy: {
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Principal: { AWS: "123456789012" },
Action: ["logs:PutSubscriptionFilter"],
Resource: "*",
},
],
},
});

Source: src/AWS/Logs/FilterLogEvents.ts

Runtime binding for logs:FilterLogEvents.

Bind this operation to a LogGroup inside a function runtime to get a callable that searches log events across all streams of the group, automatically injecting the log group name.

Search for a Marker

const filterLogEvents = yield* AWS.Logs.FilterLogEvents(logGroup);
const response = yield* filterLogEvents({
filterPattern: '"ERROR"',
limit: 100,
});

Wire into a Lambda Function

// Provide the FilterLogEventsHttp layer on the Function's init Effect.
export default SearchFunction.make(
{ main: import.meta.url, functionUrl: true },
Effect.gen(function* () {
const logGroup = yield* AWS.Logs.LogGroup("AppLogs", {});
const filterLogEvents = yield* AWS.Logs.FilterLogEvents(logGroup);
return {
fetch: Effect.gen(function* () {
const response = yield* filterLogEvents({ filterPattern: '"ERROR"' });
return HttpServerResponse.json({ events: response.events });
}),
};
}).pipe(Effect.provide(AWS.Logs.FilterLogEventsHttp)),
);

Source: src/AWS/Logs/GetLogEvents.ts

Runtime binding for logs:GetLogEvents.

Bind this operation to a LogGroup inside a function runtime to get a callable that reads log events from a single stream of the group, automatically injecting the log group name.

Read a Stream from the Beginning

const getLogEvents = yield* AWS.Logs.GetLogEvents(logGroup);
const response = yield* getLogEvents({
logStreamName: "my-stream",
startFromHead: true,
});

Wire into a Lambda Function

// Provide the GetLogEventsHttp layer on the Function's init Effect;
// combine with Layer.mergeAll when using several Logs bindings.
export default TailFunction.make(
{ main: import.meta.url, functionUrl: true },
Effect.gen(function* () {
const logGroup = yield* AWS.Logs.LogGroup("AppLogs", {});
const getLogEvents = yield* AWS.Logs.GetLogEvents(logGroup);
return {
fetch: Effect.gen(function* () {
const { events } = yield* getLogEvents({
logStreamName: "my-stream",
startFromHead: true,
});
return HttpServerResponse.json({ events });
}),
};
}).pipe(Effect.provide(AWS.Logs.GetLogEventsHttp)),
);

Source: src/AWS/Logs/GetLogGroupFields.ts

Runtime binding for logs:GetLogGroupFields (CloudWatch Logs Insights).

Bind this operation to a LogGroup inside a function runtime to discover the fields present in the group’s recent log events (and the percentage of events each field appears in), automatically injecting the log group name. Useful for building Insights queries dynamically.

const getLogGroupFields = yield* AWS.Logs.GetLogGroupFields(logGroup);
const { logGroupFields } = yield* getLogGroupFields();
// e.g. [{ name: "@timestamp", percent: 100 }, { name: "@message", ... }]

Source: src/AWS/Logs/GetLogRecord.ts

Runtime binding for logs:GetLogRecord (CloudWatch Logs Insights).

Bind this operation to the LogGroup an Insights query ran against to fetch the complete log record behind a query-result row: every row returned by GetQueryResults carries a @ptr field that identifies the record.

const getLogRecord = yield* AWS.Logs.GetLogRecord(logGroup);
const ptr = row.find((field) => field.field === "@ptr")?.value;
const { logRecord } = yield* getLogRecord({ logRecordPointer: ptr! });
// logRecord["@message"] is the full unparsed log line

Source: src/AWS/Logs/GetQueryResults.ts

Runtime binding for logs:GetQueryResults (CloudWatch Logs Insights).

Bind this operation to the LogGroup an Insights query was started against (via StartQuery) to poll for its results.

Poll Query Results

const getQueryResults = yield* AWS.Logs.GetQueryResults(logGroup);
const response = yield* getQueryResults({ queryId });
if (response.status === "Complete") {
// response.results is an array of field/value rows
}

Poll Until Complete

// Bounded, declarative polling — never a while-loop.
const results = yield* getQueryResults({ queryId }).pipe(
Effect.repeat({
schedule: Schedule.spaced("2 seconds"),
until: (r) => r.status === "Complete",
times: 15,
}),
);

Wire into a Lambda Function

// Provide the layer on the Function's init Effect, merged with
// StartQueryHttp since the two bindings are always used together.
export default InsightsFunction.make(
{ main: import.meta.url, functionUrl: true },
Effect.gen(function* () {
const logGroup = yield* AWS.Logs.LogGroup("AppLogs", {});
const startQuery = yield* AWS.Logs.StartQuery(logGroup);
const getQueryResults = yield* AWS.Logs.GetQueryResults(logGroup);
// ... start the query and poll for results in the fetch handler
return { fetch: handler };
}).pipe(
Effect.provide(
Layer.mergeAll(AWS.Logs.StartQueryHttp, AWS.Logs.GetQueryResultsHttp),
),
),
);

Source: src/AWS/Logs/LogEventSink.ts

A batching sink over CloudWatch Logs PutLogEvents (10,000 events / 1,048,576 bytes per call, where each event costs its UTF-8 message size plus 26 bytes of overhead).

Each input element is a raw InputLogEvent, so ordering stays with the caller: PutLogEvents requires events in chronological order by timestamp, and the time span within one batch must not exceed 24 hours.

Events the API reports via rejectedLogEventsInfo (older than 14 days or the group’s retention period, expired, or more than 2 hours in the future) are permanently rejected — they are dropped and surfaced with a warning, never retried. The remaining valid events in the batch are still ingested by the API.

Drain a Stream of Events into a Log Stream

const sink = yield* AWS.Logs.LogEventSink(logGroup, {
logStreamName: "audit-stream",
});
// Inside a handler: drain fully before returning.
yield* Stream.fromIterable(entries).pipe(
Stream.map((entry) => ({ timestamp: entry.at, message: entry.text })),
Stream.run(sink),
);

Wire into a Lambda Function

// LogEventSinkHttp batches over the PutLogEvents binding, so provide
// PutLogEventsHttp into it with Layer.provideMerge.
export default IngestFunction.make(
{ main: import.meta.url, functionUrl: true },
Effect.gen(function* () {
const logGroup = yield* AWS.Logs.LogGroup("IngestLogs", {});
yield* AWS.Logs.LogStream("IngestStream", {
logGroupName: logGroup.logGroupName,
logStreamName: "ingest-stream",
});
const sink = yield* AWS.Logs.LogEventSink(logGroup, {
logStreamName: "ingest-stream",
});
// ... run streams of InputLogEvents into `sink` in the fetch handler
return { fetch: handler };
}).pipe(
Effect.provide(
Layer.provideMerge(AWS.Logs.LogEventSinkHttp, AWS.Logs.PutLogEventsHttp),
),
),
);

Source: src/AWS/Logs/LogGroup.ts

A CloudWatch Logs log group — the container for log streams and the unit that retention, encryption, metric filters, and subscriptions attach to.

ECS Task Log Group

const logs = yield* LogGroup("TaskLogs", {
retention: "7 days",
});

Encrypted Log Group with Deletion Protection

const key = yield* AWS.KMS.Key("LogsKey");
const logs = yield* LogGroup("AuditLogs", {
retention: "30 days",
kmsKeyId: key.keyArn,
deletionProtectionEnabled: true,
});

Declare a LogStream and use the PutLogEvents binding inside a Lambda function (or the batching LogEventSink for high-volume streams).

// init
const logGroup = yield* AWS.Logs.LogGroup("AuditLogs", {
retention: "30 days",
});
const stream = yield* AWS.Logs.LogStream("AuditStream", {
logGroupName: logGroup.logGroupName,
logStreamName: "audit",
});
const putLogEvents = yield* AWS.Logs.PutLogEvents(logGroup);
// runtime
yield* putLogEvents({
logStreamName: "audit",
logEvents: [{ timestamp, message: "user.login id=123" }],
});
// Subscribe a Lambda handler to matching events (creates the
// subscription filter + invoke permission automatically).
yield* AWS.Logs.consumeLogEvents(
logGroup,
{ filterPattern: "?ERROR ?Error" },
(events) =>
Stream.runForEach(events, (event) =>
Effect.log(`${event.logStream}: ${event.message}`),
),
);
yield* AWS.Logs.MetricFilter("ErrorCount", {
logGroupName: logGroup.logGroupName,
filterPattern: '"ERROR"',
metricTransformations: [{
metricName: "ErrorCount",
metricNamespace: "MyApp",
metricValue: "1",
}],
});

Source: src/AWS/Logs/LogStream.ts

A CloudWatch Logs log stream — a sequence of log events within a log group.

Most log streams are created automatically by the emitting service (Lambda creates its own streams under /aws/lambda/...); declare one explicitly only when writing custom log events via putLogEvents.

const stream = yield* LogStream("AuditStream", {
logGroupName: logGroup.logGroupName,
});

Source: src/AWS/Logs/MetricFilter.ts

A CloudWatch Logs metric filter — extracts CloudWatch metrics from log events matching a filter pattern.

Count Error Log Lines

const errors = yield* MetricFilter("ErrorCount", {
logGroupName: logGroup.logGroupName,
filterPattern: "?ERROR ?Error",
metricTransformations: [
{
metricName: "ErrorCount",
metricNamespace: "MyApp",
metricValue: "1",
defaultValue: 0,
},
],
});

Extract a Latency Value from JSON Logs

const latency = yield* MetricFilter("RequestLatency", {
logGroupName: logGroup.logGroupName,
filterPattern: "{ $.latencyMs = * }",
metricTransformations: [
{
metricName: "LatencyMs",
metricNamespace: "MyApp",
metricValue: "$.latencyMs",
unit: "Milliseconds",
},
],
});

Source: src/AWS/Logs/PutLogEvents.ts

Runtime binding for logs:PutLogEvents.

Bind this operation to a LogGroup inside a function runtime to get a callable that writes log events to a stream of the group (e.g. custom audit trails), automatically injecting the log group name. The target log stream must already exist (declare an AWS.Logs.LogStream). Sequence tokens are no longer required by CloudWatch Logs.

Write an Audit Event

const putLogEvents = yield* AWS.Logs.PutLogEvents(logGroup);
yield* putLogEvents({
logStreamName: stream.logStreamName,
logEvents: [{ timestamp: now, message: "user.login id=123" }],
});

Wire into a Lambda Function

// Bind in the init phase, call in the handler, and provide the
// PutLogEventsHttp layer on the Function's init Effect.
export default AuditFunction.make(
{ main: import.meta.url, functionUrl: true },
Effect.gen(function* () {
const logGroup = yield* AWS.Logs.LogGroup("AuditLogs", {
retention: "30 days",
});
const stream = yield* AWS.Logs.LogStream("AuditStream", {
logGroupName: logGroup.logGroupName,
});
const putLogEvents = yield* AWS.Logs.PutLogEvents(logGroup);
const LogStreamName = yield* stream.logStreamName;
return {
fetch: Effect.gen(function* () {
const timestamp = yield* Clock.currentTimeMillis;
yield* putLogEvents({
logStreamName: yield* LogStreamName,
logEvents: [{ timestamp, message: "audit.event" }],
});
return HttpServerResponse.text("ok");
}),
};
}).pipe(Effect.provide(AWS.Logs.PutLogEventsHttp)),
);

Source: src/AWS/Logs/ResourcePolicy.ts

An account-scoped CloudWatch Logs resource policy — grants AWS service principals (Route 53 query logging, API Gateway execution logs, OpenSearch slow logs, …) permission to deliver logs into your account.

AWS allows at most 10 resource policies per region per account and the quota cannot be raised. Always use a deterministic policyName and destroy policies you no longer need.

const policy = yield* ResourcePolicy("Route53QueryLogging", {
policyName: "route53-query-logging",
policyDocument: {
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Principal: { Service: "route53.amazonaws.com" },
Action: ["logs:CreateLogStream", "logs:PutLogEvents"],
Resource: `arn:aws:logs:us-east-1:${accountId}:log-group:/aws/route53/*`,
},
],
},
});

Source: src/AWS/Logs/StartQuery.ts

Runtime binding for logs:StartQuery (CloudWatch Logs Insights).

Bind this operation to a LogGroup inside a function runtime to get a callable that starts an Insights query scoped to the group, automatically injecting the log group name. Pair with GetQueryResults to poll for results.

Start an Insights Query

const startQuery = yield* AWS.Logs.StartQuery(logGroup);
const { queryId } = yield* startQuery({
queryString: "fields @timestamp, @message | limit 10",
startTime: startEpochSeconds,
endTime: endEpochSeconds,
});

Wire into a Lambda Function

// Insights queries are asynchronous: start one, then poll with the
// GetQueryResults binding. Provide both HTTP layers with Layer.mergeAll.
export default InsightsFunction.make(
{ main: import.meta.url, functionUrl: true },
Effect.gen(function* () {
const logGroup = yield* AWS.Logs.LogGroup("AppLogs", {});
const startQuery = yield* AWS.Logs.StartQuery(logGroup);
const getQueryResults = yield* AWS.Logs.GetQueryResults(logGroup);
return {
fetch: Effect.gen(function* () {
const now = yield* Clock.currentTimeMillis;
const { queryId } = yield* startQuery({
queryString: "fields @timestamp, @message | limit 10",
startTime: Math.floor(now / 1000) - 3600,
endTime: Math.floor(now / 1000),
});
return HttpServerResponse.json({ queryId });
}),
};
}).pipe(
Effect.provide(
Layer.mergeAll(AWS.Logs.StartQueryHttp, AWS.Logs.GetQueryResultsHttp),
),
),
);

Source: src/AWS/Logs/StopQuery.ts

Runtime binding for logs:StopQuery (CloudWatch Logs Insights).

Bind this operation to the LogGroup an Insights query was started against (via StartQuery) to cancel it while it is still Scheduled or Running — e.g. on caller timeout or shutdown.

const startQuery = yield* AWS.Logs.StartQuery(logGroup);
const stopQuery = yield* AWS.Logs.StopQuery(logGroup);
const { queryId } = yield* startQuery({ queryString, startTime, endTime });
const { success } = yield* stopQuery({ queryId: queryId! });

Source: src/AWS/Logs/SubscriptionFilter.ts

A CloudWatch Logs subscription filter — fans matching log events out of a log group to a Lambda function, Kinesis stream, Firehose delivery stream, or cross-account logs destination. A log group supports at most two subscription filters.

For the Lambda-consumer DX (subscribe a Lambda to a log group with automatic permission wiring and payload decoding), prefer consumeLogEvents.

SubscriptionFilter: Subscribing a Lambda Function

Section titled “SubscriptionFilter: Subscribing a Lambda Function”
const filter = yield* SubscriptionFilter("ErrorFanout", {
logGroupName: logGroup.logGroupName,
filterPattern: "?ERROR ?Error",
destinationArn: fn.functionArn,
});

SubscriptionFilter: Subscribing a Kinesis Stream

Section titled “SubscriptionFilter: Subscribing a Kinesis Stream”
const filter = yield* SubscriptionFilter("StreamFanout", {
logGroupName: logGroup.logGroupName,
filterPattern: "",
destinationArn: stream.streamArn,
roleArn: role.roleArn,
distribution: "ByLogStream",
});