大部分具有 Exception 機制的程式語言都有提供全域的 Exception 處理,如果你已經有用 log 去記錄錯誤的習慣的話,與其在程式裡佈滿了 try catch,不如在全域處裡中去記錄沒有被 catch 的 Exception,這樣程式就可以更乾淨了,而且程式如果發生異常的關閉時也會進入全域處裡,可以確保 Exception 妥善地被記錄。
Console, WinForm 程式
using System; using System.Security.Permissions; using System.Threading; using System.Windows.Forms; using NLog; namespace AppThreadException { static class Program { private static readonly ILogger _log = LogManager.GetCurrentClassLogger(); [STAThread] [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)] public static void Main(string[] args) { /* ThreadException 用來攔截 UI 錯誤 */ Application.ThreadException += threadExceptionHandler; /* UnhandledException 只能攔截錯誤,不能阻止程式關閉 */ Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); AppDomain.CurrentDomain.UnhandledException += unhandledExceptionHandler; Application.Run(new MyForm()); } /// <summary>攔截 UI 錯誤</summary> private static void threadExceptionHandler(object sender, ThreadExceptionEventArgs e) { _log.Fatal(e.Exception, "操作錯誤"); MessageBox.Show(e.Exception.Message, "操作錯誤", MessageBoxButtons.OK, MessageBoxIcon.Stop); } /// <summary>攔截不可挽回的錯誤,不能阻止程式關閉</summary> private static void unhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) { Exception ex = (Exception)e.ExceptionObject; _log.Fatal(ex, "執行錯誤"); MessageBox.Show(ex.Message, "執行錯誤", MessageBoxButtons.OK, MessageBoxIcon.Stop); } } }
Web 程式
在 Global.asax.cs 中可以設定 Application_Error 就可以攔截未處裡的 Exception,除了用這個方法記錄錯誤,還可以用現有的套件(elmah)幫我們完成。
using System; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using NLog; namespace MvcReport { public class MvcApplication : System.Web.HttpApplication { private static readonly ILogger _log = LogManager.GetCurrentClassLogger(); //... protected void Application_Error(object sender, EventArgs e) { Exception ex = Server.GetLastError(); _log.Fatal(ex, ex.Message); } //... } }
Net core 程式
Net core 的程式都是相同的方式,Web Request 的要另外配置
using System; using System.IO; using System.Threading; using System.Threading.Tasks; using Autofac.Extensions.DependencyInjection; using EverTop.Api; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using NLog; namespace MyWebApp { public class Program { private static readonly ILogger _log = LogManager.GetCurrentClassLogger(); public static void Main(string[] args) { /* UnhandledException 只能攔截錯誤,不能阻止程式關閉 */ AppDomain.CurrentDomain.UnhandledException += unhandledExceptionHandler; /* 用來攔截 Task 錯誤 */ TaskScheduler.UnobservedTaskException += unobservedTaskException; var host = Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder .UseStartup<Startup>() ) .Build(); host.Run(); /* 啟動網站 */ } private static void unhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) { Exception ex = (Exception)e.ExceptionObject; _log.Fatal(ex, "執行錯誤"); } private static void unobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { _log.Fatal(e.Exception, "執行錯誤"); e.SetObserved(); } } }
Net core 3.1 Web 程式
在 Startup.cs 中配置 middleware 進行 Exception 攔截
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (_env.IsProduction()) { app.UseExceptionHandler("/Error"); } else { app.UseDeveloperExceptionPage(); } app.UseStatusCodePagesWithReExecute("/Error"); /* 利用 middleware 進行 Exception 攔截 */ /* 這裡的順序很重要,不然會被前面 ExceptionHandler 處理掉就拿不到 Exception */ app.Use(async (context, next) => { try { await next(); } catch (Exception ex) { _log.Fatal(ex, "執行錯誤"); throw; /* 把 Exception 再丟出去給別人處理 */ } }); }
週期性 Thread 的處裡方式
將主要邏輯寫在另外 method 裡,這樣可以專注在 Exception 上。
private bool _runFlag = false; public void Start() { if (_runFlag) { return; } _runFlag = true; var thread = new Thread(() => { while (_runFlag) { try { cycleHandler(); } catch (Exception ex) { _log.Fatal(ex, "執行錯誤"); } Thread.Sleep(1000); } }); thread.Start(); } public void Stop() { _runFlag = false; } private void cycleHandler() { // 主要的邏輯程式寫在這裡 }
PHP 錯誤處理
Ref: set_error_handler, set_exception_handler
<?php function error_handler($errno, $errstr, $errfile, $errline) { if(error_reporting() === 0){ return; } throw new ErrorException($errstr, 0, $errno, $errfile, $errline); } set_error_handler('error_handler', E_ALL^E_NOTICE); function exception_handler($exception) { // 在這記錄 log throw $exception; } set_exception_handler('exception_handler'); throw new Exception('Uncaught Exception'); //$a = 1 / 0;
沒有留言:
張貼留言
你好!歡迎你在我的 Blog 上留下你寶貴的意見。