如何自动配置/时间httpwebrequest到一个特定的域

本文关键字:一个 配置 何自动 时间 httpwebrequest | 更新日期: 2023-09-27 18:04:55

我的c# WebAPI使用HTTP请求与后端数据库(Couchbase)对话。我无法控制执行调用的实际库,因此我不能简单地从代码中计时,但我希望保存对数据库调用的计时,以用于SLA目的。

是否有一种方法可以拦截HTTP调用到使用Net的特定域。追踪之类的,然后保存通话时间?类似于Chrome的网络选项卡提供的功能

如何自动配置/时间httpwebrequest到一个特定的域

这是我对这个问题的粗略解决方案。我仍在寻找更好的解决方案…

支持系统。Web.config

网络登录
<system.diagnostics>
    <trace autoflush="true" /> 
    <sources>
        <source name="System.Net" maxdatasize="1024">
            <listeners>
                <add name="NetTimingParserListener"/>
            </listeners>
        </source>
    </sources>
    <sharedListeners>
            <add name="NetTimingParserListener" type="..." />
    </sharedListeners>
    <switches>
        <add name="System.Net" value="Verbose" />
    </switches>
</system.diagnostics>

上面的配置将启用详细日志记录,我将使用自定义跟踪侦听器捕获详细日志记录。这个跟踪侦听器将动态地解析日志文件,并尝试将网络事件关联起来。存储时间

接下来,我创建了一个对象来存储相关事件并计算它们之间的时间。

/// <summary>
/// Measure and store status and timings of a given Network request.
/// </summary>
public class RequestTrace
{
    private Stopwatch _timer = new Stopwatch();
    /// <summary>
    ///     Initializes a new instance of the <see cref="Object" /> class.
    /// </summary>
    public RequestTrace(string id, Uri url)
    {
        Id = new Stack<string>();
        Id.Push(id);
        Url = url;
        IsFaulted = false;
    }
    /// <summary>
    /// Any Id's that are associated with this request.  Such as
    /// HttpWebRequest, Connection, and associated Streams.
    /// </summary>
    public Stack<string> Id { get; set; }
    /// <summary>
    /// The Url of the request being made.
    /// </summary>
    public Uri Url { get; private set; }
    /// <summary>
    /// Time in ms for setting up the connection.
    /// </summary>
    public long ConnectionSetupTime { get; private set; }
    /// <summary>
    /// Time to first downloaded byte.  Includes sending request headers,
    /// body and server processing time.
    /// </summary>
    public long WaitingTime { get; private set; }
    /// <summary>
    /// Time in ms spent downloading the response.
    /// </summary>
    public long DownloadTime { get; private set; }
    /// <summary>
    /// True if the request encounters an error.
    /// </summary>
    public bool IsFaulted { get; private set; }
    /// <summary>
    /// Call this method when the request begins connecting to the server.
    /// </summary>
    public void StartConnection()
    {
        _timer.Start();
    }
    /// <summary>
    /// Call this method when the requst successfuly connects to the server.  Otherwise, fall <see cref="Faulted"/>.
    /// </summary>
    public void StopConnection()
    {
        _timer.Stop();
        ConnectionSetupTime = _timer.ElapsedMilliseconds;
        _timer.Reset();
    }
    /// <summary>
    /// Call this method after connecting to the server.
    /// </summary>
    public void StartWaiting()
    {
        _timer.Start();
    }

    /// <summary>
    /// Call this method after receiving the first byte of the HTTP server
    /// response.
    /// </summary>
    public void StopWaiting()
    {
        _timer.Stop();
        WaitingTime = _timer.ElapsedMilliseconds;
        _timer.Reset();
    }
    /// <summary>
    /// Call this method after receiving the first byte of the HTTP reponse.
    /// </summary>
    public void StartDownloadTime()
    {
        _timer.Start();
    }
    /// <summary>
    /// Call this method after the response is completely received.
    /// </summary>
    public void StopDownloadTime()
    {
        _timer.Stop();
        DownloadTime = _timer.ElapsedMilliseconds;
        _timer = null;
    }
    /// <summary>
    /// Call this method if an Exception occurs.
    /// </summary>
    public void Faulted()
    {
        DownloadTime = 0;
        WaitingTime = 0;
        ConnectionSetupTime = 0;
        IsFaulted = true;
        if (_timer.IsRunning)
        {
            _timer.Stop();
        }
        _timer = null;
    }
    /// <summary>
    ///     Returns a string that represents the current object.
    /// </summary>
    /// <returns>
    ///     A string that represents the current object.
    /// </returns>
    public override string ToString()
    {
        return IsFaulted
            ? String.Format("Request to node `{0}` - Exception", Url.DnsSafeHost)
            : String.Format("Request to node `{0}` - Connect: {1}ms - Wait: {2}ms - Download: {3}ms", Url.DnsSafeHost,
                ConnectionSetupTime, WaitingTime, DownloadTime);
    }
}

系统。Net实际上没有与相同请求相关的单个ID。你可以使用线程ID,但这会很快崩溃,所以我需要跟踪许多不同的对象(HttpWebRequest, Connection, ConnectStream等),并跟随它们在日志中相互关联。我不知道有任何内置的。net类型允许您将多个键映射到单个值,因此我创建了这个粗略的集合。

/// <summary>
/// Specialized collection that associates multiple keys with a single item.
/// 
/// WARNING: Not production quality because it does not react well to dupliate or missing keys.
/// </summary>
public class RequestTraceCollection
{
    /// <summary>
    /// Internal dictionary for doing lookups.
    /// </summary>
    private readonly Dictionary<string, RequestTrace> _dictionary = new Dictionary<string, RequestTrace>();
    /// <summary>
    /// Retrieve an item by <paramref name="key"/>.
    /// </summary>
    /// <param name="key">Any of the keys associated with an item</param>
    public RequestTrace this[string key]
    {
        get { return _dictionary[key]; }
    }
    /// <summary>
    /// Add an <paramref name="item"/> to the collection.  The item must
    /// have at least one string in the Id array.
    /// </summary>
    /// <param name="item">A RequestTrace object.</param>
    public void Add(RequestTrace item)
    {
        _dictionary.Add(item.Id.Peek(), item);
    }
    /// <summary>
    /// Given an <paramref name="item"/> in the collection, add another key
    /// that it can be looked up by.
    /// </summary>
    /// <param name="item">Item that exists in the collection</param>
    /// <param name="key">New key alias</param>
    public void AddAliasKey(RequestTrace item, string key)
    {
        item.Id.Push(key);
        _dictionary.Add(key, item);
    }
    /// <summary>
    /// Remove an <paramref name="item"/> from the collection along with any
    /// of its key aliases.
    /// </summary>
    /// <param name="item">Item to be removed</param>
    public void Remove(RequestTrace item)
    {
        while (item.Id.Count > 0)
        {
            var key = item.Id.Pop();
            _dictionary.Remove(key);
        }
    }
}

最后,需要创建自定义TraceListener并解析记录的消息。

public class HttpWebRequestTraceListener : TraceListener
{
    private readonly RequestTraceCollection _activeTraces = new RequestTraceCollection();
    private readonly Regex _associatedConnection =
        new Regex(@"^'['d+'] Associating (Connection#'d+) with (HttpWebRequest#'d+)",
            RegexOptions.Compiled & RegexOptions.IgnoreCase & RegexOptions.Singleline);
    private readonly Regex _connected = new Regex(@"^'['d+'] (ConnectStream#'d+) - Sending headers",
        RegexOptions.Compiled & RegexOptions.IgnoreCase & RegexOptions.Singleline);
    private readonly Regex _newRequest =
        new Regex(@"^'['d+'] (HttpWebRequest#'d+)::HttpWebRequest'(([http|https].+)')",
            RegexOptions.Compiled & RegexOptions.IgnoreCase & RegexOptions.Singleline);
    private readonly Regex _requestException = new Regex(@"^'['d+'] Exception in (HttpWebRequestm#'d+)::",
        RegexOptions.Compiled & RegexOptions.IgnoreCase & RegexOptions.Singleline);
    private readonly Regex _responseAssociated =
        new Regex(@"^'['d+'] Associating (HttpWebRequest#'d+) with (ConnectStream#'d+)",
            RegexOptions.Compiled & RegexOptions.IgnoreCase & RegexOptions.Singleline);
    private readonly Regex _responseComplete = new Regex(@"^'['d+'] Exiting (ConnectStream#'d+)::Close'(')",
        RegexOptions.Compiled & RegexOptions.IgnoreCase & RegexOptions.Singleline);
    private readonly Regex _responseStarted = new Regex(@"^'['d+'] (Connection#'d+) - Received status line: (.*)",
        RegexOptions.Compiled & RegexOptions.IgnoreCase & RegexOptions.Singleline);
    /// <summary>
    ///     When overridden in a derived class, writes the specified
    ///     <paramref name="message" /> to the listener you create in the derived
    ///     class.
    /// </summary>
    /// <param name="message">A message to write.</param>
    public override void Write(string message)
    {
        // Do nothing here
    }
    /// <summary>
    /// Parse the message being logged by System.Net and store relevant event information.
    /// </summary>
    /// <param name="message">A message to write.</param>
    public override void WriteLine(string message)
    {
        var newRequestMatch = _newRequest.Match(message);
        if (newRequestMatch.Success)
        {
            var requestTrace = new RequestTrace(newRequestMatch.Groups[1].Value,
                new Uri(newRequestMatch.Groups[2].Value));
            requestTrace.StartConnection();
            _activeTraces.Add(requestTrace);
            return;
        }
        var associatedConnectionMatch = _associatedConnection.Match(message);
        if (associatedConnectionMatch.Success)
        {
            var requestTrace = _activeTraces[associatedConnectionMatch.Groups[2].Value];
            _activeTraces.AddAliasKey(requestTrace, associatedConnectionMatch.Groups[1].Value);
            return;
        }
        var connectedMatch = _connected.Match(message);
        if (connectedMatch.Success)
        {
            var requestTrace = _activeTraces[connectedMatch.Groups[1].Value];
            requestTrace.StopConnection();
            requestTrace.StartWaiting();
            return;
        }
        var responseStartedMatch = _responseStarted.Match(message);
        if (responseStartedMatch.Success)
        {
            var requestTrace = _activeTraces[responseStartedMatch.Groups[1].Value];
            requestTrace.StopWaiting();
            requestTrace.StartDownloadTime();
            return;
        }
        var responseAssociatedMatch = _responseAssociated.Match(message);
        if (responseAssociatedMatch.Success)
        {
            var requestTrace = _activeTraces[responseAssociatedMatch.Groups[1].Value];
            _activeTraces.AddAliasKey(requestTrace, responseAssociatedMatch.Groups[2].Value);
            return;
        }
        var responseCompleteMatch = _responseComplete.Match(message);
        if (responseCompleteMatch.Success)
        {
            var requestTrace = _activeTraces[responseCompleteMatch.Groups[1].Value];
            requestTrace.StopDownloadTime();
            _activeTraces.Remove(requestTrace);
            // TODO: At this point the request is done, use this time to store & forward this log entry
            Debug.WriteLine(requestTrace);
            return;
        }
        var faultedMatch = _requestException.Match(message);
        if (faultedMatch.Success)
        {
            var requestTrace = _activeTraces[responseCompleteMatch.Groups[1].Value];
            requestTrace.Faulted();
            _activeTraces.Remove(requestTrace);
            // TODO: At this point the request is done, use this time to store & forward this log entry
            Debug.WriteLine(requestTrace);
        }
    }
}

我相信你正在寻找的是一个动作过滤器,你可以在asp.net官方网站上看到它的一个例子:

http://www.asp.net/mvc/overview/older-versions-1/controllers-and-routing/understanding-action-filters-cs

相关文章: