diff --git a/Sanhe.Abp.Framework.sln b/Sanhe.Abp.Framework.sln index 829f8ba..830e651 100644 --- a/Sanhe.Abp.Framework.sln +++ b/Sanhe.Abp.Framework.sln @@ -21,6 +21,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "book-store", "book-store", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BookStore", "services\book-store\BookStore.csproj", "{64178F61-A488-4182-A409-C32AE51E33A1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.ExceptionHandling", "modules\common\Sanhe.Abp.ExceptionHandling\Sanhe.Abp.ExceptionHandling.csproj", "{FBEB7703-CF8A-4E5B-B1C7-ED9DC1ABC7BD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.ExceptionHandling.Emailing", "modules\common\Sanhe.Abp.ExceptionHandling.Emailing\Sanhe.Abp.ExceptionHandling.Emailing.csproj", "{0692C2EA-7119-4065-8504-D7FDA70135A9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +43,14 @@ Global {64178F61-A488-4182-A409-C32AE51E33A1}.Debug|Any CPU.Build.0 = Debug|Any CPU {64178F61-A488-4182-A409-C32AE51E33A1}.Release|Any CPU.ActiveCfg = Release|Any CPU {64178F61-A488-4182-A409-C32AE51E33A1}.Release|Any CPU.Build.0 = Release|Any CPU + {FBEB7703-CF8A-4E5B-B1C7-ED9DC1ABC7BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBEB7703-CF8A-4E5B-B1C7-ED9DC1ABC7BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBEB7703-CF8A-4E5B-B1C7-ED9DC1ABC7BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBEB7703-CF8A-4E5B-B1C7-ED9DC1ABC7BD}.Release|Any CPU.Build.0 = Release|Any CPU + {0692C2EA-7119-4065-8504-D7FDA70135A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0692C2EA-7119-4065-8504-D7FDA70135A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0692C2EA-7119-4065-8504-D7FDA70135A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0692C2EA-7119-4065-8504-D7FDA70135A9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -51,6 +63,8 @@ Global {FDC1A6FC-DDAC-4119-9AF3-005F26030B94} = {2A768109-31B7-4C52-928C-3023DAB9F254} {928FDD8C-1EE8-455E-952F-11039B52FE03} = {BBE3B270-DF4F-47F5-9705-89FCFDE2779F} {64178F61-A488-4182-A409-C32AE51E33A1} = {928FDD8C-1EE8-455E-952F-11039B52FE03} + {FBEB7703-CF8A-4E5B-B1C7-ED9DC1ABC7BD} = {2A768109-31B7-4C52-928C-3023DAB9F254} + {0692C2EA-7119-4065-8504-D7FDA70135A9} = {2A768109-31B7-4C52-928C-3023DAB9F254} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AB69BFDE-9DDB-4D16-8CB8-72472C0319CD} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/FodyWeavers.xml b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/README.md b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/README.md new file mode 100644 index 0000000..9aaa515 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/README.md @@ -0,0 +1,37 @@ +# Sanhe.Abp.ExceptionHandling.Emailing + +基于abp框架底层的**IExceptionSubscriber**的邮件通知类型 + +## 配置使用 + +使用前需要配置**AbpExceptionHandlingOptions**定义需要发送通知的异常 +然后配置**AbpEmailExceptionHandlingOptions**定义具体异常类型通知方式 + +```csharp + + [DependsOn( + typeof(AbpEmailingExceptionHandlingModule) + )] + public class YouProjectModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + // 自定义需要处理的异常 + Configure(options => + { + // 加入需要处理的异常类型 + options.Handlers.Add(); + }); + // 自定义需要发送邮件通知的异常类型 + Configure(options => + { + // 是否发送堆栈信息 + options.SendStackTrace = true; + // 未指定异常接收者的默认接收邮件 + options.DefaultReceiveEmail = "colin.in@foxmail.com"; + // 指定某种异常发送到哪个邮件 + options.HandReceivedException("colin.in@foxmail.com"); + }); + } + } +``` \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe.Abp.ExceptionHandling.Emailing.csproj b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe.Abp.ExceptionHandling.Emailing.csproj new file mode 100644 index 0000000..ccca40f --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe.Abp.ExceptionHandling.Emailing.csproj @@ -0,0 +1,33 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/AbpEmailExceptionHandlingOptions.cs b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/AbpEmailExceptionHandlingOptions.cs new file mode 100644 index 0000000..95499fd --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/AbpEmailExceptionHandlingOptions.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; + +namespace Sanhe.Abp.ExceptionHandling.Emailing; + +public class AbpEmailExceptionHandlingOptions +{ + /// + /// 发送堆栈信息 + /// + public bool SendStackTrace { get; set; } = false; + /// + /// 默认邮件标题 + /// + public string DefaultTitle { get; set; } + /// + /// 默认邮件内容头 + /// + public string DefaultContentHeader { get; set; } + /// + /// 默认邮件内容底 + /// + public string DefaultContentFooter { get; set; } + /// + /// 默认异常收件人 + /// + public string DefaultReceiveEmail { get; set; } + /// + /// 异常类型指定收件人处理映射列表 + /// + public IDictionary Handlers { get; set; } + + public AbpEmailExceptionHandlingOptions() + { + Handlers = new Dictionary(); + } + + /// + /// 把需要接受异常通知的用户加进处理列表 + /// + /// 处理的异常类型 + /// 接收邮件的用户类别,群发用,符号分隔 + public void HandReceivedException(string receivedEmails) where TException : Exception + { + HandReceivedException(typeof(TException), receivedEmails); + } + + /// + /// 把需要接受异常通知的用户加进处理列表 + /// + /// 处理的异常类型 + /// 接收邮件的用户类别,群发用,符号分隔 + public void HandReceivedException(Type exceptionType, string receivedEmails) + { + if (Handlers.ContainsKey(exceptionType)) + { + Handlers[exceptionType] += receivedEmails; + } + else + { + Handlers.Add(exceptionType, receivedEmails); + } + } + + public string GetReceivedEmailOrDefault(Type exceptionType) + { + if (Handlers.TryGetValue(exceptionType, out string receivedUsers)) + { + return receivedUsers; + } + return DefaultReceiveEmail; + } +} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/AbpEmailingExceptionHandlingModule.cs b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/AbpEmailingExceptionHandlingModule.cs new file mode 100644 index 0000000..9077c5a --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/AbpEmailingExceptionHandlingModule.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.DependencyInjection; +using Sanhe.Abp.ExceptionHandling.Emailing.Localization; +using Volo.Abp.Emailing; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace Sanhe.Abp.ExceptionHandling.Emailing; + +[DependsOn( + typeof(AbpExceptionHandlingModule), + typeof(AbpEmailingModule))] +public class AbpEmailingExceptionHandlingModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + Configure( + configuration.GetSection("ExceptionHandling:Emailing")); + + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Add("en") + .AddVirtualJson("/Sanhe/Abp/ExceptionHandling/Emailing/Localization/Resources"); + }); + } +} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/AbpEmailingExceptionSubscriber.cs b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/AbpEmailingExceptionSubscriber.cs new file mode 100644 index 0000000..2014358 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/AbpEmailingExceptionSubscriber.cs @@ -0,0 +1,68 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; +using Sanhe.Abp.ExceptionHandling.Emailing.Localization; +using Sanhe.Abp.ExceptionHandling.Emailing.Templates; +using System; +using System.Threading.Tasks; +using Volo.Abp.Emailing; +using Volo.Abp.TextTemplating; + +namespace Sanhe.Abp.ExceptionHandling.Emailing; + +public class AbpEmailingExceptionSubscriber : AbpExceptionSubscriberBase +{ + protected IEmailSender EmailSender { get; } + protected IStringLocalizer StringLocalizer { get; } + protected ITemplateRenderer TemplateRenderer { get; } + protected AbpEmailExceptionHandlingOptions EmailOptions { get; } + + public AbpEmailingExceptionSubscriber( + IEmailSender emailSender, + ITemplateRenderer templateRenderer, + IServiceScopeFactory serviceScopeFactory, + IOptions options, + IOptions emailOptions, + IStringLocalizer stringLocalizer) + : base(serviceScopeFactory, options) + { + EmailSender = emailSender; + EmailOptions = emailOptions.Value; + StringLocalizer = stringLocalizer; + TemplateRenderer = templateRenderer; + } + + protected override async Task SendErrorNotifierAsync(ExceptionSendNotifierContext context) + { + // 需不需要用 SettingProvider 来获取? + var receivedUsers = EmailOptions.GetReceivedEmailOrDefault(context.Exception.GetType()); + + if (!receivedUsers.IsNullOrWhiteSpace()) + { + var emailTitle = EmailOptions.DefaultTitle ?? L("SendEmailTitle"); + var templateContent = await TemplateRenderer + .RenderAsync(ExceptionHandlingTemplates.SendEmail, + new + { + title = emailTitle, + header = EmailOptions.DefaultContentHeader ?? L("SendEmailHeader"), + type = context.Exception.GetType().FullName, + message = context.Exception.Message, + loglevel = context.LogLevel.ToString(), + triggertime = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), + sendstacktrace = EmailOptions.SendStackTrace, + stacktrace = context.Exception.ToString(), + footer = EmailOptions.DefaultContentFooter ?? $"Copyright to Sanhe © {DateTime.Now.Year}" + }); + + await EmailSender.SendAsync(receivedUsers, + emailTitle, + templateContent); + } + } + + protected string L(string name, params object[] args) + { + return StringLocalizer[name, args].Value; + } +} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Localization/ExceptionHandlingResource.cs b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Localization/ExceptionHandlingResource.cs new file mode 100644 index 0000000..c6cd6b2 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Localization/ExceptionHandlingResource.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Localization; + +namespace Sanhe.Abp.ExceptionHandling.Emailing.Localization; + +[LocalizationResourceName("AbpExceptionHandlingEmailing")] +public class ExceptionHandlingResource +{ +} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Localization/Resources/en.json b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Localization/Resources/en.json new file mode 100644 index 0000000..819e282 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Localization/Resources/en.json @@ -0,0 +1,8 @@ +{ + "culture": "en", + "texts": { + "TextTemplate:ExceptionHandlingTemplates.SendEmail": "Apply the exception message sending template", + "SendEmailTitle": "Application exception push", + "SendEmailHeader": "Application exception" + } +} \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Localization/Resources/zh-Hans.json b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Localization/Resources/zh-Hans.json new file mode 100644 index 0000000..3e91418 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Localization/Resources/zh-Hans.json @@ -0,0 +1,8 @@ +{ + "culture": "zh-Hans", + "texts": { + "TextTemplate:ExceptionHandlingTemplates.SendEmail": "应用异常邮件发送模板", + "SendEmailTitle": "应用程序异常推送", + "SendEmailHeader": "应用程序异常" + } +} \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/ExceptionHandlingTemplateDefinitionProvider.cs b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/ExceptionHandlingTemplateDefinitionProvider.cs new file mode 100644 index 0000000..7524cc2 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/ExceptionHandlingTemplateDefinitionProvider.cs @@ -0,0 +1,19 @@ +using Sanhe.Abp.ExceptionHandling.Emailing.Localization; +using Volo.Abp.Localization; +using Volo.Abp.TextTemplating; + +namespace Sanhe.Abp.ExceptionHandling.Emailing.Templates; + +public class ExceptionHandlingTemplateDefinitionProvider : TemplateDefinitionProvider +{ + public override void Define(ITemplateDefinitionContext context) + { + context.Add( + new TemplateDefinition( + ExceptionHandlingTemplates.SendEmail, + displayName: LocalizableString.Create("TextTemplate:ExceptionHandlingTemplates.SendEmail"), + defaultCultureName: "en" + ).WithVirtualFilePath("/Sanhe/Abp/ExceptionHandling/Emailing/Templates/SendEmail", false) + ); + } +} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/ExceptionHandlingTemplates.cs b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/ExceptionHandlingTemplates.cs new file mode 100644 index 0000000..933ed42 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/ExceptionHandlingTemplates.cs @@ -0,0 +1,6 @@ +namespace Sanhe.Abp.ExceptionHandling.Emailing.Templates; + +public class ExceptionHandlingTemplates +{ + public const string SendEmail = "Abp.ExceptionHandling.SendEmail"; +} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/SendEmail/en.tpl b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/SendEmail/en.tpl new file mode 100644 index 0000000..2a08b0d --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/SendEmail/en.tpl @@ -0,0 +1,48 @@ + + + + + {{ model.title }} + + + + + + + + + + {{ if model.sendstacktrace }} + + + + + + + {{ end }} + + + +
+
{{ model.header }} +
+
+
    +
  • Type  : {{ model.type }}
  • +
  • Message  : {{ model.message }}
  • +
  • Alarm level : {{ model.loglevel }}
  • +
  • TriggerTime : {{ model.triggertime }}
  • +
+
+ Stack trace +
+
+
{{ model.stacktrace }}
+
+
+
+ {{ model.footer }} +
+
+ + \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/SendEmail/zh-Hans.tpl b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/SendEmail/zh-Hans.tpl new file mode 100644 index 0000000..b53f74b --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/Sanhe/Abp/ExceptionHandling/Emailing/Templates/SendEmail/zh-Hans.tpl @@ -0,0 +1,48 @@ + + + + + {{ model.title }} + + + + + + + + + + {{ if model.sendstacktrace }} + + + + + + + {{ end }} + + + +
+
{{ model.header }} +
+
+
    +
  • 异常类型 : {{ model.type }}
  • +
  • 异常信息 : {{ model.message }}
  • +
  • 告警级别 : {{ model.loglevel }}
  • +
  • 触发时间 : {{ model.triggertime }}
  • +
+
+ 异常堆栈 +
+
+
{{ model.stacktrace }}
+
+
+
+ {{ model.footer }} +
+
+ + \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.ExceptionHandling/FodyWeavers.xml b/modules/common/Sanhe.Abp.ExceptionHandling/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.ExceptionHandling/README.md b/modules/common/Sanhe.Abp.ExceptionHandling/README.md new file mode 100644 index 0000000..8698cc6 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling/README.md @@ -0,0 +1,26 @@ +# Sanhe.Abp.ExceptionHandling + +基于abp框架底层的**IExceptionSubscriber**实现二次扩展,用于自定义异常通知方式 + +## 配置使用 + +使用前只需配置**AbpExceptionHandlingOptions**定义需要发送通知的异常即可。 + +```csharp + + [DependsOn( + typeof(AbpExceptionHandlingModule) + )] + public class YouProjectModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + // 自定义需要处理的异常 + Configure(options => + { + // 加入需要处理的异常类型 + options.Handlers.Add(); + }); + } + } +``` \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe.Abp.ExceptionHandling.csproj b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe.Abp.ExceptionHandling.csproj new file mode 100644 index 0000000..271d9ef --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe.Abp.ExceptionHandling.csproj @@ -0,0 +1,14 @@ + + + + + + + netstandard2.0 + + + + + + + diff --git a/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/AbpExceptionHandlingModule.cs b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/AbpExceptionHandlingModule.cs new file mode 100644 index 0000000..ab05d67 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/AbpExceptionHandlingModule.cs @@ -0,0 +1,17 @@ +using Sanhe.Abp.ExceptionHandling.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; + +namespace Sanhe.Abp.ExceptionHandling; + +[DependsOn(typeof(AbpLocalizationModule))] +public class AbpExceptionHandlingModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Resources.Add("en"); + }); + } +} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/AbpExceptionHandlingOptions.cs b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/AbpExceptionHandlingOptions.cs new file mode 100644 index 0000000..58287b0 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/AbpExceptionHandlingOptions.cs @@ -0,0 +1,24 @@ +using System; +using System.Linq; +using Volo.Abp.Collections; + +namespace Sanhe.Abp.ExceptionHandling; + +public class AbpExceptionHandlingOptions +{ + public ITypeList Handlers { get; } + + public AbpExceptionHandlingOptions() + { + Handlers = new TypeList(); + } + + public bool HasNotifierError(Exception ex) + { + if (typeof(IHasNotifierErrorMessage).IsAssignableFrom(ex.GetType())) + { + return true; + } + return Handlers.Any(x => x.IsAssignableFrom(ex.GetType())); + } +} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/AbpExceptionSubscriberBase.cs b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/AbpExceptionSubscriberBase.cs new file mode 100644 index 0000000..8c835cb --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/AbpExceptionSubscriberBase.cs @@ -0,0 +1,66 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Threading.Tasks; +using Volo.Abp.ExceptionHandling; + +namespace Sanhe.Abp.ExceptionHandling; + +public abstract class AbpExceptionSubscriberBase : ExceptionSubscriber +{ + protected IServiceScopeFactory ServiceScopeFactory { get; } + protected AbpExceptionHandlingOptions Options { get; } + protected IServiceProvider ServiceProvider { get; set; } + protected readonly object ServiceProviderLock = new(); + + protected TService LazyGetRequiredService(ref TService reference) + => LazyGetRequiredService(typeof(TService), ref reference); + + protected TRef LazyGetRequiredService(Type serviceType, ref TRef reference) + { + if (reference == null) + { + lock (ServiceProviderLock) + { + if (reference == null) + { + reference = (TRef)ServiceProvider.GetRequiredService(serviceType); + } + } + } + + return reference; + } + + protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); + private ILoggerFactory _loggerFactory; + + protected ILogger Logger => _lazyLogger.Value; + private Lazy _lazyLogger => new(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); + + + protected AbpExceptionSubscriberBase( + IServiceScopeFactory serviceScopeFactory, + IOptions options) + { + Options = options.Value; + ServiceScopeFactory = serviceScopeFactory; + } + + public override async Task HandleAsync(ExceptionNotificationContext context) + { + if (context.Handled && + Options.HasNotifierError(context.Exception)) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + await SendErrorNotifierAsync( + new ExceptionSendNotifierContext(scope.ServiceProvider, context.Exception, context.LogLevel)); + } + } + } + + protected abstract Task SendErrorNotifierAsync(ExceptionSendNotifierContext context); +} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/ExceptionSendNotifierContext.cs b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/ExceptionSendNotifierContext.cs new file mode 100644 index 0000000..04f1a1f --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/ExceptionSendNotifierContext.cs @@ -0,0 +1,27 @@ +using JetBrains.Annotations; +using Microsoft.Extensions.Logging; +using System; +using Volo.Abp; + +namespace Sanhe.Abp.ExceptionHandling; + +public class ExceptionSendNotifierContext +{ + [NotNull] + public Exception Exception { get; } + + [NotNull] + public IServiceProvider ServiceProvider { get; } + + public LogLevel LogLevel { get; } + + internal ExceptionSendNotifierContext( + [NotNull] IServiceProvider serviceProvider, + [NotNull] Exception exception, + LogLevel? logLevel = null) + { + ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider)); + Exception = Check.NotNull(exception, nameof(exception)); + LogLevel = logLevel ?? exception.GetLogLevel(); + } +} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/IHasNotifierErrorMessage.cs b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/IHasNotifierErrorMessage.cs new file mode 100644 index 0000000..f5aa1cc --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/IHasNotifierErrorMessage.cs @@ -0,0 +1,9 @@ +namespace Sanhe.Abp.ExceptionHandling; + +/// +/// 需要发送异常通知的自定义异常需要实现此接口。 +/// +public interface IHasNotifierErrorMessage +{ + +} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/Localization/ExceptionHandlingResource.cs b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/Localization/ExceptionHandlingResource.cs new file mode 100644 index 0000000..dae4e89 --- /dev/null +++ b/modules/common/Sanhe.Abp.ExceptionHandling/Sanhe/Abp/ExceptionHandling/Localization/ExceptionHandlingResource.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Localization; + +namespace Sanhe.Abp.ExceptionHandling.Localization; + +[LocalizationResourceName("AbpExceptionHandling")] +public class ExceptionHandlingResource +{ +} diff --git a/services/book-store/BookStore.csproj b/services/book-store/BookStore.csproj index 79656cc..9a2ffdd 100644 --- a/services/book-store/BookStore.csproj +++ b/services/book-store/BookStore.csproj @@ -2,8 +2,6 @@ net6.0 - enable - enable @@ -93,6 +91,7 @@ + diff --git a/services/book-store/BookStoreModule.cs b/services/book-store/BookStoreModule.cs index 4d0499e..6f1fb44 100644 --- a/services/book-store/BookStoreModule.cs +++ b/services/book-store/BookStoreModule.cs @@ -1,11 +1,20 @@ using BookStore.Data; using BookStore.Localization; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; using Sanhe.Abp.AspNetCore.Mvc.Wrapper; +using Sanhe.Abp.ExceptionHandling; +using Sanhe.Abp.ExceptionHandling.Emailing; using Sanhe.Abp.Wrapper; using StackExchange.Redis; +using System; +using System.Linq; using Volo.Abp; using Volo.Abp.Account; using Volo.Abp.Account.Web; @@ -21,7 +30,6 @@ using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.AuditLogging.EntityFrameworkCore; using Volo.Abp.Autofac; using Volo.Abp.AutoMapper; -using Volo.Abp.Caching.StackExchangeRedis; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.PostgreSql; using Volo.Abp.FeatureManagement; @@ -96,7 +104,8 @@ namespace BookStore; typeof(AbpSettingManagementEntityFrameworkCoreModule), typeof(AbpSettingManagementHttpApiModule), - typeof(AbpAspNetCoreMvcWrapperModule) + typeof(AbpAspNetCoreMvcWrapperModule), + typeof(AbpEmailingExceptionHandlingModule) )] public class BookStoreModule : AbpModule { @@ -125,7 +134,7 @@ public class BookStoreModule : AbpModule options.IsEnabled = true; options.IgnoreNamespaces.Clear(); }); - + ConfigureExceptionHandling(); ConfigureBundles(); ConfigureMultiTenancy(); ConfigureUrls(configuration); @@ -140,6 +149,32 @@ public class BookStoreModule : AbpModule ConfigureEfCore(context); } + private void ConfigureExceptionHandling() + { + // 自定义需要处理的异常 + Configure(options => + { + // 加入需要处理的异常类型 + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + }); + + // 自定义需要发送邮件通知的异常类型 + Configure(options => + { + // 是否发送堆栈信息 + options.SendStackTrace = true; + // 未指定异常接收者的默认接收邮件 + // 请指定自己的邮件地址 + // options.DefaultReceiveEmail = ""; + }); + } private void ConfigureBundles() { Configure(options => diff --git a/services/book-store/Controllers/WrapController.cs b/services/book-store/Controllers/WrapController.cs index a530c2a..8511b96 100644 --- a/services/book-store/Controllers/WrapController.cs +++ b/services/book-store/Controllers/WrapController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Authorization; @@ -41,6 +42,6 @@ public class WrapController : AbpControllerBase public class Person { - public string? Name { get; set; } + public string Name { get; set; } public int Age { get; set; } } \ No newline at end of file diff --git a/services/book-store/Data/BookStoreDbMigrationService.cs b/services/book-store/Data/BookStoreDbMigrationService.cs index 12cbef9..8d516cc 100644 --- a/services/book-store/Data/BookStoreDbMigrationService.cs +++ b/services/book-store/Data/BookStoreDbMigrationService.cs @@ -1,6 +1,12 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Identity; diff --git a/services/book-store/Data/BookStoreEFCoreDbSchemaMigrator.cs b/services/book-store/Data/BookStoreEFCoreDbSchemaMigrator.cs index c68b0b2..06d4fab 100644 --- a/services/book-store/Data/BookStoreEFCoreDbSchemaMigrator.cs +++ b/services/book-store/Data/BookStoreEFCoreDbSchemaMigrator.cs @@ -1,4 +1,7 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Threading.Tasks; using Volo.Abp.DependencyInjection; namespace BookStore.Data; diff --git a/services/book-store/Data/IdentityServerDataSeedContributor.cs b/services/book-store/Data/IdentityServerDataSeedContributor.cs index 5d864e2..efd3d57 100644 --- a/services/book-store/Data/IdentityServerDataSeedContributor.cs +++ b/services/book-store/Data/IdentityServerDataSeedContributor.cs @@ -1,4 +1,8 @@ using IdentityServer4.Models; +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; diff --git a/services/book-store/Program.cs b/services/book-store/Program.cs index 81b4317..b0c1fd6 100644 --- a/services/book-store/Program.cs +++ b/services/book-store/Program.cs @@ -1,7 +1,12 @@ -using System; using BookStore.Data; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Serilog; using Serilog.Events; +using System; +using System.Linq; +using System.Threading.Tasks; namespace BookStore;