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>

Ответ 1

Большое спасибо @LMK, это приятно. У меня была та же проблема, потому что я хочу регистрировать сетевой трафик для анализа ошибок в коде.

С вашим VB-кодом, адаптированным к С#, я написал этот метод:

/// <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 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;
        }
    }
}

Удачи, Марко

Ответ 2

Вы можете создать свою собственную реализацию TraceListener. пример (ы), который я нашел в Интернете, которые настраивают вещи во время выполнения, не показывают работу с источниками трассировки системы. Если вы не против того, чтобы ваши руки были грязными, вы можете попробовать использовать отражение для переключения частного статического bool System.Net.Logging.s_LoggingEnabled (.NET 2).

Возьмите пример в следующей статье и переключите его с отправки сообщений электронной почты на публикацию статического события, на которое вы можете подписаться, когда вы заинтересованы в получении сообщений трассировки:

Расширение System.Diagnostics

Это приводит к поражению производительности, поскольку ведение журнала включено все время (все или ничего как настроено в файле web.config). (См. в этой статье и комментарии, объясняющие важность удаления трассы по умолчанию для повышения производительности.)

Ответ 3

Здесь, как подключить систему System.Net к регистрации через код через отражение. Код находится в VB, но тривиально преобразовать в С#...

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.