Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public final class DurableTaskGrpcWorker implements AutoCloseable {
private final DataConverter dataConverter;
private final Duration maximumTimerInterval;
private final DurableTaskGrpcWorkerVersioningOptions versioningOptions;
private final ExceptionPropertiesProvider exceptionPropertiesProvider;
private final int maxConcurrentEntityWorkItems;
private final ExecutorService workItemExecutor;

Expand Down Expand Up @@ -84,6 +85,7 @@ public final class DurableTaskGrpcWorker implements AutoCloseable {
this.dataConverter = builder.dataConverter != null ? builder.dataConverter : new JacksonDataConverter();
this.maximumTimerInterval = builder.maximumTimerInterval != null ? builder.maximumTimerInterval : DEFAULT_MAXIMUM_TIMER_INTERVAL;
this.versioningOptions = builder.versioningOptions;
this.exceptionPropertiesProvider = builder.exceptionPropertiesProvider;
int maxThreads = builder.maxWorkItemThreads > 0 ? builder.maxWorkItemThreads : DEFAULT_MAX_WORK_ITEM_THREADS;
this.workItemExecutor = new ThreadPoolExecutor(
0, maxThreads,
Expand Down Expand Up @@ -159,7 +161,8 @@ public void startAndBlock() {
this.maximumTimerInterval,
logger,
this.versioningOptions,
true);
true,
this.exceptionPropertiesProvider);
TaskActivityExecutor taskActivityExecutor = new TaskActivityExecutor(
this.activityFactories,
this.dataConverter,
Expand Down Expand Up @@ -389,11 +392,8 @@ public void startAndBlock() {
activityRequest.getTaskId());
} catch (Throwable e) {
activityError = e;
failureDetails = TaskFailureDetails.newBuilder()
.setErrorType(e.getClass().getName())
.setErrorMessage(e.getMessage())
.setStackTrace(StringValue.of(FailureDetails.getFullStackTrace(e)))
.build();
failureDetails = FailureDetails.fromException(
e, this.exceptionPropertiesProvider).toProto();
} finally {
activityScope.close();
TracingHelper.endSpan(activitySpan, activityError);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public final class DurableTaskGrpcWorkerBuilder {
DataConverter dataConverter;
Duration maximumTimerInterval;
DurableTaskGrpcWorkerVersioningOptions versioningOptions;
ExceptionPropertiesProvider exceptionPropertiesProvider;
int maxConcurrentEntityWorkItems = 1;
int maxWorkItemThreads;
private WorkItemFilter workItemFilter;
Expand Down Expand Up @@ -292,6 +293,21 @@ public DurableTaskGrpcWorkerBuilder useVersioning(DurableTaskGrpcWorkerVersionin
return this;
}

/**
* Sets the {@link ExceptionPropertiesProvider} to use for extracting custom properties from exceptions.
* <p>
* When set, the provider is invoked whenever an activity or orchestration fails with an exception. The returned
* properties are included in the {@link FailureDetails} and can be retrieved via
* {@link FailureDetails#getProperties()}.
*
* @param provider the exception properties provider
* @return this builder object
*/
public DurableTaskGrpcWorkerBuilder exceptionPropertiesProvider(ExceptionPropertiesProvider provider) {
this.exceptionPropertiesProvider = provider;
return this;
}

/**
* Sets explicit work item filters for this worker. When set, only work items matching the filters
* will be dispatched to this worker by the backend.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.durabletask;

import javax.annotation.Nullable;
import java.util.Map;

/**
* Provider interface for extracting custom properties from exceptions.
* <p>
* Implementations of this interface can be registered with a {@link DurableTaskGrpcWorkerBuilder} to include
* custom exception properties in {@link FailureDetails} when activities or orchestrations fail.
* These properties are then available via {@link FailureDetails#getProperties()}.
* <p>
* Example usage:
* <pre>{@code
* DurableTaskGrpcWorker worker = new DurableTaskGrpcWorkerBuilder()
* .exceptionPropertiesProvider(exception -> {
* if (exception instanceof MyCustomException) {
* MyCustomException custom = (MyCustomException) exception;
* Map<String, Object> props = new HashMap<>();
* props.put("errorCode", custom.getErrorCode());
* props.put("retryable", custom.isRetryable());
* return props;
* }
* return null;
* })
* .addOrchestration(...)
* .build();
* }</pre>
*/
@FunctionalInterface
public interface ExceptionPropertiesProvider {

/**
* Extracts custom properties from the given exception.
* <p>
* Return {@code null} or an empty map if no custom properties should be included for this exception.
*
* @param exception the exception to extract properties from
* @return a map of property names to values, or {@code null}
*/
@Nullable
Map<String, Object> getExceptionProperties(Exception exception);
}
Loading
Loading