使用WCF Ria Service和Log4net在服务器端记录异常日志/ExceptionLog

今天谈下在Silverlight中如何进行异常处理,用WCF Ria Service在服务器端记录异常日志,后台用log4net组件处理logging,前台给用户弹出一个友好的框,用户也可以在客户端保存异常log到本地,就这么点内容。废话不说,开始了!

Silverlight Exception handling

1. 后台实现Wcf Ria service + log4net记录异常日志

在SilverlightApplication1.Web项目添加一个空的Domain Service,名字叫LoggingService,里面加一个写一个Invoke的方法。注意这个DomainService需要加[Invoke]属性标明这不是一个Entity相关的操作。详细解释请参考MSDN这个页面

Domain Service
复制代码
   
   
1 [EnableClientAccess()] 2   public class LoggingService : DomainService 3 { 4 [Invoke] 5 public void LogException( string message, string stackTrace) 6 { 7 Logger.LogException(message, stackTrace); 8 } 9 }
复制代码
2. 后台加个Logger静态类用log4net写log
Logger
复制代码
   
   
1 using log4net; 2 3   public class Logger 4 { 5 static ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 6 7 public static void LogException( string message, string stackTrace) 8 { 9 log.Debug( string .Format( " Exception occured: {0}, stack trace: {1} " , message, stackTrace)); 10 } 11 12 public static void LogInfo( string info) 13 { 14 log.Info(info); 15 } 16 }
复制代码
3. 添加log4net的dll引用,web.config配置记录log的log4net部分

首先下载log4net,然后在SilverlightApplication1.Web项目添加log4net的dll引用,添加一个Global.asax,在Global.asax.cs里面添加以下代码来初始化log4net配置:

Global.asax.cs
   
   
1 protected void Application_Start( object sender, EventArgs e) 2 { 3 log4net.Config.XmlConfigurator.Configure(); 4 }
配置web.config的log4net部分
复制代码
   
   
1 < configuration > 2 < configSections > 3 < section name ="log4net" type ="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> 4 </ configSections > 5 < log4net debug ="false" > 6 <!-- Define output to file --> 7 < appender name ="LogFileAppender" type ="log4net.Appender.FileAppender" > 8 < param name ="File" value ="MyException.log" /> 9 < param name ="datePattern" value ="MM.dd.yyyy HH:mm" /> 10 < param name ="AppendToFile" value ="true" /> 11 < layout type ="log4net.Layout.PatternLayout" > 12 < param name ="ConversionPattern" value ="[%date] %message%newline" /> 13 </ layout > 14 < param name ="lockingModel" value ="log4net.Appender.FileAppender+MinimalLock" /> 15 < param name ="StaticLogFileName" value ="false" /> 16 < param name ="rollingStyle" value ="Date" /> 17 < param name ="datePattern" value ="yyyyMMdd" /> 18 </ appender > 19 < root > 20 <!-- use the next line to not output log.Info and log.debug 21 <level value="WARN" /> 22 --> 23 < level value ="DEBUG" /> 24 < appender-ref ref ="LogFileAppender" /> 25 </ root > 26 </ log4net > 27   </ configuration >
复制代码
4. 前台SilverlightApplication1调用Wcf Ria Service的Domain Context

刚才我们在SilverlightApplication1.Web项目添加了DomainService,编译之后会在有RiaLink的前台项目自动生成客户端代理Generated_Code\*.web.g.cs。如果你有很多WCF Ria Service Class Library,那么需要检查RiaLink,它们之间的对应关系。可以查看项目属性,见下图:

RiaLink

我们在App.xaml.cs里面处理UnhandledException:

App.xaml.cs
复制代码
   
   
1 public App() 2 { 3 this .UnhandledException += this .Application_UnhandledException; 4 5 InitializeComponent(); 6 } 7 8   private void Application_UnhandledException( object sender, ApplicationUnhandledExceptionEventArgs e) 9 { 10 11 if ( ! Debugger.IsAttached) 12 { 13 14 // 记录log到服务器:调用WCF Ria Service 15   LogExceptionToServer(e.ExceptionObject); 16 17 // 弹出用户友好窗口 18   ShowUserFriendlyExceptionWindow(e.ExceptionObject); 19 20 e.Handled = true ; 21 22 } 23 24 } 25 26   private void LogExceptionToServer(Exception e) 27 { 28 var logDomainContext = new LoggingContext(); 29 30 logDomainContext.LogException(e.Message, e.StackTrace, 31 invokeOperation => 32 { 33 if (invokeOperation.HasError) 34 { 35 // .... 36   } 37 }, null ); 38 } 39 40   private void ShowUserFriendlyExceptionWindow(Exception e) 41 { 42 var exceptionDialog = new ExceptionWindow(); 43 exceptionDialog.UserFriendlyException = e.Message; 44 exceptionDialog.DetailedException = e.ToString(); 45 exceptionDialog.Error = e; 46 exceptionDialog.Show(); 47 }
复制代码
5. 发生未知异常显示用户友好的界面

SilverlightException

如果发生异常显示用户友好的界面,可以保存异常到本地,这个ExceptionWindow的代码如下:

Exception ChildWindow
复制代码
   
   
1 public partial class ExceptionWindow : ChildWindow 2 { 3 private Exception _exception; 4 5 public ExceptionWindow() 6 { 7 InitializeComponent(); 8 } 9 10 public string UserFriendlyException 11 { 12 get { return userFriendlyMessageTextBlock.Text; } 13 set 14 { 15 if ( ! String.IsNullOrEmpty(value) && value.Length > 65 ) 16 userFriendlyMessageTextBlock.Text = value.Substring( 0 , 65 ) + " ... " ; 17 else 18 userFriendlyMessageTextBlock.Text = value; 19 } 20 } 21 22 public string DetailedException 23 { 24 get { return detailedInforTextBox.Text; } 25 set { detailedInforTextBox.Text = value; } 26 } 27 28 public Exception Error 29 { 30 set { _exception = value; } 31 } 32 33 private void closeButton_Click( object sender, RoutedEventArgs e) 34 { 35 this .DialogResult = false ; 36 } 37 38 private void saveLogButton_Click( object sender, RoutedEventArgs e) 39 { 40 SaveLogOnClientDisc(FormatMessage()); 41 } 42 43 private string FormatMessage() 44 { 45 string originalException = string .Empty; 46 47 if (_exception != null ) 48 originalException = _exception.ToString(); 49 50 return string .Format( " {0} - {1}\n\nDetailed Message:\n{2}\n\nOriginal Exception:\n{3} " , 51 DateTime.Now.ToString(), 52 UserFriendlyException, 53 DetailedException, 54 originalException); 55 } 56 57 private void SaveLogOnClientDisc( string errorMessage) 58 { 59 var saveLogDialog = new SaveFileDialog(); 60 61 saveLogDialog.DefaultExt = " .log " ; 62 saveLogDialog.Filter = " Text Files|*.txt|Log Files|*.log|All Files|*.* " ; 63 saveLogDialog.FilterIndex = 2 ; 64 65 bool ? dialogResult = saveLogDialog.ShowDialog(); 66 67 if (dialogResult == true ) 68 { 69 try 70 { 71 var contents = Encoding.Unicode.GetBytes(errorMessage); 72 73 using (var fileStream = saveLogDialog.OpenFile()) 74 { 75 fileStream.Write(contents, 0 , contents.Length); 76 fileStream.Close(); 77 MessageBox.Show( " File successfully saved! " ); 78 } 79 } 80 catch (Exception ex) 81 { 82 MessageBox.Show( " Can't save file: " + ex.Message); 83 } 84 } 85 } 86 }
复制代码

ExceptionWindow的xaml代码

Exception Window.xaml
复制代码
   
   
1 < controls:ChildWindow 2 xmlns:controlsToolkit ="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit" x:Class ="SilverlightApplication1.ExceptionWindow" 3 xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:controls ="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" 6 Width ="400" Height ="300" 7 Title ="" > 8 < Grid x:Name ="LayoutRoot" Margin ="2" > 9 < Grid.RowDefinitions > 10 < RowDefinition /> 11 < RowDefinition Height ="Auto" /> 12 </ Grid.RowDefinitions > 13 < StackPanel > 14 < TextBlock FontSize ="12" FontWeight ="Bold" Text ="An exception occured" /> 15 < TextBlock 16 x:Name ="userFriendlyMessageTextBlock" 17 Margin ="0,10,0,0" 18 FontStyle ="italic" 19 TextWrapping ="Wrap" 20 Text ="User friendly message" /> 21 < controlsToolkit:Expander Height ="160" IsExpanded ="True" Header ="More information" Margin ="0,10,0,0" > 22 < controlsToolkit:Expander.Content > 23 < TextBox 24 x:Name ="detailedInforTextBox" 25 AcceptsReturn ="True" 26 VerticalScrollBarVisibility ="Auto" 27 HorizontalScrollBarVisibility ="Auto" 28 Text ="A more detailed information about the exception" /> 29 </ controlsToolkit:Expander.Content > 30 </ controlsToolkit:Expander > 31 < Grid Height ="43" Name ="grid1" Width ="370" > 32 < Button Content ="Save Log..." Height ="26" HorizontalAlignment ="Left" Margin ="96,11,0,0" Name ="saveLogButton" VerticalAlignment ="Top" Width ="124" Click ="saveLogButton_Click" /> 33 < Button Content ="Close" Height ="26" HorizontalAlignment ="Right" Margin ="0,11,0,0" Name ="closeButton" VerticalAlignment ="Top" Width ="124" Click ="closeButton_Click" /> 34 </ Grid > 35 </ StackPanel > 36 </ Grid > 37   </ controls:ChildWindow >
复制代码
6. 在WCF Ria Service内处理异常

在带Entity的Domain Service里面我们应该在OnError里面写服务端日志,方法同上:

OnError
复制代码
   
   
1 [EnableClientAccess()] 2   public class DomainService1 : DomainService 3 { 4 public IEnumerable < Customer > GetCustomers() 5 { 6 throw new ApplicationException( " My exception " ); 7 } 8 9 protected override void OnError(DomainServiceErrorInfo errorInfo) 10 { 11 // Log exception errorInfo.Error 12   } 13 }
复制代码

然后客户端应该这样捕捉Error:

Call Domain Service
复制代码
   
   
1 customerDomainContext.Load < Customer > (ds.GetCustomersQuery(), 2 loadOperation => 3 { 4 if (loadOperation.HasError) 5 { 6 MessageBox.Show(loadOperation.Error.Message); 7 loadOperation.MarkErrorAsHandled(); 8 } 9 } 10 , null );
复制代码
如果没有进行上述处理则会在UnhandledException会捕捉到。
7. 结束

为了安全性考虑,不要把敏感信息比如stackTrace从客户端传递到服务端(本文是示例)来记录log,否则黑客会xxxx。Silverlight异常处理方面,不知道大家有没有更好的方法实现 ?另:如果对Silverlight+Wcf Ria service+EF还不明白的,可以看博客园其他的博文。本人也有Silverlight系列文章可以一读。

8. Enterprise Library 5.0 - Logging Application Block - Silverlight Integration Pack

除了本文说的方法以外,你还可以使用MS Enterprise Library 5.0 - Silverlight Integration Pack,其中的Logging Application Block 实现了WCF在服务器端写log。此处下载Silverlight Integration Pack for Enterprise Library 5.0 - May 2011

Logging Application Block, including:
  • Notification trace listener
  • Isolated storage trace listener
  • Remote service trace listener with support of batch logging
  • Implementation of a WCF Remote logging service that integrates with the desktop version of the Logging Application Block
  • Logging filters
  • Tracing
  • Logging settings runtime change API
  • EF
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值