Browse Source

feat(tasks): 完善作业异常推送.

pull/461/head
cKey 4 years ago
parent
commit
6017f03a5e
  1. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContextExtensions.cs
  2. 10
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN.Abp.BackgroundTasks.ExceptionHandling.csproj
  3. 16
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/AbpBackgroundTasksExceptionHandlingModule.cs
  4. 109
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/JobExceptionNotifier.cs
  5. 15
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/Localization/Resources/en.json
  6. 15
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/Localization/Resources/zh-Hans.json
  7. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs
  8. 30
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob.cs
  9. 7
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs
  10. 16
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs
  11. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs
  12. 8
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/BackgroundTasksResource.cs
  13. 5
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/Resources/en.json
  14. 5
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/Resources/zh-Hans.json
  15. 9
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs

2
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContextExtensions.cs

@ -71,7 +71,7 @@ public static class JobRunnableContextExtensions
{
return value;
}
throw new ArgumentException($"Job required data {key} not specified.");
throw new ArgumentException($"Job required data [{key}] not specified.");
}
public static bool TryGetJobData(this JobRunnableContext context, string key, out object value)

10
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN.Abp.BackgroundTasks.ExceptionHandling.csproj

@ -8,6 +8,16 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<None Remove="LINGYUN\Abp\BackgroundTasks\ExceptionHandling\Localization\Resources\*.json" />
<None Remove="LINGYUN\Abp\BackgroundTasks\ExceptionHandling\Templates\*.tpl" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LINGYUN\Abp\BackgroundTasks\ExceptionHandling\Localization\Resources\*.json" />
<EmbeddedResource Include="LINGYUN\Abp\BackgroundTasks\ExceptionHandling\Templates\*.tpl" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Timing" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>

16
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/AbpBackgroundTasksExceptionHandlingModule.cs

@ -1,5 +1,8 @@
using LINGYUN.Abp.BackgroundTasks.Jobs;
using LINGYUN.Abp.BackgroundTasks.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling;
@ -7,5 +10,18 @@ namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling;
[DependsOn(typeof(AbpBackgroundTasksJobsModule))]
public class AbpBackgroundTasksExceptionHandlingModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpBackgroundTasksExceptionHandlingModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<BackgroundTasksResource>()
.AddVirtualJson("/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/Localization/Resources");
});
}
}

109
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/JobExceptionNotifier.cs

@ -1,10 +1,16 @@
using JetBrains.Annotations;
using LINGYUN.Abp.BackgroundTasks.Jobs;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TextTemplating;
using Volo.Abp.Timing;
namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling;
@ -13,64 +19,89 @@ namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling;
public class JobExceptionNotifier : IJobExceptionNotifier, ITransientDependency
{
public const string Prefix = "exception.";
public const string JobGroup = "ExceptionNotifier";
public ILogger<JobExceptionNotifier> Logger { protected get; set; }
protected IClock Clock { get; }
protected IJobStore JobStore { get; }
protected IJobScheduler JobScheduler { get; }
protected IEmailSender EmailSender { get; }
protected ITemplateRenderer TemplateRenderer { get; }
public JobExceptionNotifier(
IClock clock,
IJobStore jobStore,
IJobScheduler jobScheduler)
IEmailSender emailSender,
ITemplateRenderer templateRenderer)
{
Clock = clock;
JobStore = jobStore;
JobScheduler = jobScheduler;
EmailSender = emailSender;
TemplateRenderer = templateRenderer;
Logger = NullLogger<JobExceptionNotifier>.Instance;
}
public virtual async Task NotifyAsync([NotNull] JobExceptionNotificationContext context)
{
// 异常所属分组不处理, 防止死循环
if (string.Equals(context.JobInfo.Group, JobGroup))
{
Logger.LogWarning($"There is a problem executing the job, reason: {context.Exception.Message}");
return;
}
var notifyKey = Prefix + SendEmailJob.PropertyTo;
if (context.JobInfo.Args.TryGetValue(notifyKey, out var exceptionTo))
if (context.JobInfo.Args.TryGetValue(notifyKey, out var exceptionTo) &&
exceptionTo is string to)
{
var template = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyTemplate) ?? "";
var subject = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertySubject) ?? "From job execute exception";
var globalContext = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyContext) ?? "{}";
var from = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyFrom) ?? "";
var culture = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyCulture) ?? CultureInfo.CurrentCulture.Name;
var template = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyTemplate)?.ToString() ?? "";
var content = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyBody)?.ToString() ?? "";
var subject = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertySubject)?.ToString() ?? "From job execute exception";
var from = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyFrom)?.ToString() ?? "";
var errorMessage = context.Exception.GetBaseException().Message;
var jobId = Guid.NewGuid();
var jobArgs = new Dictionary<string, object>
if (template.IsNullOrWhiteSpace())
{
{ SendEmailJob.PropertyTo, exceptionTo.ToString() },
{ SendEmailJob.PropertySubject, subject },
{ SendEmailJob.PropertyBody, context.Exception.GetBaseException().Message },
{ SendEmailJob.PropertyTemplate, template },
{ SendEmailJob.PropertyContext, globalContext },
{ SendEmailJob.PropertyFrom, from },
{ SendEmailJob.PropertyCulture, culture }
};
var jobInfo = new JobInfo
await EmailSender.SendAsync(from, to, subject, content, false);
return;
}
var footer = context.JobInfo.Args.GetOrDefault("footer")?.ToString() ?? $"Copyright to LY Colin © {Clock.Now.Year}";
var model = new
{
Id = jobId,
Name = jobId.ToString(),
Group = "ExceptionHandling",
Priority = JobPriority.Normal,
BeginTime = Clock.Now,
Args = jobArgs,
Description = subject.ToString(),
JobType = JobType.Once,
Interval = 5,
MaxCount = 1,
MaxTryCount = 1,
CreationTime = Clock.Now,
Status = JobStatus.None,
Type = DefaultJobNames.SendEmailJob,
Title = subject,
Group = context.JobInfo.Group,
Name = context.JobInfo.Name,
Id = context.JobInfo.Id,
Type = context.JobInfo.Type,
Triggertime = Clock.Now.ToString("yyyy-MM-dd HH:mm:ss"),
Message = errorMessage,
Tenantname = context.JobInfo.Args.GetOrDefault(nameof(IMultiTenant.TenantId)),
Footer = footer,
};
await JobStore.StoreAsync(jobInfo);
var globalContext = new Dictionary<string, object>();
if (context.JobInfo.Args.TryGetValue(Prefix + SendEmailJob.PropertyContext, out var ctx) &&
ctx is string ctxStr && !ctxStr.IsNullOrWhiteSpace())
{
try
{
globalContext = JsonConvert.DeserializeObject<Dictionary<string, object>>(ctxStr);
}
catch { }
}
var culture = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyCulture)?.ToString() ?? CultureInfo.CurrentCulture.Name;
await JobScheduler.TriggerAsync(jobInfo);
content = await TemplateRenderer.RenderAsync(
templateName: template,
model: model,
cultureName: culture,
globalContext: globalContext);
if (from.IsNullOrWhiteSpace())
{
await EmailSender.SendAsync(to, subject, content, true);
return;
}
await EmailSender.SendAsync(from, to, subject, content, true);
}
}
}

15
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/Localization/Resources/en.json

@ -0,0 +1,15 @@
{
"culture": "en",
"texts": {
"TextTemplate:JobExceptionNotifier": "Background job exception pushes template",
"JobExceptionNotifier": "Background job exception push",
"TenantName": "TenantName",
"JobGroup": "Job Group",
"JobName": "Job Name",
"JobId": "Job Id",
"JobType": "Job Type",
"TriggerTime": "Trigger Time",
"ErrorMessage": "Error Message",
"JobExecuteError": "Background job exception"
}
}

15
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/Localization/Resources/zh-Hans.json

@ -0,0 +1,15 @@
{
"culture": "zh-Hans",
"texts": {
"TextTemplate:JobExceptionNotifier": "后台作业异常推送模板",
"JobExceptionNotifier": "后台作业异常推送",
"TenantName": "租户名称",
"JobGroup": "作业分组",
"JobName": "作业名称",
"JobId": "作业标识",
"JobType": "作业类型",
"TriggerTime": "触发时间",
"ErrorMessage": "错误消息",
"JobExecuteError": "后台作业异常"
}
}

2
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs

@ -9,7 +9,7 @@ public class ConsoleJob : IJobRunnable
public Task ExecuteAsync(JobRunnableContext context)
{
context.TryGetString(PropertyMessage, out var message);
Console.WriteLine($"This message: {message ?? "None"} comes from the job: {GetType()}");
Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}] - This message: {message ?? "None"} comes from the job: {GetType()}");
return Task.CompletedTask;
}
}

30
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob.cs

@ -4,6 +4,7 @@ using Volo.Abp.TextTemplating;
using Volo.Abp.Json;
using System.Collections.Generic;
using System;
using Newtonsoft.Json;
namespace LINGYUN.Abp.BackgroundTasks.Jobs;
@ -28,6 +29,10 @@ public class SendEmailJob : IJobRunnable
/// </summary>
public const string PropertyTemplate = "template";
/// <summary>
/// 可选, 模板消息中的模型参数
/// </summary>
public const string PropertyModel = "model";
/// <summary>
/// 可选, 模板消息中的上下文参数
/// </summary>
public const string PropertyContext = "context";
@ -58,19 +63,40 @@ public class SendEmailJob : IJobRunnable
catch { }
}
object model = null;
if (context.TryGetString(PropertyModel, out var modelString) && !modelString.IsNullOrWhiteSpace())
{
try
{
model = JsonConvert.DeserializeObject(modelString);
}
catch { }
}
var templateRenderer = context.GetRequiredService<ITemplateRenderer>();
var content = await templateRenderer.RenderAsync(
templateName: template,
model: model,
cultureName: culture,
globalContext: globalContext);
await emailSender.QueueAsync(from, to, subject, content, true);
await QueueEmail(emailSender, from, to, subject, content, true);
return;
}
var body = context.GetString(PropertyBody);
await emailSender.QueueAsync(from, to, subject, body, false);
await QueueEmail(emailSender, from, to, subject, body, false);
}
private async Task QueueEmail(IEmailSender emailSender, string from, string to, string subject, string body, bool isBodyHtml = true)
{
if (from.IsNullOrWhiteSpace())
{
await emailSender.SendAsync(to, subject, body, isBodyHtml);
return;
}
await emailSender.SendAsync(from, to, subject, body, isBodyHtml);
}
}

7
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs

@ -154,10 +154,11 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
var jobKey = new JobKey(job.Name, job.Group);
if (!await Scheduler.CheckExists(jobKey))
{
job.JobType = JobType.Once;
await QueueAsync(job);
}
await Scheduler.TriggerJob(jobKey);
else
{
await Scheduler.TriggerJob(jobKey);
}
}
}

16
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs

@ -1,14 +1,18 @@
using LINGYUN.Abp.BackgroundTasks.Internal;
using LINGYUN.Abp.BackgroundTasks.Localization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Auditing;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Guids;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.BackgroundTasks;
[DependsOn(typeof(AbpAuditingModule))]
[DependsOn(typeof(AbpLocalizationModule))]
[DependsOn(typeof(AbpBackgroundTasksAbstractionsModule))]
[DependsOn(typeof(AbpBackgroundJobsAbstractionsModule))]
[DependsOn(typeof(AbpDistributedLockingAbstractionsModule))]
@ -19,5 +23,17 @@ public class AbpBackgroundTasksModule : AbpModule
{
context.Services.AddTransient(typeof(BackgroundJobAdapter<>));
context.Services.AddHostedService<DefaultBackgroundWorker>();
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpBackgroundTasksModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<BackgroundTasksResource>("en")
.AddVirtualJson("/LINGYUN/Abp/BackgroundTasks/Localization/Resources");
});
}
}

2
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs

@ -47,7 +47,7 @@ public class JobExecutedEvent : JobEventBase<JobExecutedEvent>, ITransientDepend
job.Priority = JobPriority.Low;
}
if (job.TryCount > job.MaxTryCount)
if (job.TryCount >= job.MaxTryCount)
{
job.Status = JobStatus.Stopped;
job.IsAbandoned = true;

8
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/BackgroundTasksResource.cs

@ -0,0 +1,8 @@
using Volo.Abp.Localization;
namespace LINGYUN.Abp.BackgroundTasks.Localization;
[LocalizationResourceName("BackgroundTasks")]
public class BackgroundTasksResource
{
}

5
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/Resources/en.json

@ -0,0 +1,5 @@
{
"culture": "en",
"texts": {
}
}

5
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/Resources/zh-Hans.json

@ -0,0 +1,5 @@
{
"culture": "zh-Hans",
"texts": {
}
}

9
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs

@ -92,12 +92,9 @@ public class BackgroundJobManager : DomainService
public virtual async Task TriggerAsync(BackgroundJobInfo jobInfo)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
//if (!await JobScheduler.ExistsAsync(job))
//{
// throw new BusinessException(TaskManagementErrorCodes.JobNotFoundInQueue)
// .WithData("Group", job.Group)
// .WithData("Name", job.Name);
//}
job.JobType = JobType.Once;
// 延迟两秒触发
job.Interval = 2;
await JobScheduler.TriggerAsync(job);
}

Loading…
Cancel
Save