يتم تنفيذ عمليات تنظيم Durable Function في التعليمات البرمجية ويمكنها استخدام ميزات معالجة الأخطاء المضمنة في لغة البرمجة. لا توجد أي مفاهيم جديدة تحتاج إلى تعلم كيفية إضافة معالجة الأخطاء والتعويض في التزامن الخاص بك. ومع ذلك، هناك بعض السلوكيات التي يجب أن تكون على علم بها.
إشعار
يتوفر الإصدار 4 من نموذج البرمجة Node.js ل Azure Functions بشكل عام. تم تصميم نموذج v4 الجديد للحصول على تجربة أكثر مرونة وبديهية لمطوري JavaScript وTypeScript. تعرف على المزيد حول الاختلافات بين v3 وv4 في دليل الترحيل.
في القصاصات البرمجية التالية، يشير JavaScript (PM4) إلى نموذج البرمجة V4، التجربة الجديدة.
أخطاء في وظائف النشاط والتنسيقات الفرعية
في الوظائف الدائمة، يتم تنظيم الاستثناءات غير المعالجة التي يتم طرحها داخل وظائف النشاط أو التنسيقات الفرعية مرة أخرى إلى وظيفة المنسق باستخدام أنواع الاستثناءات القياسية.
على سبيل المثال، ضع في اعتبارك وظيفة المنسق التالية التي تقوم بتحويل الأموال بين حسابين:
في Durable Functions C # قيد المعالجة، يتم طرح الاستثناءات غير المعالجة ك FunctionFailedException.
عادة ما تحدد رسالة الاستثناء وظائف النشاط أو التنسيقات الفرعية التي تسببت في الفشل. للوصول إلى معلومات أكثر تفصيلا عن الخطأ، افحص الموقع InnerException .
[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
});
}
}
إشعار
أمثلة C# السابقة هي لـ Durable Functions 2.x. بالنسبة إلى Durable Functions 1.x، يتعين عليك استخدام DurableOrchestrationContext بدلاً من IDurableOrchestrationContext. لمزيدٍ من المعلومات حول الاختلافات بين الإصدارات، راجع مقالة إصدارات Durable Functions.
في Durable Functions C# Isolated، تظهر الاستثناءات غير المعالجة ك TaskFailedException.
عادة ما تحدد رسالة الاستثناء وظائف النشاط أو التنسيقات الفرعية التي تسببت في الفشل. للوصول إلى معلومات خطأ أكثر تفصيلا، افحص الخاصية 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
});
}
}
إشعار
- عادة ما تحدد رسالة الاستثناء وظائف النشاط أو التنسيقات الفرعية التي تسببت في الفشل. للوصول إلى معلومات أكثر تفصيلا عن الخطأ، افحص الموقع
FailureDetails .
- بشكل افتراضي،
FailureDetails يتضمن نوع الخطأورسالة الخطأوتتبع المكدس وأي استثناءات داخلية متداخلة (يتم تمثيل كل منها ككائن متكرر FailureDetails ). إذا كنت ترغب في تضمين خصائص استثناء إضافية في إخراج الفشل، فراجع تضمين خصائص الاستثناء المخصصة ل 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)
بشكل افتراضي، لا ترفع أوامر cmdlets في PowerShell استثناءات يمكن اكتشافها باستخدام كتل try/catch. لديك خياران لتغيير هذا السلوك:
- استخدم العلامة
-ErrorAction Stop عند استدعاء أوامر cmdlets، مثل Invoke-DurableActivity.
- قم بتعيين
$ErrorActionPreferenceمتغير التفضيل إلى "Stop" في دالة المنسق قبل استدعاء أوامر cmdlets.
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 }
}
لمزيد من المعلومات حول معالجة الأخطاء في PowerShell، راجع وثائق Try-Catch-Finally PowerShell.
@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();
}
}
إذا فشل استدعاء دالة CreditAccount الأول، فإن دالة المنسق تعوض عن طريق إعادة رصيد الأموال إلى الحساب المصدر.
أخطاء في وظائف الكيان
يختلف سلوك معالجة الاستثناءات لوظائف الكيان استنادا إلى نموذج استضافة Durable Functions:
في Durable Functions باستخدام C# قيد المعالجة، يتم إرجاع أنواع الاستثناءات الأصلية التي طرحتها وظائف الكيان مباشرة إلى المنسق.
[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;
}
}
في Durable Functions C# Isolated ، تظهر الاستثناءات للمنسق ك.EntityOperationFailedException للوصول إلى تفاصيل الاستثناء الأصلية، افحص موقعه FailureDetails .
[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
وظائف الكيان غير مدعومة حاليا في PowerShell.
وظائف الكيان غير مدعومة حاليا في Java.
إعادة المحاولة التلقائية عند الفشل
عند استدعاء دال النشاط أو دوال التزامن الفرعي، يمكنك تحديد سياسة إعادة المحاولة التلقائية. يحاول المثال التالي استدعاء دالة حتى ثلاث مرات وينتظر 5 ثوانٍ بين كل إعادة محاولة:
[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);
// ...
}
إشعار
أمثلة C# السابقة هي لـ Durable Functions 2.x. بالنسبة إلى Durable Functions 1.x، يتعين عليك استخدام DurableOrchestrationContext بدلاً من IDurableOrchestrationContext. لمزيدٍ من المعلومات حول الاختلافات بين الإصدارات، راجع مقالة إصدارات 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();
// ...
}
استدعاء دالة النشاط في المثال السابق يأخذ معلمة لتكوين نهج إعادة المحاولة التلقائي. هناك عدة خيارات لتخصيص سياسة إعادة المحاولة التلقائية:
-
الحد الأقصى للمحاولات: الحد الأقصى لعدد المحاولات. إذا تم تعيينه إلى 1، لن تكون هناك إعادة محاولة.
-
الفاصل الزمني الأول لإعادة المحاولة: مقدار الوقت المطلوب الانتظار قبل محاولة إعادة المحاولة الأولى.
-
معامل التراجع: المعامل المستخدم لتحديد معدل زيادة التراجع. تعود الإعدادات الافتراضية على 1.
-
الحد الأقصى للفاصل الزمني لإعادة المحاولة:الحد الأقصى لمقدار الوقت الذي يجب الانتظار فيه بين محاولات إعادة المحاولة.
-
مهلة إعادة المحاولة:الحد الأقصى لمقدار الوقت الذي تقضيه في إجراء عمليات إعادة المحاولة. السلوك الافتراضي هو إعادة المحاولة إلى أجل غير مسمى.
معالجات إعادة المحاولة المخصصة
عند استخدام .NET أو Java، لديك أيضا خيار تنفيذ معالجات إعادة المحاولة في التعليمات البرمجية. يكون هذا مفيدا عندما لا تكون سياسات إعادة المحاولة التعريفية معبرة بما فيه الكفاية. بالنسبة للغات التي لا تدعم معالجات إعادة المحاولة المخصصة، لا يزال لديك خيار تنفيذ نهج إعادة المحاولة باستخدام التكرارات الحلقية ومعالجة الاستثناءات والمؤقتات لإدخال التأخيرات بين عمليات إعادة المحاولة.
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 حالياً معالجات إعادة المحاولة المخصصة. ومع ذلك، لا يزال لديك خيار تنفيذ منطق إعادة المحاولة مباشرة في دالة المنسق باستخدام التكرارات الحلقية ومعالجة الاستثناءات والمؤقتات لإدخال التأخيرات بين عمليات إعادة المحاولة.
لا يدعم JavaScript حالياً معالجات إعادة المحاولة المخصصة. ومع ذلك، لا يزال لديك خيار تنفيذ منطق إعادة المحاولة مباشرة في دالة المنسق باستخدام التكرارات الحلقية ومعالجة الاستثناءات والمؤقتات لإدخال التأخيرات بين عمليات إعادة المحاولة.
لا يدعم Python حالياً معالجات إعادة المحاولة المخصصة. ومع ذلك، لا يزال لديك خيار تنفيذ منطق إعادة المحاولة مباشرة في دالة المنسق باستخدام التكرارات الحلقية ومعالجة الاستثناءات والمؤقتات لإدخال التأخيرات بين عمليات إعادة المحاولة.
لا يدعم PowerShell حالياً معالجات إعادة المحاولة المخصصة. ومع ذلك، لا يزال لديك خيار تنفيذ منطق إعادة المحاولة مباشرة في دالة المنسق باستخدام التكرارات الحلقية ومعالجة الاستثناءات والمؤقتات لإدخال التأخيرات بين عمليات إعادة المحاولة.
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...
}
مهلات الدوال
قد ترغب في التخلي عن استدعاء دالة داخل وظيفة منسق إذا استغرق إكمالها وقتًا طويلاً. الطريقة الصحيحة للقيام بذلك اليوم هي من خلال إنشاء مؤقت دائم مع محدد مهمة "أي"، كما في المثال التالي:
[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;
}
}
}
إشعار
أمثلة C# السابقة هي لـ Durable Functions 2.x. بالنسبة إلى Durable Functions 1.x، يتعين عليك استخدام DurableOrchestrationContext بدلاً من IDurableOrchestrationContext. لمزيدٍ من المعلومات حول الاختلافات بين الإصدارات، راجع مقالة إصدارات 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;
}
}
إشعار
لا تنهي هذه الآلية فعليا تنفيذ وظيفة النشاط قيد التقدم. بدلاً من ذلك، فإنه يسمح ببساطة دالة المنظم لتجاهل النتيجة والمضي قدمًا. لمزيد من المعلومات، راجع وثائق Timers.
استثناء غير معالج
إذا فشلت دالة المنسق باستثناء غير معالج، يتم تسجيل تفاصيل الاستثناء واكتمال المثيل بحالة Failed.
تضمين خصائص الاستثناء المخصص ل FailureDetails (.NET معزول)
عند تشغيل مهام سير عمل Durable Task في نموذج .NET Isolated ، يتم تسلسل حالات فشل المهام تلقائيا إلى كائن FailureDetails. بشكل افتراضي، يتضمن هذا الكائن حقول قياسية مثل:
- ErrorType — اسم نوع الاستثناء
- الرسالة - رسالة الاستثناء
- StackTrace — تتبع المكدس المتسلسل
- InnerFailure - كائن FailureDetails متداخل للاستثناءات الداخلية المتكررة
بدءا من Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0، يمكنك توسيع هذا السلوك عن طريق تنفيذ IExceptionPropertiesProvider (محدد في حزمة Microsoft.DurableTask.Worker بدءا من الإصدار 1.16.1). يحدد هذا الموفر أنواع الاستثناءات وخصائصها التي يجب تضمينها في قاموس FailureDetails.Properties.
إشعار
- تتوفر هذه الميزة في .NET Isolated فقط. ستتم إضافة دعم Java في إصدار مستقبلي.
- تأكد من أنك تستخدم Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0 أو أحدث.
- تأكد من أنك تستخدم Microsoft.DurableTask.Worker v1.16.1 أو أحدث.
تنفيذ موفر خصائص الاستثناء
قم بتنفيذ IExceptionPropertiesProvider مخصص لاستخراج الخصائص المحددة وإرجاعها للاستثناءات التي تهتم بها. سيتم تسلسل القاموس الذي تم إرجاعه في حقل الخصائص الخاص ب FailureDetails عند طرح نوع استثناء مطابق.
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
};
}
}
تسجيل المزود
قم بتسجيل IExceptionPropertiesProvider المخصص في مضيف عامل .NET المعزول، عادة في 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();
بمجرد التسجيل، سيتضمن أي استثناء يطابق أحد الأنواع التي تمت معالجتها تلقائيا الخصائص التي تم تكوينها في تفاصيل الفشل الخاصة به.
فشل العينةإخراج التفاصيل
عند حدوث استثناء يطابق تكوين الموفر الخاص بك، يتلقى التنسيق بنية FailureDetails متسلسلة مثل هذا:
{
"errorType": "TaskFailedException",
"message": "Activity failed with an exception.",
"stackTrace": "...",
"innerFailure": {
"errorType": "ArgumentOutOfRangeException",
"message": "Specified argument was out of range.",
"properties": {
"ParamName": "count",
"ActualValue": 42
}
}
}
الخطوات التالية