zoukankan      html  css  js  c++  java
  • 用SignalR实现实时查看WebAPI请求日志

    实现的原理比较直接,定义一个MessageHandler记录WebAPI的请求记录,然后将这些请求日志推送到客户端,客户端就是一个查看日志的页面,实时将请求日志展示在页面中。

    这个例子的目的是演示如何在PersistentConnection类外部给Clients推送消息

    image

    实现过程

    一、服务端

    服务端同时具备SignalR和WebAPI的功能,通过定义一个记录日志的MessageHandler实现对WebAPI请求的拦截,并生成请求记录,然后推送到客户端

    step 1

    创建一个具备WebAPI功能的站点,为了简化起见,设置Authentication为No Authentication

    image

    step 2

    创建一个DelegatingHandler,用于记录WebAPI日志,代码如下

    下面代码没有涉及到SignalR的功能,日志展示是通过ILoggingDisplay接口传入

    public class LoggingHandler : DelegatingHandler 
        { 
            private static readonly string _loggingInfoKey = "loggingInfo";
    
            private ILoggingDisplay _loggingDisplay;
    
            public LoggingHandler(ILoggingDisplay loggingDisplay) 
            { 
                _loggingDisplay = loggingDisplay; 
            }
    
            public LoggingHandler(HttpMessageHandler innerHandler, ILoggingDisplay loggingDisplay) 
                : base(innerHandler) 
            { 
                _loggingDisplay = loggingDisplay; 
            }
    
            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
            { 
                LogRequestLoggingInfo(request); 
                return base.SendAsync(request, cancellationToken).ContinueWith(task => 
                { 
                    var response = task.Result; 
                    LogResponseLoggingInfo(response); 
                    return response; 
                }); 
            }
    
            private void LogRequestLoggingInfo(HttpRequestMessage request) 
            { 
                var info = new ApiLoggingInfo(); 
                info.HttpMethod = request.Method.Method; 
                info.UriAccessed = request.RequestUri.AbsoluteUri; 
                info.IpAddress = HttpContext.Current != null ? HttpContext.Current.Request.UserHostAddress : "0.0.0.0"; 
                info.StartTime = DateTime.Now; 
                ExtractMessageHeadersIntoLoggingInfo(info, request.Headers.ToList()); 
                request.Properties.Add(_loggingInfoKey, info); 
            }
    
            private void LogResponseLoggingInfo(HttpResponseMessage response) 
            { 
                object loggingInfoObject = null; 
                if (!response.RequestMessage.Properties.TryGetValue(_loggingInfoKey, out loggingInfoObject)) 
                { 
                    return; 
                } 
                var info = loggingInfoObject as ApiLoggingInfo; 
                if (info == null) 
                { 
                    return; 
                } 
                info.HttpMethod = response.RequestMessage.Method.ToString(); 
                info.ResponseStatusCode = response.StatusCode; 
                info.ResponseStatusMessage = response.ReasonPhrase; 
                info.UriAccessed = response.RequestMessage.RequestUri.AbsoluteUri; 
                info.IpAddress = HttpContext.Current != null ? HttpContext.Current.Request.UserHostAddress : "0.0.0.0"; 
                info.EndTime = DateTime.Now; 
                info.TotalTime = (info.EndTime - info.StartTime).TotalMilliseconds; 
                _loggingDisplay.Display(info); 
            }
    
            private void ExtractMessageHeadersIntoLoggingInfo(ApiLoggingInfo info, List<KeyValuePair<string, IEnumerable<string>>> headers) 
            { 
                headers.ForEach(h => 
                { 
                    var headerValues = new StringBuilder();
    
                    if (h.Value != null) 
                    { 
                        foreach (var hv in h.Value) 
                        { 
                            if (headerValues.Length > 0) 
                            { 
                                headerValues.Append(", "); 
                            } 
                            headerValues.Append(hv); 
                        } 
                    } 
                    info.Headers.Add(string.Format("{0}: {1}", h.Key, headerValues.ToString())); 
                }); 
            } 
        }
    LoggingHandler

    step 3

    引入SignalR相关packages

    Install-Package Microsoft.AspNet.SignalR

    新建一个PersistentConnection,代码为空即可(因为不涉及到客户端调用,推送也是在类的外部执行)

    public class RequestMonitor : PersistentConnection
    {
    }

    添加一个Startup类

    public void Configuration(IAppBuilder app)
    {
           app.MapSignalR<RequestMonitor>("/monitor");
    }

    step 4

    创建一个类实现ILoggingDisplay接口,用SignalR推送的方式实现这个接口

    代码比较关键的部分就是通过GlobalHost.ConnectionManager获取当前应用程序的connectionContext,得到这个context就可以类似在PersistentConnection内部给clients推送消息

    public class SignalRLoggingDisplay : ILoggingDisplay 
        { 
            /// <summary> 
            /// PersistentConnection上下文 
            /// </summary> 
            private static IPersistentConnectionContext connectionContext = GlobalHost.ConnectionManager.GetConnectionContext<RequestMonitor>();
    
            public void Display(ApiLoggingInfo loggingInfo) 
            { 
                var message = new StringBuilder(); 
                 message.AppendFormat("StartTime:{0},Method:{1},Url:{2},ReponseStatus:{3},TotalTime:{4}" 
                    , loggingInfo.StartTime, loggingInfo.HttpMethod, loggingInfo.UriAccessed, loggingInfo.ResponseStatusCode, loggingInfo.TotalTime); 
                message.AppendLine(); 
                message.AppendFormat("Headers:{0},Body:{1}", string.Join(",", loggingInfo.Headers), loggingInfo.BodyContent); 
                connectionContext.Connection.Broadcast(message.ToString()); 
            }
    SignalRLoggingDisplay

    step 5

    在WebApiConfig的Register方法中添加自定义的handler

    public static void Register(HttpConfiguration config) 
           { 
               config.MapHttpAttributeRoutes();
    
               config.Routes.MapHttpRoute( 
                   name: "DefaultApi", 
                   routeTemplate: "api/{controller}/{id}", 
                   defaults: new { id = RouteParameter.Optional } 
               );
    
               config.MessageHandlers.Add(new LoggingHandler(new SignalRLoggingDisplay())); 
           }
    RegisterHandler

    到此完成服务端的功能实现

    二、客户端

    客户端只需要一个监控页面,html页面足矣,其主要功能是提供开始监控、停止监控功能,其他都是接收服务端的推送并展示功能代码。

    <!DOCTYPE html> 
    <html> 
    <head> 
        <title>Web API页面实时请求监控</title> 
        <meta charset="utf-8" /> 
    </head> 
    <body> 
        <h1>Web API Request Logs</h1> 
        <div> 
            <input type="button" value="start" id="btnStart"/> 
            <input type="button" value="stop" id="btnStop"/> 
            <input type="button" value="clear" id="btnClear"/> 
        </div> 
        <ul id="requests"></ul> 
        <script src="/Scripts/jquery-1.10.2.min.js"></script> 
        <script src="/Scripts/jquery.signalR-2.2.0.min.js"></script> 
        <script> 
            $(function () { 
                var requests = $("#requests"); 
                var startButton = $("#btnStart"); 
                var stopButton = $("#btnStop"); 
                var connection = null;
    
                enable(stopButton, false); 
                enable(startButton, true);
    
                startButton.click(function () { 
                    startConnection(); 
                    enable(stopButton, true); 
                    enable(startButton, false); 
                });
    
                $("#btnClear").click(function () { 
                    $("#requests").children().remove(); 
                });
    
                stopButton.click(function () { 
                    stopConnection(); 
                    enable(stopButton, false); 
                    enable(startButton, true); 
                });
    
                function startConnection() { 
                    stopConnection(); 
                    connection = $.connection("/monitor"); 
                    connection.start() 
                        .fail(function () { 
                            console.log("connect failed"); 
                        }); 
                    connection.received(function (data) { 
                        data = data.replace(/
    /g, "<br/>") 
                        data = data.replace(/
    /g, "<br/>"); 
                        requests.append("<li>" + data + "</li>"); 
                    }); 
                }
    
                function stopConnection() { 
                    if (connection != null){ 
                        connection.stop(); 
                    } 
                }
    
                function enable(button, enabled) { 
                    if (enabled) { 
                        button.removeAttr("disabled"); 
                    } 
                    else { 
                        button.attr("disabled", "disabled"); 
                    } 
                } 
            }); 
        </script> 
    </body> 
    </html>
    Monitor

    示例代码下载

  • 相关阅读:
    RabbitMQ指南之一:"Hello World!"
    Java8新特性之五:Optional
    Java8新特性之四:接口默认方法和静态方法
    Java8新特性之三:Stream API
    Java8新特性之二:方法引用
    Notepad++编辑器——Verilog代码片段和语法检查
    数电(5):半导体存储电路
    数电(4):组合逻辑电路
    DDR3_新版(1):IP核调取和官方例程仿真
    数电(2):逻辑代数的基本定理
  • 原文地址:https://www.cnblogs.com/shenba/p/5430190.html
Copyright © 2011-2022 走看看