.Net分布式异常报警系统-客户端及服务端API

客户端的作用就是捕获未处理异常, 发送异常到服务端。 关于捕获未处理异常的方法参考 http://www.cnblogs.com/youring2/archive/2012/04/25/2469974.html  http://www.cnblogs.com/eaglet/archive/2009/02/17/1392191.html 这两篇博客。
总结下来对于web程序来说, 捕获未处理异常有两种方式:
1. 定义一个HttpModule, 在这个module的context_Error方法中来捕获未处理的异常。 
2. 在Global.asax中有一个Application_Error的方法, 当程序发生未处理异常的时候, 会调用这个方法。 
对于以上两种方式, 以上两篇博客也做了对比, 对异常的捕获, HttpModule会优于Global,同时从编程的角度来说, 如果使用Global, 那就意味着代码侵入。 所以最后决定采用了HttpModule的方式。
public class ErrorLogModule : IHttpModule
    {
 
        #region IHttpModule Members
 
        public void Dispose()
        {
        }
 
        public void Init(HttpApplication context)
        {
            context.Error += context_Error;
        }
 
        #endregion
 
        private void context_Error(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication) sender;
            ClientErrorEntity errorMessage = new ClientErrorEntity(application.Server.GetLastError().GetBaseException(),
                application.Context);
            SendErrorEntity.SendError(errorMessage);
        }
    } 
View Code
异常信息组装。 
 public ClientErrorEntity(Exception exception, HttpContext httpContext)
        {
            Id = Guid.NewGuid().ToString("N");
 
            MachineName = Environment.MachineName;
            Ip = Common.GetLocalIp();
            ExceptionType = exception.GetType().FullName;
            ExceptionMessage = exception.Message;
            ExceptionSource = exception.Source;
            ExceptionDetail = exception.ToString();
            DateTime = DateTime.Now;
            HttpException httpException = exception as HttpException;
            if (httpException != null)
            {
                HttpStatusCode = httpException.GetHttpCode();
            }
 
            if (httpContext != null)
            {
                HttpRequest request = httpContext.Request;
                RequestUrl = request.Url.AbsoluteUri;
                ServerVariables = Common.ConvertCollectionToDictionary(request.ServerVariables);
                QueryString = Common.ConvertCollectionToDictionary(request.QueryString);
                Form = Common.ConvertCollectionToDictionary(request.Form);
                Cookies = Common.ConvertCollectionToDictionary(request.Cookies);
            }
            Type = 0;
        }  
View Code
这里实现了异常信息的发送,为了避免影响主逻辑, 采用了异步的方式。 注意一个问题, 对于API的地址, 我们采用的硬编码的方式, 当然正式环境为了避免单点, 应该采用域名的方式, 然后通过负载均衡在在多台服务器中选择。 如果仅仅有一台服务器, 那我们要将这个地址放在配置文件中, 一旦地址发生了变化,可以方便我们更改。  
  public static void SendError(ClientErrorEntity errorMessage)
        {
            try
            {
                string jsonstr = JsonConvert.SerializeObject(errorMessage);
                //异步, 确保不会影响主逻辑
                sendPostdelegate.BeginInvoke("http://192.168.16.39:90/api/Error", jsonstr, ContentType.Json, 3000,
                   Encoding.UTF8,
                   Encoding.UTF8, Response, sendPostdelegate);   //这里请求地址 建议改为域名的方式,  如果没有域名,建议请求地址和webtoken类似为可配置
            }
            catch (Exception exception)
            {
                Common.Log("错误信息上传失败:" + exception.Message);
            }
        } 
View Code
为了标示异常是从哪个网站发出的, 我们会对每个网站分配一个token,token会随异常信息一起发送到服务端。 
 <configSections>
    <section name="WebWarningSetting" type="System.Configuration.SingleTagSectionHandler"/>
  </configSections>
  <WebWarningSetting WebToken="1E8C46FC74DC41569EFF63C92A7A4087"/>  
View Code
服务端API主要功能是接收异常信息,考虑到性能问题, 并没有将异常信息直接入库,而是将异常信息放入到Redis的List中,  因为提供的驱动中Redis的List的Value并不支持对象, 所以这里采用分开存放的方法, 将ErrorEntity.Id放入到队列中, ErrorEntity则以普通的方式set到Redis中, 在获取数据的时候,从队列中获取到ErrorEntity.Id , 这样取出来的Id确保顺序不会变, 再根据这个Id, 通过get取出实体。这种方法其实还有待考虑, 虽然List不支持复杂Value, 但是通过json序列化,我们还是可以直接将ErrorEntity放入到List中。
  public HttpResponseMessage Post([FromBody]ErrorEntityDto errorEntity)
        {
            if (errorEntity != null && !string.IsNullOrWhiteSpace(errorEntity.Id))
            {
                if (RedisHelper.EnqueueItemOnList("ErrorEntityQueue", errorEntity.Id))    //先将ErrorEntity.Id放入到队列中,确保顺序不会变
                {
                    if (RedisHelper.Set(errorEntity.Id, errorEntity))  //将实体添加到Redis中
                    {
                        return ReturnPlainText("ok");
                    }
                    return ReturnPlainText("set  error");
                }
                return ReturnPlainText("add item to list error");
            }
            return ReturnPlainText("modle error");
        }  
 
View Code
posted @ 2015-08-24 22:31  秋夜  阅读(487)  评论(0编辑  收藏  举报