Трассировка System.Net (HttpWebRequest) без использования файлов или app.config?
Я хочу захватить определенный, но не весь трафик HttpWebRequest в моем приложении для целей отладки. Это веб-сервис, размещенный на IIS.
Я прочитал, как: настроить трассировку сети. Это прекрасно работает, но я не хочу направлять трассировку в файл из-за возможных проблем с правами доступа к файловой системе, чувствительности данных и т. Д. Я хотел бы записать непосредственно на что-то в памяти, что я могу впоследствии проверить или зашифровать и отправить по электронной почте. Предпочтительно, это не повлечет за собой никаких изменений в файле app.config.
Я попробовал следующее, но, очевидно, мне не хватает шага, чтобы связать TextWriterTraceListener в System.Net. Как я могу захватить трафик System.Net в мой StringWriter?
StringWriter sw = new StringWriter();
TextWriterTraceListener myListener = new TextWriterTraceListener(sw);
Trace.Listeners.Add(myListener);
HttpWebRequest req = (HttpWebRequest) WebRequest.Create("http://www.microsoft.com");
HttpWebResponse resp = (HttpWebResponse) req.GetResponse();
Stream s = resp.GetResponseStream();
byte[] buf = new byte[4096];
while (s.Read(buf, 0, buf.Length) > 0) ;
s.Close();
myListener.Flush();
sw.Flush();
Редактировать: В частности, я хочу сделать эквивалент этого во время выполнения, за исключением того, что я не хочу, чтобы вывод шел в network.log, я хочу, чтобы он шел в строковый буфер, который я настроил для этой цели.
<configuration>
<system.diagnostics>
<sources>
<source name="System.Net.Sockets" tracemode="includehex">
<listeners>
<add name="System.Net.Sockets" type="System.Diagnostics.TextWriterTraceListener" initializeData="network.log" />
</listeners>
</source>
</sources>
<switches>
<add name="System.Net.Sockets" value="Verbose"/>
</switches>
<trace autoflush="true" />
</system.diagnostics>
</configuration>
3 ответа
Большое спасибо @LMK, это приятно. У меня была такая же проблема, потому что я хочу регистрировать сетевой трафик для анализа ошибок в коде.
С вашим VB-кодом, адаптированным к C#, я написал этот метод:
/// <summary>
/// Executes a action with enabled System.Net.Logging with listener(s) at the code-site
///
/// Message from Microsoft:
/// To configure you the listeners and level of logging for a listener you need a reference to the listener that is going to be doing the tracing.
/// A call to create a new TraceSource object creates a trace source with the same name as the one used by the System.Net.Sockets classes,
/// but it's not the same trace source object, so any changes do not have an effect on the actual TraceSource object that System.Net.Sockets is using.
/// </summary>
/// <param name="webTraceSourceLevel">The sourceLevel for the System.Net traceSource</param>
/// <param name="httpListenerTraceSourceLevel">The sourceLevel for the System.Net.HttpListener traceSource</param>
/// <param name="socketsTraceSourceLevel">The sourceLevel for the System.Net.Sockets traceSource</param>
/// <param name="cacheTraceSourceLevel">The sourceLevel for the System.Net.Cache traceSource</param>
/// <param name="actionToExecute">The action to execute</param>
/// <param name="listener">The listener(s) to use</param>
public static void ExecuteWithEnabledSystemNetLogging(SourceLevels webTraceSourceLevel, SourceLevels httpListenerTraceSourceLevel, SourceLevels socketsTraceSourceLevel, SourceLevels cacheTraceSourceLevel, Action actionToExecute, params TraceListener[] listener)
{
if (listener == null)
{
throw new ArgumentNullException("listener");
}
if (actionToExecute == null)
{
throw new ArgumentNullException("actionToExecute");
}
var logging = typeof(WebRequest).Assembly.GetType("System.Net.Logging");
var isInitializedField = logging.GetField("s_LoggingInitialized", BindingFlags.NonPublic | BindingFlags.Static);
if (!(bool)isInitializedField.GetValue(null))
{
//// force initialization
HttpWebRequest.Create("http://localhost");
Thread waitForInitializationThread = new Thread(() =>
{
while (!(bool)isInitializedField.GetValue(null))
{
Thread.Sleep(100);
}
});
waitForInitializationThread.Start();
waitForInitializationThread.Join();
}
var isEnabledField = logging.GetField("s_LoggingEnabled", BindingFlags.NonPublic | BindingFlags.Static);
var webTraceSource = (TraceSource)logging.GetField("s_WebTraceSource", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
var httpListenerTraceSource = (TraceSource)logging.GetField("s_HttpListenerTraceSource", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
var socketsTraceSource = (TraceSource)logging.GetField("s_SocketsTraceSource", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
var cacheTraceSource = (TraceSource)logging.GetField("s_CacheTraceSource", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
bool wasEnabled = (bool)isEnabledField.GetValue(null);
Dictionary<TraceListener, TraceFilter> originalTraceSourceFilters = new Dictionary<TraceListener, TraceFilter>();
//// save original Levels
var originalWebTraceSourceLevel = webTraceSource.Switch.Level;
var originalHttpListenerTraceSourceLevel = httpListenerTraceSource.Switch.Level;
var originalSocketsTraceSourceLevel = socketsTraceSource.Switch.Level;
var originalCacheTraceSourceLevel = cacheTraceSource.Switch.Level;
//System.Net
webTraceSource.Listeners.AddRange(listener);
webTraceSource.Switch.Level = SourceLevels.All;
foreach (TraceListener tl in webTraceSource.Listeners)
{
if (!originalTraceSourceFilters.ContainsKey(tl))
{
originalTraceSourceFilters.Add(tl, tl.Filter);
tl.Filter = new ModifiedTraceFilter(tl, originalWebTraceSourceLevel, webTraceSourceLevel, originalHttpListenerTraceSourceLevel, httpListenerTraceSourceLevel, originalSocketsTraceSourceLevel, socketsTraceSourceLevel, originalCacheTraceSourceLevel, cacheTraceSourceLevel, listener.Contains(tl));
}
}
//System.Net.HttpListener
httpListenerTraceSource.Listeners.AddRange(listener);
httpListenerTraceSource.Switch.Level = SourceLevels.All;
foreach (TraceListener tl in httpListenerTraceSource.Listeners)
{
if (!originalTraceSourceFilters.ContainsKey(tl))
{
originalTraceSourceFilters.Add(tl, tl.Filter);
tl.Filter = new ModifiedTraceFilter(tl, originalWebTraceSourceLevel, webTraceSourceLevel, originalHttpListenerTraceSourceLevel, httpListenerTraceSourceLevel, originalSocketsTraceSourceLevel, socketsTraceSourceLevel, originalCacheTraceSourceLevel, cacheTraceSourceLevel, listener.Contains(tl));
}
}
//System.Net.Sockets
socketsTraceSource.Listeners.AddRange(listener);
socketsTraceSource.Switch.Level = SourceLevels.All;
foreach (TraceListener tl in socketsTraceSource.Listeners)
{
if (!originalTraceSourceFilters.ContainsKey(tl))
{
originalTraceSourceFilters.Add(tl, tl.Filter);
tl.Filter = new ModifiedTraceFilter(tl, originalWebTraceSourceLevel, webTraceSourceLevel, originalHttpListenerTraceSourceLevel, httpListenerTraceSourceLevel, originalSocketsTraceSourceLevel, socketsTraceSourceLevel, originalCacheTraceSourceLevel, cacheTraceSourceLevel, listener.Contains(tl));
}
}
//System.Net.Cache
cacheTraceSource.Listeners.AddRange(listener);
cacheTraceSource.Switch.Level = SourceLevels.All;
foreach (TraceListener tl in cacheTraceSource.Listeners)
{
if (!originalTraceSourceFilters.ContainsKey(tl))
{
originalTraceSourceFilters.Add(tl, tl.Filter);
tl.Filter = new ModifiedTraceFilter(tl, originalWebTraceSourceLevel, webTraceSourceLevel, originalHttpListenerTraceSourceLevel, httpListenerTraceSourceLevel, originalSocketsTraceSourceLevel, socketsTraceSourceLevel, originalCacheTraceSourceLevel, cacheTraceSourceLevel, listener.Contains(tl));
}
}
isEnabledField.SetValue(null, true);
try
{
actionToExecute();
}
finally
{
//// restore Settings
webTraceSource.Switch.Level = originalWebTraceSourceLevel;
httpListenerTraceSource.Switch.Level = originalHttpListenerTraceSourceLevel;
socketsTraceSource.Switch.Level = originalSocketsTraceSourceLevel;
cacheTraceSource.Switch.Level = originalCacheTraceSourceLevel;
foreach (var li in listener)
{
webTraceSource.Listeners.Remove(li);
httpListenerTraceSource.Listeners.Remove(li);
socketsTraceSource.Listeners.Remove(li);
cacheTraceSource.Listeners.Remove(li);
}
//// restore filters
foreach (var kvP in originalTraceSourceFilters)
{
kvP.Key.Filter = kvP.Value;
}
isEnabledField.SetValue(null, wasEnabled);
}
}
Класс ModifiedTraceFilter:
public class ModifiedTraceFilter : TraceFilter
{
private readonly TraceListener _traceListener;
private readonly SourceLevels _originalWebTraceSourceLevel;
private readonly SourceLevels _originalHttpListenerTraceSourceLevel;
private readonly SourceLevels _originalSocketsTraceSourceLevel;
private readonly SourceLevels _originalCacheTraceSourceLevel;
private readonly SourceLevels _modifiedWebTraceTraceSourceLevel;
private readonly SourceLevels _modifiedHttpListenerTraceSourceLevel;
private readonly SourceLevels _modifiedSocketsTraceSourceLevel;
private readonly SourceLevels _modifiedCacheTraceSourceLevel;
private readonly bool _ignoreOriginalSourceLevel;
private readonly TraceFilter _filter = null;
public ModifiedTraceFilter(TraceListener traceListener, SourceLevels originalWebTraceSourceLevel, SourceLevels modifiedWebTraceSourceLevel, SourceLevels originalHttpListenerTraceSourceLevel, SourceLevels modifiedHttpListenerTraceSourceLevel, SourceLevels originalSocketsTraceSourceLevel, SourceLevels modifiedSocketsTraceSourceLevel, SourceLevels originalCacheTraceSourceLevel, SourceLevels modifiedCacheTraceSourceLevel, bool ignoreOriginalSourceLevel)
{
_traceListener = traceListener;
_filter = traceListener.Filter;
_originalWebTraceSourceLevel = originalWebTraceSourceLevel;
_modifiedWebTraceTraceSourceLevel = modifiedWebTraceSourceLevel;
_originalHttpListenerTraceSourceLevel = originalHttpListenerTraceSourceLevel;
_modifiedHttpListenerTraceSourceLevel = modifiedHttpListenerTraceSourceLevel;
_originalSocketsTraceSourceLevel = originalSocketsTraceSourceLevel;
_modifiedSocketsTraceSourceLevel = modifiedSocketsTraceSourceLevel;
_originalCacheTraceSourceLevel = originalCacheTraceSourceLevel;
_modifiedCacheTraceSourceLevel = modifiedCacheTraceSourceLevel;
_ignoreOriginalSourceLevel = ignoreOriginalSourceLevel;
}
public override bool ShouldTrace(TraceEventCache cache, string source, TraceEventType eventType, int id, string formatOrMessage, object[] args, object data1, object[] data)
{
SourceLevels originalTraceSourceLevel = SourceLevels.Off;
SourceLevels modifiedTraceSourceLevel = SourceLevels.Off;
if (source == "System.Net")
{
originalTraceSourceLevel = _originalWebTraceSourceLevel;
modifiedTraceSourceLevel = _modifiedWebTraceTraceSourceLevel;
}
else if (source == "System.Net.HttpListener")
{
originalTraceSourceLevel = _originalHttpListenerTraceSourceLevel;
modifiedTraceSourceLevel = _modifiedHttpListenerTraceSourceLevel;
}
else if (source == "System.Net.Sockets")
{
originalTraceSourceLevel = _originalSocketsTraceSourceLevel;
modifiedTraceSourceLevel = _modifiedSocketsTraceSourceLevel;
}
else if (source == "System.Net.Cache")
{
originalTraceSourceLevel = _originalCacheTraceSourceLevel;
modifiedTraceSourceLevel = _modifiedCacheTraceSourceLevel;
}
var level = ConvertToSourceLevel(eventType);
if (!_ignoreOriginalSourceLevel && (originalTraceSourceLevel & level) == level)
{
if (_filter == null)
{
return true;
}
else
{
return _filter.ShouldTrace(cache, source, eventType, id, formatOrMessage, args, data1, data);
}
}
else if (_ignoreOriginalSourceLevel && (modifiedTraceSourceLevel & level) == level)
{
if (_filter == null)
{
return true;
}
else
{
return _filter.ShouldTrace(cache, source, eventType, id, formatOrMessage, args, data1, data);
}
}
return false;
}
private static SourceLevels ConvertToSourceLevel(TraceEventType eventType)
{
switch (eventType)
{
case TraceEventType.Critical:
return SourceLevels.Critical;
case TraceEventType.Error:
return SourceLevels.Error;
case TraceEventType.Information:
return SourceLevels.Information;
case TraceEventType.Verbose:
return SourceLevels.Verbose;
case TraceEventType.Warning:
return SourceLevels.Warning;
default:
return SourceLevels.ActivityTracing;
}
}
}
Веселись, Марко
Вот как подключить System.Net для регистрации кода с помощью отражения. Код в VB, но тривиально конвертировать в C#...
Dim logging = GetType(Net.HttpWebRequest).Assembly.GetType("System.Net.Logging")
Dim enabled = logging.GetField("s_LoggingEnabled", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static)
enabled.SetValue(Nothing, True)
Dim webTr = logging.GetProperty("Web", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static)
Dim tr as TraceSource = webTr.GetValue(Nothing, Nothing)
tr.Switch.Level = SourceLevels.Verbose
tr.Listeners.Add(New MyTraceListener())
Поместите это в Global.asax Application_Start() с любыми условиями, которые вы хотите включить. Вам может понадобиться Flush() tr перед чтением.
Вы можете создать свою собственную реализацию TraceListener. Примеры, которые я нашел в сети, которые настраивают во время выполнения, не показывают работу с источниками системной трассировки. Если вы не возражаете запачкать руки, попробуйте использовать отражение, чтобы переключить приватный статический bool System.Net.Logging.s_LoggingEnabled (.NET 2).
Возьмите пример из следующей статьи и переключите его с отправки электронной почты на публикацию статического события, на которое вы можете подписаться, когда вы заинтересованы в получении сообщений трассировки:
Расширение системы. Диагностика
Это приводит к снижению производительности, поскольку ведение журнала включено все время (все или ничего, как настроено в файле web.config). (См. Эту статью и комментарии, объясняющие важность удаления трассировки по умолчанию для повышения производительности.)