This article is a machine translation of the contents of the following URL, which I wrote in Japanese:
Introduction
Hi, I'm @H0ukiStar.
Have you tried using Lambda Durable Functions?
Lambda Durable Functions is a feature that allows you to implement multi-step workflows—similar to Step Functions—using only a single Lambda function.
When implementing logging in Lambda Durable Functions, it is recommended to use context.logger and step_context.logger. But why is that?
In this article, I’ll explain the reason based on actual behavior and implementation details.
Logging with the Standard Method
First, in a typical Lambda function (Python), logging is usually done using the built-in logging module, like this:
import logging
logger: logging.Logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.info("Loading lambda_function.py")
Below is a sample where logging is performed using the logging module inside both steps and the lambda_handler in Lambda Durable Functions:
import logging
from aws_durable_execution_sdk_python.config import Duration
from aws_durable_execution_sdk_python.execution import durable_execution
from aws_durable_execution_sdk_python.context import DurableContext, StepContext, durable_step
logger: logging.Logger = logging.getLogger()
logger.setLevel(logging.INFO)
@durable_step
def step_one(step_context: StepContext, arg: int) -> str:
logger.info("Hello from step_one")
return f"from step_one: {arg=}"
@durable_step
def step_two(step_context: StepContext, arg: int) -> str:
logger.info("Hello from step_two")
return f"from step_two: {arg=}"
@durable_step
def step_three(step_context: StepContext, arg: int) -> str:
logger.info("Hello from step_three")
return f"from step_three: {arg=}"
@durable_execution
def lambda_handler(event: dict, context: DurableContext) -> None:
msg_one: str = context.step(step_one(123))
logger.info(msg_one)
msg_two: str = context.step(step_two(456))
logger.info(msg_two)
context.wait(Duration.from_seconds(30))
msg_three: str = context.step(step_three(789))
logger.info(msg_three)
Execution Result
When you run this code and check the logs in CloudWatch, you will see output like this:
In summary, the logs behave as follows:
-
step_one()runs →"Hello from step_one"is logged - The return value
"from step_one: arg=123"is logged (①) -
step_two()runs →"Hello from step_two"is logged - The return value
"from step_two: arg=456"is logged (②) - Execution pauses with
context.wait() - After resuming,
"from step_one: arg=123"is logged again (①') -
"from step_two: arg=456"is logged again (②') -
step_three()runs →"Hello from step_three"is logged - The return value
"from step_three: arg=789"is logged - You can see that logs before and after
context.wait()are duplicated.
Recommended Logging Method
As mentioned earlier, in Lambda Durable Functions:
- Use
context.loggerin the handler - Use
step_context.loggerinside steps
Logging—I use context.logger for the main handler and step_context.logger inside steps. The context logger suppresses duplicate logs during replay.
Here is the updated version of the previous code using context.logger:
from aws_durable_execution_sdk_python.config import Duration
from aws_durable_execution_sdk_python.execution import durable_execution
from aws_durable_execution_sdk_python.context import DurableContext, StepContext, durable_step
@durable_step
def step_one(step_context: StepContext, arg: int) -> str:
step_context.logger.info("Hello from step_one")
return f"from step_one: {arg=}"
@durable_step
def step_two(step_context: StepContext, arg: int) -> str:
step_context.logger.info("Hello from step_two")
return f"from step_two: {arg=}"
@durable_step
def step_three(step_context: StepContext, arg: int) -> str:
step_context.logger.info("Hello from step_three")
return f"from step_three: {arg=}"
@durable_execution
def lambda_handler(event: dict, context: DurableContext) -> None:
msg_one: str = context.step(step_one(123))
context.logger.info(msg_one)
msg_two: str = context.step(step_two(456))
context.logger.info(msg_two)
context.wait(Duration.from_seconds(30))
msg_three: str = context.step(step_three(789))
context.logger.info(msg_three)
Execution Result
The output now looks like this:
Unlike the previous case, there are no duplicated logs before and after context.wait().
Why This Is Recommended
1. Logs in the handler are not duplicated
From the results above, logs written using context.logger are not duplicated.
This is because Lambda Durable Functions uses a mechanism called replay.
When execution resumes after context.wait(), the function does NOT continue from where it left off. Instead, the handler is executed again from the beginning.
During this process:
- Completed steps are skipped using checkpoint logs
- Their results are reused instead of re-executing the step
When your function resumes after a pause or interruption, the SDK performs replay:
- Load checkpoint log: The SDK retrieves the checkpoint log for this execution from Lambda.
- Run from beginning: The SDK invokes your handler function from the start, not from where it paused.
- Skip completed durable operations: As your code calls durable operations, the SDK checks each against the checkpoint log. For completed durable operations, the SDK returns the stored result without executing the operation code.
As a result:
- Step functions are skipped → no duplicate logs inside steps
- But handler code runs again → duplicate logs when using
logging
On the other hand, context.logger is implemented to suppress duplicate logs during replay:
2. Logs can be viewed in the "Logger output" tab
You might think:
"If step functions are skipped anyway, why not just use logging?"
While that works for avoiding duplication, there is another benefit.
Logs written using context.logger or step_context.logger can be viewed in the "Logger output" tab in the Durable Execution management console.
This allows you to:
- View logs per execution
- See timestamps and messages in a structured way
Internally, this is just a CloudWatch Logs Insights query:
fields timestamp, message
| filter executionArn like "arn:aws:lambda:{region}:{accountId}:function:{functionname}:{version or alias}/durable-execution/{executionId}/{invocationId}"
You could replicate this format manually, but it's much easier to just use step_context.logger.
Conclusion
In this article, we explored why context.logger and step_context.logger are recommended in Lambda Durable Functions, based on both behavior and SDK implementation.
Lambda Durable Functions is a powerful feature that enables multi-step workflows within a single Lambda function.
I hope this article helps you when implementing Durable Functions in your own projects.



Top comments (0)