Le orchestrazioni di Durable Functions vengono implementate nel codice e possono usare le funzionalità predefinite di gestione degli errori del linguaggio di programmazione. Non è necessario apprendere nuovi concetti per aggiungere la gestione e la compensazione degli errori nelle orchestrazioni. È tuttavia opportuno conoscere alcuni comportamenti.
Note
La versione 4 del modello di programmazione Node.js per Funzioni di Azure è disponibile a livello generale. Il nuovo modello v4 è progettato per offrire un'esperienza più flessibile e intuitiva agli sviluppatori JavaScript e TypeScript. Altre informazioni sulle differenze tra v3 e v4 sono disponibili nella guida alla migrazione.
Nei frammenti di codice seguenti JavaScript (PM4) indica il modello di programmazione V4, la nuova esperienza.
Errori nelle funzioni di attività e nelle sotto-orchestrazioni
In Durable Functions, viene effettuato il marshalling delle eccezioni non gestite, generate all'interno di funzioni di attività o orchestrazioni secondarie, alla funzione dell'agente usando tipi di eccezione standardizzati.
Si consideri, ad esempio, la seguente funzione dell'agente di orchestrazione che esegue un trasferimento di fondi tra due conti:
In Durable Functions C# in-process le eccezioni non gestite vengono generate come FunctionFailedException.
Il messaggio di eccezione identifica in genere quali funzioni di attività o sotto orchestrazioni hanno causato l'errore. Per accedere a informazioni più dettagliate sull'errore, esaminare la InnerException proprietà .
[FunctionName("TransferFunds")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var transferDetails = context.GetInput<TransferOperation>();
await context.CallActivityAsync("DebitAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
try
{
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.DestinationAccount,
Amount = transferDetails.Amount
});
}
catch (FunctionFailedException)
{
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
}
}
Note
Gli esempi C# precedenti sono relativi a Durable Functions 2.x. Per Durable Functions 1.x, è necessario usare DurableOrchestrationContext anziché IDurableOrchestrationContext. Per altre informazioni sulle differenze tra le versioni, vedere l'articolo Versioni di Durable Functions.
In Durable Functions C# Isolated, le eccezioni non gestite vengono esposte come TaskFailedException.
Il messaggio di eccezione identifica in genere quali funzioni di attività o sotto orchestrazioni hanno causato l'errore. Per accedere a informazioni più dettagliate sull'errore, esaminare la proprietà FailureDetails .
[FunctionName("TransferFunds")]
public static async Task Run(
[OrchestrationTrigger] TaskOrchestrationContext context, TransferOperation transferDetails)
{
await context.CallActivityAsync("DebitAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
try
{
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.DestinationAccount,
Amount = transferDetails.Amount
});
}
catch (TaskFailedException)
{
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
}
}
Note
- Il messaggio di eccezione identifica in genere quali funzioni di attività o sotto orchestrazioni hanno causato l'errore. Per accedere a informazioni più dettagliate sull'errore, esaminare la
FailureDetails proprietà .
- Per impostazione predefinita,
FailureDetails include il tipo di errore, il messaggio di errore, l'analisi dello stack ed eventuali eccezioni interne annidate (ognuna rappresentata come oggetto ricorsivo FailureDetails ). Se si desidera includere proprietà di eccezione aggiuntive nell'output degli errori, vedere Includere proprietà di eccezione personalizzate per FailureDetails (.NET Isolated).
const df = require("durable-functions");
module.exports = df.orchestrator(function* (context) {
const transferDetails = context.df.getInput();
yield context.df.callActivity("DebitAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
try {
yield context.df.callActivity("CreditAccount", {
account: transferDetails.destinationAccount,
amount: transferDetails.amount,
});
} catch (error) {
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
yield context.df.callActivity("CreditAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
}
})
const df = require("durable-functions");
df.app.orchestration("transferFunds", function* (context) {
const transferDetails = context.df.getInput();
yield context.df.callActivity("debitAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
try {
yield context.df.callActivity("creditAccount", {
account: transferDetails.destinationAccount,
amount: transferDetails.amount,
});
} catch (error) {
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
yield context.df.callActivity("creditAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
}
});
import azure.functions as func
import azure.durable_functions as df
def orchestrator_function(context: df.DurableOrchestrationContext):
transfer_details = context.get_input()
yield context.call_activity('DebitAccount', {
'account': transfer_details['sourceAccount'],
'amount' : transfer_details['amount']
})
try:
yield context.call_activity('CreditAccount', {
'account': transfer_details['destinationAccount'],
'amount': transfer_details['amount'],
})
except:
yield context.call_activity('CreditAccount', {
'account': transfer_details['sourceAccount'],
'amount': transfer_details['amount']
})
main = df.Orchestrator.create(orchestrator_function)
Per impostazione predefinita, i cmdlet in PowerShell non generano eccezioni che possono essere rilevate usando blocchi try/catch. Sono disponibili due opzioni per modificare questo comportamento:
- Usare il flag
-ErrorAction Stop quando si richiamano cmdlet, ad esempio Invoke-DurableActivity.
- Impostare la variabile di preferenza
$ErrorActionPreference su "Stop" nella funzione dell'agente di orchestrazione prima di richiamare i cmdlet.
param($Context)
$ErrorActionPreference = "Stop"
$transferDetails = $Context.Input
Invoke-DurableActivity -FunctionName 'DebitAccount' -Input @{ account = transferDetails.sourceAccount; amount = transferDetails.amount }
try {
Invoke-DurableActivity -FunctionName 'CreditAccount' -Input @{ account = transferDetails.destinationAccount; amount = transferDetails.amount }
} catch {
Invoke-DurableActivity -FunctionName 'CreditAccount' -Input @{ account = transferDetails.sourceAccount; amount = transferDetails.amount }
}
Per altre informazioni sulla gestione degli errori in PowerShell, vedere la documentazione di PowerShell Try-Catch-Finally.
@FunctionName("TransferFunds")
public void transferFunds(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
TransferOperation transfer = ctx.getInput(TransferOperation.class);
ctx.callActivity(
"DebitAccount",
new OperationArgs(transfer.sourceAccount, transfer.amount)).await();
try {
ctx.callActivity(
"CreditAccount",
new OperationArgs(transfer.destinationAccount, transfer.amount)).await();
} catch (TaskFailedException ex) {
// Refund the source account on failure
ctx.callActivity(
"CreditAccount",
new OperationArgs(transfer.sourceAccount, transfer.amount)).await();
}
}
Se la prima chiamata di funzione CreditAccount ha esito negativo, la funzione dell'agente di orchestrazione la compensa riaccreditando i fondi all'account di origine.
Errori nelle funzioni di entità
Il comportamento di gestione delle eccezioni per le funzioni di entità è diverso in base al modello di hosting Durable Functions.
Se si utilizza C# in-process in Durable Functions, i tipi di eccezione originali generati dalle funzioni di entità vengono restituiti direttamente all'agente di orchestrazione.
[FunctionName("Function1")]
public static async Task<string> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
try
{
var entityId = new EntityId(nameof(Counter), "myCounter");
await context.CallEntityAsync(entityId, "Add", 1);
}
catch (Exception ex)
{
// The exception type will be InvalidOperationException with the message "this is an entity exception".
}
return string.Empty;
}
[FunctionName("Counter")]
public static void Counter([EntityTrigger] IDurableEntityContext ctx)
{
switch (ctx.OperationName.ToLowerInvariant())
{
case "add":
throw new InvalidOperationException("this is an entity exception");
case "get":
ctx.Return(ctx.GetState<int>());
break;
}
}
Se si utilizza C# isolato in Durable Functions, le eccezioni vengono visualizzate nell'agente di orchestrazione come EntityOperationFailedException. Per accedere ai dettagli dell'eccezione originale, controllare la relativa FailureDetails proprietà.
[Function(nameof(MyOrchestrator))]
public static async Task<List<string>> MyOrchestrator(
[Microsoft.Azure.Functions.Worker.OrchestrationTrigger] TaskOrchestrationContext context)
{
var entityId = new Microsoft.DurableTask.Entities.EntityInstanceId(nameof(Counter), "myCounter");
try
{
await context.Entities.CallEntityAsync(entityId, "Add", 1);
}
catch (EntityOperationFailedException ex)
{
// Add your error handling
}
return new List<string>();
}
df.app.orchestration("counterOrchestration", function* (context) {
const entityId = new df.EntityId(counterEntityName, "myCounter");
try {
const currentValue = yield context.df.callEntity(entityId, "get");
if (currentValue < 10) {
yield context.df.callEntity(entityId, "add", 1);
}
} catch (err) {
context.log(`Entity call failed: ${err.message ?? err}`);
}
});
df.app.orchestration("counterOrchestration", function* (context) {
const entityId = new df.EntityId(counterEntityName, "myCounter");
try {
const currentValue = yield context.df.callEntity(entityId, "get");
if (currentValue < 10) {
yield context.df.callEntity(entityId, "add", 1);
}
} catch (err) {
context.log(`Entity call failed: ${err.message ?? err}`);
}
});
@myApp.orchestration_trigger(context_name="context")
def run_orchestrator(context):
try:
entityId = df.EntityId("Counter", "myCounter")
yield context.call_entity(entityId, "get")
return "finished"
except Exception as e:
# Add your error handling
Le funzioni di entità non sono attualmente supportate in PowerShell.
Le funzioni di entità non sono attualmente supportate in Java.
Ripetizione automatica in caso di errore
Quando si chiamano le funzioni di attività o di orchestrazione secondaria, è possibile specificare un criterio di ripetizione automatica. Nell'esempio seguente si tenta di chiamare una funzione fino a tre volte e si attende 5 secondi tra un tentativo e l'altro:
[FunctionName("TimerOrchestratorWithRetry")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var retryOptions = new RetryOptions(
firstRetryInterval: TimeSpan.FromSeconds(5),
maxNumberOfAttempts: 3);
await context.CallActivityWithRetryAsync("FlakyFunction", retryOptions, null);
// ...
}
Note
Gli esempi C# precedenti sono relativi a Durable Functions 2.x. Per Durable Functions 1.x, è necessario usare DurableOrchestrationContext anziché IDurableOrchestrationContext. Per altre informazioni sulle differenze tra le versioni, vedere l'articolo Versioni di Durable Functions.
[FunctionName("TimerOrchestratorWithRetry")]
public static async Task Run([OrchestrationTrigger] TaskOrchestrationContext context)
{
var options = TaskOptions.FromRetryPolicy(new RetryPolicy(
maxNumberOfAttempts: 3,
firstRetryInterval: TimeSpan.FromSeconds(5)));
await context.CallActivityAsync("FlakyFunction", options: options);
// ...
}
const df = require("durable-functions");
module.exports = df.orchestrator(function*(context) {
const firstRetryIntervalInMilliseconds = 5000;
const maxNumberOfAttempts = 3;
const retryOptions =
new df.RetryOptions(firstRetryIntervalInMilliseconds, maxNumberOfAttempts);
yield context.df.callActivityWithRetry("FlakyFunction", retryOptions);
// ...
});
const df = require("durable-functions");
df.app.orchestration("callActivityWithRetry", function* (context) {
const firstRetryIntervalInMilliseconds = 5000;
const maxNumberOfAttempts = 3;
const retryOptions = new df.RetryOptions(firstRetryIntervalInMilliseconds, maxNumberOfAttempts);
yield context.df.callActivityWithRetry("flakyFunction", retryOptions);
// ...
});
import azure.functions as func
import azure.durable_functions as df
def orchestrator_function(context: df.DurableOrchestrationContext):
first_retry_interval_in_milliseconds = 5000
max_number_of_attempts = 3
retry_options = df.RetryOptions(first_retry_interval_in_milliseconds, max_number_of_attempts)
yield context.call_activity_with_retry('FlakyFunction', retry_options)
main = df.Orchestrator.create(orchestrator_function)
param($Context)
$retryOptions = New-DurableRetryOptions `
-FirstRetryInterval (New-TimeSpan -Seconds 5) `
-MaxNumberOfAttempts 3
Invoke-DurableActivity -FunctionName 'FlakyFunction' -RetryOptions $retryOptions
@FunctionName("TimerOrchestratorWithRetry")
public void timerOrchestratorWithRetry(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
final int maxAttempts = 3;
final Duration firstRetryInterval = Duration.ofSeconds(5);
RetryPolicy policy = new RetryPolicy(maxAttempts, firstRetryInterval);
TaskOptions options = new TaskOptions(policy);
ctx.callActivity("FlakeyFunction", options).await();
// ...
}
La chiamata di funzione dell'attività nell'esempio precedente accetta un parametro per la configurazione di un criterio di ripetizione automatica. Sono disponibili diverse opzioni per personalizzare il criterio di ripetizione automatica:
-
Numero massimo di tentativi: numero massimo di tentativi. Se impostato su 1, non verrà eseguito alcun nuovo tentativo.
-
First retry interval (Intervallo primo tentativo): il tempo di attesa prima del primo tentativo.
-
Backoff coefficient (Coefficiente di backoff): il coefficiente usato per determinare la frequenza di aumento del backoff. Assume il valore predefinito 1.
-
Max retry interval (Intervallo massimo tra tentativi): il tempo di attesa massimo tra i tentativi di ripetizione.
-
Retry timeout (Timeout tentativi): il tempo massimo a disposizione per i tentativi. Il comportamento predefinito è la ripetizione per un periodo illimitato.
Gestori di tentativi personalizzati
Quando si usa .NET o Java, è anche possibile implementare gestori di ripetizione nel codice. Ciò è utile quando i criteri di ripetizione dichiarativi non sono sufficientemente espressivi. Per i linguaggi che non supportano gestori di tentativi personalizzati, è comunque possibile implementare criteri di ripetizione usando cicli, gestione delle eccezioni e timer per l'inserimento di ritardi tra i tentativi.
RetryOptions retryOptions = new RetryOptions(
firstRetryInterval: TimeSpan.FromSeconds(5),
maxNumberOfAttempts: int.MaxValue)
{
Handle = exception =>
{
// True to handle and try again, false to not handle and throw.
if (exception is TaskFailedException failure)
{
// Exceptions from TaskActivities are always this type. Inspect the
// inner Exception to get more details.
}
return false;
};
}
await ctx.CallActivityWithRetryAsync("FlakeyActivity", retryOptions, null);
TaskOptions retryOptions = TaskOptions.FromRetryHandler(retryContext =>
{
// Don't retry anything that derives from ApplicationException
if (retryContext.LastFailure.IsCausedBy<ApplicationException>())
{
return false;
}
// Quit after N attempts
return retryContext.LastAttemptNumber < 3;
});
try
{
await ctx.CallActivityAsync("FlakeyActivity", options: retryOptions);
}
catch (TaskFailedException)
{
// Case when the retry handler returns false...
}
JavaScript non supporta attualmente gestori di ripetizioni personalizzati. Tuttavia, è comunque possibile implementare la logica di ripetizione direttamente nella funzione dell'agente di orchestrazione usando cicli, gestione delle eccezioni e timer per l'inserimento di ritardi tra i tentativi.
JavaScript non supporta attualmente gestori di ripetizioni personalizzati. Tuttavia, è comunque possibile implementare la logica di ripetizione direttamente nella funzione dell'agente di orchestrazione usando cicli, gestione delle eccezioni e timer per l'inserimento di ritardi tra i tentativi.
Python non supporta attualmente gestori di tentativi personalizzati. Tuttavia, è comunque possibile implementare la logica di ripetizione direttamente nella funzione dell'agente di orchestrazione usando cicli, gestione delle eccezioni e timer per l'inserimento di ritardi tra i tentativi.
PowerShell non supporta attualmente gestori di tentativi personalizzati. Tuttavia, è comunque possibile implementare la logica di ripetizione direttamente nella funzione dell'agente di orchestrazione usando cicli, gestione delle eccezioni e timer per l'inserimento di ritardi tra i tentativi.
RetryHandler retryHandler = retryCtx -> {
// Don't retry anything that derives from RuntimeException
if (retryCtx.getLastFailure().isCausedBy(RuntimeException.class)) {
return false;
}
// Quit after N attempts
return retryCtx.getLastAttemptNumber() < 3;
};
TaskOptions options = new TaskOptions(retryHandler);
try {
ctx.callActivity("FlakeyActivity", options).await();
} catch (TaskFailedException ex) {
// Case when the retry handler returns false...
}
Timeout delle funzioni
Potrebbe essere necessario abbandonare una chiamata di funzione all'interno di una funzione dell'agente di orchestrazione se il completamento richiede troppo tempo. Al momento, il modo corretto per eseguire questa operazione è la creazione di un timer durevole con un selettore di attività "any", come nell'esempio seguente:
[FunctionName("TimerOrchestrator")]
public static async Task<bool> Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
TimeSpan timeout = TimeSpan.FromSeconds(30);
DateTime deadline = context.CurrentUtcDateTime.Add(timeout);
using (var cts = new CancellationTokenSource())
{
Task activityTask = context.CallActivityAsync("FlakyFunction");
Task timeoutTask = context.CreateTimer(deadline, cts.Token);
Task winner = await Task.WhenAny(activityTask, timeoutTask);
if (winner == activityTask)
{
// success case
cts.Cancel();
return true;
}
else
{
// timeout case
return false;
}
}
}
Note
Gli esempi C# precedenti sono relativi a Durable Functions 2.x. Per Durable Functions 1.x, è necessario usare DurableOrchestrationContext anziché IDurableOrchestrationContext. Per altre informazioni sulle differenze tra le versioni, vedere l'articolo Versioni di Durable Functions.
[Function("TimerOrchestrator")]
public static async Task<bool> Run([OrchestrationTrigger] TaskOrchestrationContext context)
{
TimeSpan timeout = TimeSpan.FromSeconds(30);
DateTime deadline = context.CurrentUtcDateTime.Add(timeout);
using (var cts = new CancellationTokenSource())
{
Task activityTask = context.CallActivityAsync("FlakyFunction");
Task timeoutTask = context.CreateTimer(deadline, cts.Token);
Task winner = await Task.WhenAny(activityTask, timeoutTask);
if (winner == activityTask)
{
// success case
cts.Cancel();
return true;
}
else
{
// timeout case
return false;
}
}
}
const df = require("durable-functions");
const moment = require("moment");
module.exports = df.orchestrator(function*(context) {
const deadline = moment.utc(context.df.currentUtcDateTime).add(30, "s");
const activityTask = context.df.callActivity("FlakyFunction");
const timeoutTask = context.df.createTimer(deadline.toDate());
const winner = yield context.df.Task.any([activityTask, timeoutTask]);
if (winner === activityTask) {
// success case
timeoutTask.cancel();
return true;
} else {
// timeout case
return false;
}
});
const df = require("durable-functions");
const { DateTime } = require("luxon");
df.app.orchestration("timerOrchestrator", function* (context) {
const deadline = DateTime.fromJSDate(context.df.currentUtcDateTime).plus({ seconds: 30 });
const activityTask = context.df.callActivity("flakyFunction");
const timeoutTask = context.df.createTimer(deadline.toJSDate());
const winner = yield context.df.Task.any([activityTask, timeoutTask]);
if (winner === activityTask) {
// success case
timeoutTask.cancel();
return true;
} else {
// timeout case
return false;
}
});
import azure.functions as func
import azure.durable_functions as df
from datetime import datetime, timedelta
def orchestrator_function(context: df.DurableOrchestrationContext):
deadline = context.current_utc_datetime + timedelta(seconds = 30)
activity_task = context.call_activity('FlakyFunction')
timeout_task = context.create_timer(deadline)
winner = yield context.task_any(activity_task, timeout_task)
if winner == activity_task:
timeout_task.cancel()
return True
else:
return False
main = df.Orchestrator.create(orchestrator_function)
param($Context)
$expiryTime = New-TimeSpan -Seconds 30
$activityTask = Invoke-DurableActivity -FunctionName 'FlakyFunction'-NoWait
$timerTask = Start-DurableTimer -Duration $expiryTime -NoWait
$winner = Wait-DurableTask -Task @($activityTask, $timerTask) -NoWait
if ($winner -eq $activityTask) {
Stop-DurableTimerTask -Task $timerTask
return $True
}
else {
return $False
}
@FunctionName("TimerOrchestrator")
public boolean timerOrchestrator(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
Task<Void> activityTask = ctx.callActivity("SlowFunction");
Task<Void> timeoutTask = ctx.createTimer(Duration.ofMinutes(30));
Task<?> winner = ctx.anyOf(activityTask, timeoutTask).await();
if (winner == activityTask) {
// success case
return true;
} else {
// timeout case
return false;
}
}
Note
Questo meccanismo non termina effettivamente l'esecuzione della funzione di attività in corso. ma consente semplicemente alla funzione di orchestrazione di ignorare il risultato e continuare. Per altre informazioni, vedere la documentazione sui Timer.
Eccezioni non gestite
Se una funzione dell'agente di orchestrazione ha esito negativo generando un'eccezione non gestita, i dettagli dell'eccezione vengono registrati e l'istanza viene completata con uno stato Failed.
Includere proprietà di eccezione personalizzate per FailureDetails (.NET isolato)
Quando si eseguono flussi di lavoro Durable Task nel modello isolato .NET, gli errori delle attività vengono serializzati automaticamente in un oggetto FailureDetails. Per impostazione predefinita, questo oggetto include campi standard, ad esempio:
- ErrorType : nome del tipo di eccezione
- Messaggio : messaggio di eccezione
- StackTrace: analisi dello stack serializzato
- InnerFailure: oggetto FailureDetails annidato per le eccezioni interne ricorsive
A partire da Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0, è possibile estendere questo comportamento implementando un IExceptionPropertiesProvider (definito nel pacchetto Microsoft.DurableTask.Worker a partire dal pacchetto v1.16.1). Questo provider definisce i tipi di eccezione e quali delle relative proprietà devono essere inclusi nel dizionario FailureDetails.Properties.
Note
- Questa funzionalità è disponibile solo in .NET Isolated . Il supporto per Java verrà aggiunto in una versione futura.
- Assicurarsi di usare Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0 o versione successiva.
- Assicurarsi di usare Microsoft.DurableTask.Worker v1.16.1 o versione successiva.
Implementare un fornitore di proprietà delle eccezioni
Implementare un oggetto IExceptionPropertiesProvider personalizzato per estrarre e restituire le proprietà selezionate per le eccezioni di cui si è a cuore. Il dizionario restituito verrà serializzato nel campo Proprietà di FailureDetails quando viene generato un tipo di eccezione corrispondente.
using Microsoft.DurableTask.Worker;
public class CustomExceptionPropertiesProvider : IExceptionPropertiesProvider
{
public IDictionary<string, object?>? GetExceptionProperties(Exception exception)
{
return exception switch
{
ArgumentOutOfRangeException e => new Dictionary<string, object?>
{
["ParamName"] = e.ParamName,
["ActualValue"] = e.ActualValue
},
InvalidOperationException e => new Dictionary<string, object?>
{
["CustomHint"] = "Invalid operation occurred",
["TimestampUtc"] = DateTime.UtcNow
},
_ => null // Other exception types not handled
};
}
}
Registrare il provider
Registra il tuo oggetto IExceptionPropertiesProvider personalizzato nel tuo host di lavoro isolato .NET, in genere in Program.cs:
using Microsoft.DurableTask.Worker;
using Microsoft.Extensions.DependencyInjection;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(builder =>
{
// Register custom exception properties provider
builder.Services.AddSingleton<IExceptionPropertiesProvider, CustomExceptionPropertiesProvider>();
})
.Build();
host.Run();
Dopo la registrazione, qualsiasi eccezione che corrisponde a uno dei tipi gestiti includerà automaticamente le proprietà configurate nei relativi failureDetails.
Output FailureDetails di esempio
Quando si verifica un'eccezione che corrisponde alla configurazione del provider, l'orchestrazione riceve una struttura FailureDetails serializzata simile alla seguente:
{
"errorType": "TaskFailedException",
"message": "Activity failed with an exception.",
"stackTrace": "...",
"innerFailure": {
"errorType": "ArgumentOutOfRangeException",
"message": "Specified argument was out of range.",
"properties": {
"ParamName": "count",
"ActualValue": 42
}
}
}
Passaggi successivi