我已经基于此博客
https://blog.kloud.com.au/2016/03/23/aspnet-core-tips-and-tricks-global-exception-handling/实现了自定义异常过滤器.
我现在注意到我无法从StackFrame获取发生异常的文件名或行号;方法GetFileLineNumber的值始终为0.
我试过的一个简单示例:
StackTrace trace = new StackTrace(exception,true); StackFrame[] stackFrames = trace.GetFrames(); if (stackFrames != null) { foreach (StackFrame traceFrame in stackFrames) { sourceFile = traceFrame.GetFileName(); sourceLineNumber = traceFrame.GetFileLineNumber(); } }
要提一下:GetFileName也返回null,但我可以从不同的位置获取命名空间和类名,所以这不是问题.
在某个地方注意到我应该在文件夹上有PDB文件才能工作,我检查过我已经拥有它.即使它起作用,它也应该在生产环境中起作用.
尝试也使用来电者信息(https://msdn.microsoft.com/en-us/library/mt653988.aspx),但有一个不同的问题.异常过滤器必须实现IExceptionFilter接口并使用它的方法.因此,即使它们是可选的,我也无法添加属性.
这是Startup.cs的代码部分:
public void ConfigureServices(IServiceCollection services) { ... services.AddMvc(config => { config.Filters.Add(new ApplicationExceptionFilter()); }); }
这是自定义过滤器
public class ApplicationExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext context) { ApplicationExceptionHandler.handleException(context.HttpContext,context.Exception); } }
我尝试获取来电者信息的原因是:
public void OnException(ExceptionContext context,[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) {
但这不起作用,因为它不再实现接口.
如何在仍然在一个地方处理异常的同时获取行号?
假设我的问题是我没有从堆栈跟踪获取行号并重现问题:
public ActionResult ServerError() { Int32 x = 0; try { return Content((1 / x).ToString()); } catch (Exception ex) { StackTrace trace = new StackTrace(ex,true); StackFrame[] stackFrames = trace.GetFrames(); if (stackFrames != null) { foreach (StackFrame traceFrame in stackFrames) { String sourceFile = traceFrame.GetFileName(); int sourceLineNumber = traceFrame.GetFileLineNumber(); } } } return Content(""); }
仍然没有获得sourceFile(它为null)或sourceLineNumber(它为0)的值.
解决方法
看一下DeveloperErrorPageMiddleware
implementation:
private IEnumerable<ErrorDetails> GetErrorDetails(Exception ex) { for (var scan = ex; scan != null; scan = scan.InnerException) { var stackTrace = ex.StackTrace; yield return new ErrorDetails { Error = scan,StackFrames = StackTraceHelper.GetFrames(ex) .Select(frame => GetStackFrame(frame.MethodDisplayInfo.ToString(),frame.FilePath,frame.LineNumber)) }; }; } // make it internal to enable unit testing internal StackFrame GetStackFrame(string method,string filePath,int lineNumber) { var stackFrame = new StackFrame { Function = method,File = filePath,Line = lineNumber }; if (string.IsNullOrEmpty(stackFrame.File)) { return stackFrame; } IEnumerable<string> lines = null; if (File.Exists(stackFrame.File)) { lines = File.ReadLines(stackFrame.File); } else { // Handle relative paths and embedded files var fileInfo = _fileProvider.GetFileInfo(stackFrame.File); if (fileInfo.Exists) { // ReadLines doesn't accept a stream. Use ReadLines as its more efficient // relative to reading lines via stream reader if (!string.IsNullOrEmpty(fileInfo.PhysicalPath)) { lines = File.ReadLines(fileInfo.PhysicalPath); } else { lines = ReadLines(fileInfo); } } } if (lines != null) { ReadFrameContent(stackFrame,lines,stackFrame.Line,stackFrame.Line); } return stackFrame; }