如何自动配置/时间httpwebrequest到一个特定的域
本文关键字:一个 配置 何自动 时间 httpwebrequest | 更新日期: 2023-09-27 18:04:55
我的c# WebAPI使用HTTP请求与后端数据库(Couchbase)对话。我无法控制执行调用的实际库,因此我不能简单地从代码中计时,但我希望保存对数据库调用的计时,以用于SLA目的。
是否有一种方法可以拦截HTTP调用到使用Net的特定域。追踪之类的,然后保存通话时间?类似于Chrome的网络选项卡提供的功能
这是我对这个问题的粗略解决方案。我仍在寻找更好的解决方案…
支持系统。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