Skip to content

Add replay-safe logging#295

Open
bachuv wants to merge 2 commits into
mainfrom
vabachu/replay-safe-logger-java
Open

Add replay-safe logging#295
bachuv wants to merge 2 commits into
mainfrom
vabachu/replay-safe-logger-java

Conversation

@bachuv

@bachuv bachuv commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

This pull request introduces a new replay-safe logging feature for orchestrations, ensuring that log output is suppressed during replay segments to prevent duplicate log entries. It includes the implementation of a ReplaySafeLogger, integration into the orchestration context, comprehensive documentation, and a new integration test to validate the behavior.

Replay-safe logging feature:

  • Added the ReplaySafeLogger class, which wraps a standard Logger and suppresses log output when the orchestration is replaying. This prevents duplicate logs during state reconstruction. (client/src/main/java/com/microsoft/durabletask/ReplaySafeLogger.java)
  • Introduced the createReplaySafeLogger default method to the TaskOrchestrationContext interface, allowing orchestrator code to easily obtain a replay-safe logger. (client/src/main/java/com/microsoft/durabletask/TaskOrchestrationContext.java)
  • Updated the documentation to explain how to use replay-safe logging, including code examples and guidance for Azure Functions scenarios. (README.md)
  • Added a changelog entry describing the new createReplaySafeLogger API. (CHANGELOG.md)

Testing:

  • Added an integration test to verify that log messages from orchestrators and activities are emitted exactly once, even when orchestrations replay. (client/src/test/java/com/microsoft/durabletask/IntegrationTests.java)

These changes improve the reliability and usability of orchestration logging by preventing duplicate log entries during replay, while providing clear documentation and tests for the new functionality.

Pull request checklist

  • My changes do not require documentation changes
    • Otherwise: Documentation issue linked to PR
  • My changes are added to the CHANGELOG.md
  • I have added all required tests (Unit tests, E2E tests)

Additional information

Additional PR information

Copilot AI review requested due to automatic review settings July 21, 2026 00:12
@bachuv
bachuv requested a review from a team as a code owner July 21, 2026 00:12
Comment thread client/src/main/java/com/microsoft/durabletask/ReplaySafeLogger.java Dismissed
Comment thread client/src/main/java/com/microsoft/durabletask/ReplaySafeLogger.java Dismissed
Comment thread client/src/main/java/com/microsoft/durabletask/ReplaySafeLogger.java Dismissed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a replay-safe orchestration logging capability to the Durable Task Java client by providing a Logger wrapper that suppresses log emissions during orchestration replay, reducing duplicated log output.

Changes:

  • Introduces ReplaySafeLogger and a TaskOrchestrationContext#createReplaySafeLogger(Logger) convenience API.
  • Documents replay-safe logging usage patterns (including Azure Functions guidance) in the README and adds a changelog entry.
  • Adds unit and integration tests validating suppression during replay and single emission during live segments.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
samples-azure-functions/src/main/java/com/functions/AzureFunctions.java Updates the Azure Functions sample orchestrator to use createReplaySafeLogger for replay-safe logs.
README.md Adds documentation and examples for replay-safe orchestration logging.
client/src/main/java/com/microsoft/durabletask/TaskOrchestrationContext.java Adds the createReplaySafeLogger(Logger) default method API.
client/src/main/java/com/microsoft/durabletask/ReplaySafeLogger.java Implements the replay-suppressing Logger wrapper.
client/src/test/java/com/microsoft/durabletask/ReplaySafeLoggerTest.java Adds focused unit tests for suppression behavior and delegation semantics.
client/src/test/java/com/microsoft/durabletask/TaskOrchestrationExecutorTest.java Adds an executor-level test validating replay suppression vs live emission.
client/src/test/java/com/microsoft/durabletask/IntegrationTests.java Adds an end-to-end integration test covering parent/child orchestrations and activity logging behavior.
CHANGELOG.md Records the new createReplaySafeLogger API addition.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread client/src/test/java/com/microsoft/durabletask/TaskOrchestrationExecutorTest.java Outdated
Comment thread README.md Outdated
Copilot AI review requested due to automatic review settings July 22, 2026 21:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

@YunchuWang YunchuWang left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this — the design is careful and the test coverage is genuinely strong (the reflection-based allPublicLoggerMethodsAreClassified guard is a nice touch for pinning JDK behavior). A few things worth confirming before merge; nothing here is a hard blocker.

Strengths

  • isReplaying is read lazily per call via this::getIsReplaying, so replay state is evaluated at log time on the orchestrator thread — correct timing.
  • Using the protected Logger(name, null) constructor avoids LogManager registration, so creating a wrapper on every replay is cheap and leak-free. Good call.
  • Supplier-based overloads are correctly short-circuited before evaluation during replay, and that's explicitly tested.
  • Executor-level + integration tests validate suppress-on-replay vs emit-on-live, including asserting that replay actually occurred (parentExecutions/childExecutions >= 2).

Main thing to confirm — Java 8 target vs Java 9+ API

client/build.gradle sets sourceCompatibility/targetCompatibility = 1.8, but ReplaySafeLogger overrides Logger methods introduced in Java 9:

  • log(Level, Throwable, Supplier)
  • logp(Level, String, String, Throwable, Supplier)
  • logrb(Level, String, String, ResourceBundle, String, Throwable) (plus the inherited logrb(Level, ResourceBundle, …) overloads the tests exercise)

This compiles in CI because the build runs on JDK 11 with -source/-target 8 (not --release 8), so the newer Logger API is on the compile classpath. The published artifact is therefore Java 8 bytecode that references Java 9+ methods. On a Java 8 runtime the class loads and the Java 8 methods work fine, but those specific overloads would throw NoSuchMethodError if invoked. Practical risk is low — a Java 8-compiled caller can't reach them through a Logger-typed reference, and JUL never routes into them internally — but it's inconsistent with the stated Java 8 target.

Could we confirm the intended minimum JDK? If Java 8 must be supported, this isn't a faithful drop-in there; if it doesn't need to be, using options.release = 8 (or bumping the target) would remove the ambiguity and catch this class of issue at compile time.

Minor / documented tradeoffs (fine to leave — just flagging)

  • isLoggable(Level) ignores replay, so the guard idiom if (logger.isLoggable(FINE)) logger.fine(expensive()) still builds the message during replay before it's suppressed. The javadoc already steers users to supplier methods, which is reasonable — just confirming it's intentional rather than making isLoggable replay-aware.
  • Source class/method inference is lost for everything except logp (records get attributed to ReplaySafeLogger.log). Immaterial for the Azure Functions ExecutionContext.getLogger() path and it's documented, so no action needed.

Nits

  • ReplaySafeLogger.java and ReplaySafeLoggerTest.java are missing a trailing newline.
  • The CHANGELOG entry could include the ([#295](…)) link to match the style of the entry below it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants