2016-07-28 11 views
4

Ich versuche, alle ausgehenden Anfragen, die zu Service-Referenzen gehen, einschließlich der vollständigen Anfrage und Antwort Körper zu protokollieren. Ich dachte, ich hätte eine Lösung mit behaviorExtensions, aber nach der Bereitstellung wurde klar, dass die Erweiterung zwischen mehreren Anfragen geteilt wurde.IEndpointBehavior Lebenszyklus/Logging Serviceaufrufe

Hier ist meine aktuellen Code:

public class LoggingBehaviorExtender : BehaviorExtensionElement 
{ 
    public override Type BehaviorType => typeof(LoggingRequestExtender); 
    protected override object CreateBehavior() { return new LoggingRequestExtender(); } 
} 

public class LoggingRequestExtender : IClientMessageInspector, IEndpointBehavior 
{ 
    public string Request { get; private set; } 
    public string Response { get; private set; } 

    #region IClientMessageInspector 

    public virtual object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
    { 
     Request = request.ToString(); 
     Response = null; 
     return null; 
    } 
    public virtual void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) 
    { 
     Response = reply.ToString(); 
    } 

    #endregion 

    #region IEndpointBehavior 

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
    { 
     clientRuntime.MessageInspectors.Add(this); 
    } 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } 

    public void Validate(ServiceEndpoint endpoint) { } 

    #endregion 
} 

Dann, als ich den Punkt erreicht, zu protokollieren, ich das Verhalten extrahieren ...

var lre = client.Endpoint.Behaviors.OfType<LoggingRequestExtender>().FirstOrDefault(); 
var req = lre?.Request; 
var resp = lre?.Response; 

Debug-Protokollierung der LoggingRequestExtender Hinzufügen, fand ich es wurde nur einmal für mehrere Anfragen instanziiert.

Gibt es eine Möglichkeit, sicherzustellen, dass diese Verhaltensklasse für jeden Thread neu instanziiert wird? Oder gibt es eine bessere Möglichkeit, den vollständigen Anfrage-/Antworttext bei Serviceanrufen zu erhalten?

Bearbeiten/Partial Antwort:

Da dieses Schreiben ich, dass der Wert von BeforeSendRequest zurück entdeckt habe, ist in AfterReceiveReply vergangen als correlationState so kann ich die Anfrage und Antwort mit einem guid verbinden:

public virtual object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
{ 
    var guid = Guid.NewGuid(); 
    WebServiceLog.LogCallStart(guid, channel.RemoteAddress.ToString(), request.ToString()); 
    return guid; 
} 

public virtual void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) 
{ 
    Guid guid = (Guid)correlationState; 
    WebServiceLog.LogCallEnd(guid, reply.ToString()); 
} 

Ich sehe zwei Fehler zu diesem Ansatz. Eine, die lebenswert ist, ist, dass dies eine Protokolleinfügung erfordert und dann aktualisieren, anstatt eine einzelne Einfügung.

Die zweite ist mehr ein Problem: Im Falle einer Ausnahme (z. B. Timeout), treffen wir nie AfterRecieveSupply, so dass das Protokoll nicht weiß, was passiert ist. Ich kann separat die Ausnahme log ...

try 
{ 
    response = client.SomeFunction(request); 
} 
catch (Exception ex) 
{ 
    AppLog.Error("Some function failed", ex); 
} 

... aber ich kann nicht einen Weg, den Zugriff auf die guid außerhalb von BeforeSendRequest/AfterReceiveReply so ich habe nichts zu binden das Ausnahmeprotokoll an den Dienstanforderungsprotokoll sehen .

Antwort

0

Es gibt mehrere Ansätze dazu.

1, Die Situation, die Sie beschrieben haben, Anrufe separat protokollieren zu müssen, muss nicht so sein. Wenn sich Ihr WCF-Dienst auf einem Server ohne Lastausgleich befindet, fügen Sie die Anforderung einfach mithilfe der GUID als Schlüssel zu einem MemoryCache hinzu. Wenn die Anfrage eingeht, ziehen Sie die Anfrage ab und melden Sie sich einmal an. Um die zeitgesteuerten Anrufe zu erfassen, könnten Sie einen Prozess für einen Thread ausführen, der den MemoryCache alle x Minuten überprüft, um ihn auszulösen und zu protokollieren (mit einer entsprechenden Sperre, um die Thread-Sicherheit zu gewährleisten).

Wenn der WCF-Dienst in einer Load-Balanced-Umgebung ist, ist alles, was Sie tun, dasselbe wie oben, aber speichern Sie es in einem Datenspeicher vom Typ no sql.

2, Ist der Code, der die ausgehenden Anrufe in Ihrem Bereich für die Änderung macht? Wenn dies der Fall ist, können Sie auf die Erstellung einer Verhaltenserweiterung verzichten und stattdessen eine maßgeschneiderte Nachrichtenprotokollierung erstellen. eine Klasse verwenden, die implementiert IDisposable Sie schöne Code wie diesen schreiben kann ..

RequestMessage request = new RequestMessage(); 
ResponseMessage response = null; 

using (_messageLogger.LogMessage(request,() => response, CallContextHelper.GetContextId(), enabled)) 
{ 
    response = _outboundService.DoSomething(request); 
} 

Diese müssen dann keinen weiteren Prozess jede Zeitüberschreitung Fäden zu erfassen, die in der dispose-Methode behandelt werden.

Wenn Sie mehr Klarheit brauchen dann lassen Sie mich wissen, hoffentlich hilft Ihnen das ...