Best way to log errors in WCF - c#

What's the best way to catch and log errors when developing a WCF service layer, and why?
I can think of three ways,
1) Manual try/catches around each method.
2) Leave the responsibility to the WCF engine.
3) Use a third party library such as Enterprise Library Policy Injection/Logging.

I would implement custom IErrorHandler and use log4net
[AttributeUsage (AttributeTargets.Interface)]
public class ErrorPolicyBehaviorAttribute : Attribute, IContractBehavior, IErrorHandler
{
private ILog m_logger;
#region IErrorHandler
public void ProvideFault (Exception error, MessageVersion version, ref Message fault)
{
return;
}
public bool HandleError (Exception error)
{
m_logger.Error (error.Message, error);
return true;
}
#endregion
#region IContractBehavior
public void ApplyDispatchBehavior (ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
...init logger
......Add this class to a list of dispatchRuntime.ChannelDispatcher.ErrorHandlers...
}
#endregion
}
This class also implements IContractBehavior, so you can use it as Attribute on your service contracts.
[ErrorPolicyBehavior]
public interface IYourServiceContract
{ }
log4net is quite flexible, so you can log what you need and when you need.

WCF can be configured to output traces for process milestones across all components of the applications, such as operation calls, code exceptions, warnings and other significant processing events.
The following is an app.config example to enable tracing.
<configuration>
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Warning" propagateActivity="true" >
<listeners>
<add name="xml"/>
</listeners>
</source>
<source name="myUserTraceSource" switchValue="Warning, ActivityTracing">
<listeners>
<add name="xml"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xml"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="TraceLog.svclog" />
</sharedListeners>
</system.diagnostics>
</configuration>
You can read more about WCF Tracing from MSDN: Configuring Tracing.
Microsoft provides a Service Trace Viewer Tool to read .svclog files.
Apart from tracing, you may also want to consider using log4net for in-application logging.

If you are asking for logging framework ELMAH is also a good option to consider. If you dont like to litter your code with try/catch around each method, you can try using AOP frameworks which would give you the ability to handle exceptions by marking the method with Attributes

It might be worth your while to check out log4net. There is a good tutorial here on CodeProject.

I would go with number 1. Mainly because of reduced overhead over number 3 and number 2 should just be a no-no.
Granted you still want to log to something say like a file or event manager. But I personnally would use log4net for it since it's a bit lighter than all the entlib stuff.

Related

Casting to an invalid enum value results in unspecified exception on clientside

I know that casting an integer to an invalid enum value does not result in an exception in C# due to Flags. Here I was, specifying a table in a DB table and getting its values 1-3 with EntityFramework. On my serverside I cast the values into an enum XyzType ranging from 0-2, which obviously generates the invalid value (XyzType)3. Now i put this value in a XyzDTO and send it to a WCF client. Which upon deserialization fails with this very vague description:
Now i've read some other posts discussing exceptions caused by deserializing invalid XML and it all makes sense.
My question is, how come I did not get any useful information as to what the cause was at all? I spent the last 1.5 hours with this bug, until i figured the difference between the enum and the DB values. Is it considered a flaw in the WCF error handling that no useful info was given? If so, should MS be notified? Do you have any good practices as to how to avoid scenarios like this in the future?
EDIT: Please note i do not have a web.config. My serverside is a WCF service within a windows service, ie. i have app.config.
EDIT2: There seems to me some misunderstanding regarding this issue and what ive tried. Inspecting the InnerExceptions of the CommunicationException in the debugger provides no useful information at all. Also i already have "includeExceptionDetailsInFaults" = true on serverside, but the exception is not thrown by the serverside! It is caused during deserialization on clientside.
EDIT3: Some answers have suggested activating tracing on clientside. I have now attempted this, but apparantly for this issue it does not provide any useful information either. Here is a link to the output.
When discussing enums and WCF, ensure the values are serialized/deserialized by marking up the enum with the DataContract and EnumMember attributes as follows:
[DataContract(Namespace = Namespaces.V1)]
public enum AreaType : int
{
[EnumMember]
Unknown = 0,
[EnumMember]
Urban = 1,
[EnumMember]
Suburban = 2,
[EnumMember]
Rural = 3
}
It seems obvious, but also be aware that the default value is 0. As khlr mentioned, you can activate tracing. You will want to set the customErrors mode to "Off" and compilation for debug to "true" in your web.config while in Development.
<system.web>
<compilation debug="true" />
<customErrors mode="Off" />
</system.web>
Once you've moved off development, you'll want to provide a custom error page and set debug="false".
<system.web>
<compilation debug="false" />
<customErrors mode="RemoteOnly" defaultRedirect="ErrorRedirectPage.html" />
</system.web>
You'll also want to give some thought and design consideration to exception handling. You'll need to ensure you do not lose info. on the exception if you're rethrowing the exceptions. I like to use a simple logging framework, such as NLog, to log the exception, then I'll rethrow the exception back to the original method in the service where I wrap the exception in a FaultException where T is a class that I create for serializing and holding exception information. For example:
[DataContract(Namespace=Namespaces.V1)]
public class MyFaultException
{
[DataMember]
public string Reason { get; set; }
public MyFaultException()
{
this.Reason = "Service encountered an error";
}
public MyFaultException(string reason)
{
this.Reason = reason;
}
}
And in my service method:
try
{
//...
}
catch (Exception ex)
{
// Asynchronously log the error message in a helper class using NLog
LogHelper.LogException(ex, "Error calculating blah.", LogLevel.Error);
throw new FaultException<MyFaultException>(
new MyFaultException(ex.Message), new FaultReason(ex.Message));
}
You might want to define your own classes that derive from Exception to throw your own exceptions.
It's almost always a good idea to activate tracing to get some more detailed informations about the error:
<configuration>
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true">
<listeners>
<add name="traceListener"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData= "c:\log\Traces.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
</configuration>
You can configure tracing in your server's and client's web- or app.config files to get details from both sides.
Edit:
<serviceDebug includeExceptionDetailInFaults="true" /> makes your service transport an occured exception (see here for more details) to the client. If the client doesn't handle that fault, you might won't notice it.
You're having problems with serialization/deserialization and as others already suggested you need more logging. And you've enabled some of it, but not all and not specific for serialization.
Try to use following config for logging:
<configuration>
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true">
<listeners>
<add name="traceListener" />
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="traceListener" />
</listeners>
</source>
<source name="System.Runtime.Serialization"
switchValue="Information">
<listeners>
<add name="traceListener" />
</listeners>
</source>
</sources>
<sharedListeners>
<add name="traceListener"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData= "c:\log\Traces.svclog" />
</sharedListeners>
</system.diagnostics>
<system.serviceModel>
<diagnostics>
<messageLogging
logEntireMessage="true"
logMessagesAtServiceLevel="true"
maxSizeOfMessageToLog="16777216"/>
</diagnostics>
</system.serviceModel>
</configuration>
Here I added two additional trace sources (and enabled message logging): System.Runtime.Serialization will produce any errors (if any) regarding serialization/deserialization process. And message logging will help you understand whether you receive anything on client side.
From callstack in the log you've published it's clear to me that exception happens on the server side. But message logging will show this better.
If you want to use Enum in WCF, you need to define 0 value for Enum. It is very important.
public enum MyEnum{
Default = 0,
SomeValue1 = 1,
SomeValue2= 2,
....
}

Close upcoming Debug Assertion

I am testing Debug DLLs with NUnit and it is very annoying that with every testrun a large amount of Debug Assertions come up.
So i have written a tool which is polling for a new Debug Assertion. the tool is iterating to all windows every x milliseconds and checks if the title is one of the collection of possible Debug Assertion window titles.
In pseudo code it looks like this:
void main()
{
var thread = new Thread(ThreadMethod);
thread.Start();
}
private void ThreadMethod()
{
while(true)
{
foreach(var title in possibleWindowTitles)
{
IntPtr windowHandle = FindWindowHandle(title);
if(windowHandle != IntPtr.Zero)
{
SetToForeground(windowHandle);
SendKeys.SendWait("%I);
}
}
}
}
The codeline SendKeys.SendWait("%I") sends the keycombination of ALT + I to the window, which is the keycombination for the "Continue" Button.
My Question is:
Is there a better option? something like:
An event when a new window comes up
A better method of closing the window (the CloseWindow method from the User32.dll hasn't worked for me -- no Exception, but nothing happens)
A better solution for the SetToForeGround() workaround
The best way would be an suppression of the Assertion -- Is that possible?
My way works, but i think my solution is very prone to errors. So i hole there is someone with a good idea
You can configure the behavior of the dialog box. This is a behavior provided by the DefaultTraceListener, which is configurable in code or via config file. The documentation for Debug.Assert mentions this.
In the config file, you can turn off the UI like this:
<configuration>
<system.diagnostics>
<assert assertuienabled="false" logfilename="c:\myFile.log" />
</system.diagnostics>
</configuration>
You can also just remove the default trace listener from the collection via the config file:
<configuration>
<system.diagnostics>
<trace autoflush="false" indentsize="4">
<listeners>
<remove name="Default" />
<add name="myListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="c:\myListener.log" />
</listeners>
</trace>
</system.diagnostics>
</configuration>
Either of these can also be done via code, with the Debug.Listeners collection.

Add a default TraceListener for all TraceSources in App.config

How can I define a default TraceListener, that is automatically added to all TraceSources, in a net 4.0 c# project?
Currently I have to list every named TraceSource I use in the App.config file like this:
<system.diagnostics>
<sharedListeners>
<add name="MyListener" type="MyListenerType,MyAssemblyName" />
</sharedListeners>
<sources>
<source name="Class1" switchValue="All">
<listeners><add name="MyListener"></add></listeners>
</source>
<source name="Class2" switchValue="All">
<listeners><add name="MyListener"></add></listeners>
</source>
<source name="Class3" switchValue="All">
<listeners><add name="MyListener"></add></listeners>
</source>
... repeat for a gazillion classes ...
</sources>
<system.diagnostics>
I am using a SharedListener that should receive all outputs from all TraceSources, unless otherwise specified. With the above syntax, this requires a manual entry for each TraceSource.
Whenever I introduce a new class with a new TraceSource, I have to update the App.Config. If multiple programs use that assembly, I have to update multiple App.Config. A spelling mistake while updating these entries will not produce any error, it will just silently omit all trace output from the correct source.
Is there a way I can set a default TraceListener via App.config, so that I only have to name specific TraceSources if I want to deviate from the default?
I didn't find a great solution to this, so what I did was at least centralize the creation of TraceSources. Then I can add any of the 'trace' listeners in app.config to these newly created sources:
TraceSource toReturn = new TraceSource(name, filterLevel);
//remove the default trace listener; don't 'clear' the listeners entirely, because that would undo changes made in app.config; this is a decent compromise
toReturn.Listeners.Remove("Default");
//add all global trace listeners from the app.config
toReturn.Listeners.AddRange(Trace.Listeners);
return toReturn;
Now any listeners I add to <system.diagnostics> \ <trace> \ <listeners> will be added to all trace sources I create with this code.
You could add a default listener in the machine config, but that would affect more apps than you want to affect.

Regarding Enable Tracing in WCF

i go through this msdn link http://msdn.microsoft.com/en-us/library/ms733025%28v=vs.110%29.aspx
here is the full settings
<configuration>
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true">
<listeners>
<add name="xml" />
</listeners>
</source>
<source name="CardSpace">
<listeners>
<add name="xml" />
</listeners>
</source>
<source name="System.IO.Log">
<listeners>
<add name="xml" />
</listeners>
</source>
<source name="System.Runtime.Serialization">
<listeners>
<add name="xml" />
</listeners>
</source>
<source name="System.IdentityModel">
<listeners>
<add name="xml" />
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xml"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="c:\log\Traces.svclog" />
</sharedListeners>
</system.diagnostics>
</configuration>
but do not understand
why so many source & listener tag what is the meaning of having multiple source & listener tag ??
if possible please explain regarding multiple source & listener tag in config setting.
another point is
i enable the tracing but saw no file was created in this folder c:\log\Traces.svclog
so someone advise me that run the apps with admin privileged. when i distribute my apps to anyone then how do i know the person has admin privileged or not?
guide me. thanks
Most of your questions are answered inside the linked article
System.ServiceModel.MessageLogging: Logs all messages that flow through the system.
System.Runtime.Serialization: Logs when objects are read or written.
etc. You can log different information on different trace level (e.g. errors to database, warnings to xml file).
to another point:
You can for example request admin context as asked and answered here How to request administrator permissions when the program starts?
OK, so you have different sources because you may want to log problems/messages in different portions of the program. Most inportant for a WCF web service is the System.ServiceModel since that's going to capture the WCF in/out traffic, but you might also want to log trouble with your serialization, IO or IdentityModel (authentication/Authorization). So based on your config, you've got five sources all of which will be using the shared listener, named XML, which will log your text. That's what's going on here in a nutshell, but there's more.
"Can't see c:\log\Traces.svclog" ... if your WCF service is running and responding to requests, you should see something. If not, make sure you're running your service as "administrator", or the ID underwhich you're running your servce has read/write access to the folder.
"When i distribute my apps to anyone then how do i know the person has admin privileged or not?" OK, yes, I can see this as an issue. Here's a block of code, setting a boolean, you could use to 1) determine if the program is run as "admin", and if not B) instruct the user to re-start the program under the proper "admin" rights:
private void GetServicePermissionLevel()
{
bool bAdmin = false;
try {
SecurityIdentifier sidAdmin = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
AppDomain myDomain = Thread.GetDomain();
myDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
WindowsPrincipal myPrincipal = (WindowsPrincipal)Thread.CurrentPrincipal;
if (myPrincipal.IsInRole(sidAdmin)) {
bAdmin = true;
} else {
bAdmin = false;
}
} catch (Exception ex) {
throw new Exception("Error in GetServicePermissionlevel(): ");
} finally {
_ServiceRunAsAdmin = bAdmin;
}
}
Also, if you're trying to debug host-side problems in your WCF service, you might want to turn on this parameter for your web host interface: IncludeExceptoinDetailsInFaults = True. It's false by default, but if you set this to true to expose more information about WCF service faults, make sure you turn it off before going to production.
Here's an additional URL to consider for your setup: http: //www.topwcftutorials.net/2012/06/simple-steps-to-enable-tracing-in-wcf.html

webservices trace / log

I have set of web services and I want to add a trace layer.
I don't want to modify each web service since I have many.
I would like to write log every entering to a web service: name of web service and parameters.
What is the best way to do so?
P.S. I am using asp.net and C#.
EDIT:
I only want to wrap the web services as each one will have log(..) at the beginning.
A common way to achieve this is to inject a SOAP extension. From there you can intercept every request/response packet in raw SOAP. The sample shows how to implement one, and the explanation describes how it works and how to configure it.
Sample:
http://msdn.microsoft.com/en-us/library/system.web.services.protocols.soapextension.aspx
Explanation:
http://msdn.microsoft.com/en-us/library/esw638yk(vs.71).aspx
Configuration:
http://msdn.microsoft.com/en-us/library/b5e8e7kk(v=vs.71).aspx
<configuration>
<system.web>
<webServices>
<soapExtensionTypes>
<add type="{Type name}, {Assembly}" priority="1" group="0" />
</soapExtensionTypes>
</webServices>
</system.web>
</configuration>
Add a Global Application Class Global.asax file to your project and add the logging logic to the Application_BeginRequest() method. The sender object will contain the HTTP Request and parameters. You can filter for just .asmx requests and log those.
protected void Application_BeginRequest(object sender, EventArgs e)
{
}
EDIT--
Give PostSharp a try. It's the easiest way to get this functionality. For posterity I will leave my posting below but just ignore it and use PostSharp.
If your web services are WCF then you should check out http://msdn.microsoft.com/en-us/magazine/cc163302.aspx.
At each step along the way they provide extensibility points that you can plug into. You can use these extensibility points to implement a wide variety of custom behaviors including message or parameter validation, message logging, message transformations.
No doubt this is the way to go for WCF services. Otherwise, if they are just web services then you can use the Unity framework and hookup and Interceptor to do the same thing.
If the progamming language is not important, you may put Apache Synapse as proxy in front of your services. Your clients will then send the requests to Synapse, which will delegate the requests to your original services. The proxy can be configured to do something with the requests in between, such as logging.
Please see the following links for more information:
http://synapse.apache.org/Synapse_Configuration_Language.html#proxy,
http://synapse.apache.org/Synapse_Configuration_Language.html#send,
http://synapse.apache.org/Synapse_Configuration_Language.html#log
A combination of the following examples could work for you:
http://synapse.apache.org/Synapse_Samples.html#Sample0
http://synapse.apache.org/Synapse_Samples.html#ProxyServices
e.g.:
<definitions xmlns="http://ws.apache.org/ns/synapse"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ws.apache.org/ns/synapse http://synapse.apache.org/ns/2010/04/configuration/synapse_config.xsd">
<proxy name="StockQuoteProxy">
<target>
<endpoint>
<address uri="http://localhost:9000/services/SimpleStockQuoteService"/>
</endpoint>
<outSequence>
<!-- log all attributes of messages passing through -->
<log level="full"/>
<!-- Send the message to implicit destination -->
<send/>
</outSequence>
</target>
<publishWSDL uri="file:repository/conf/sample/resources/proxy/sample_proxy_1.wsdl"/>
</proxy>
How about writing your own HttpModule? That would negate the need to touch the existing web service code. You would just need to add your module to each web.config file.
I maintain an Open source web services framework that lets you simply achieve this by having all web services inherit from a base class and do your own logging.
Here is an example of a base-class where I maintain a distributed rolling log for all exceptions in redis - a very fast NoSQL data store:
public object Execute(TRequest request)
{
try
{
//Run the request in a managed scope serializing all
return Run(request);
}
catch (Exception ex)
{
return HandleException(request, ex);
}
}
protected object HandleException(TRequest request, Exception ex)
{
var responseStatus = ResponseStatusTranslator.Instance.Parse(ex);
if (EndpointHost.UserConfig.DebugMode)
{
// View stack trace in tests and on the client
responseStatus.StackTrace = GetRequestErrorBody() + ex;
}
Log.Error("ServiceBase<TRequest>::Service Exception", ex);
//If Redis is configured, maintain rolling service error logs in Redis (an in-memory datastore)
var redisManager = TryResolve<IRedisClientsManager>();
if (redisManager != null)
{
try
{
//Get a thread-safe redis client from the client manager pool
using (var client = redisManager.GetClient())
{
//Get a client with a native interface for storing 'ResponseStatus' objects
var redis = client.GetTypedClient<ResponseStatus>();
//Store the errors in predictable Redis-named lists i.e.
//'urn:ServiceErrors:{ServiceName}' and 'urn:ServiceErrors:All'
var redisSeriviceErrorList = redis.Lists[UrnId.Create(UrnServiceErrorType, ServiceName)];
var redisCombinedErrorList = redis.Lists[UrnId.Create(UrnServiceErrorType, CombinedServiceLogId)];
//Append the error at the start of the service-specific and combined error logs.
redisSeriviceErrorList.Prepend(responseStatus);
redisCombinedErrorList.Prepend(responseStatus);
//Clip old error logs from the managed logs
const int rollingErrorCount = 1000;
redisSeriviceErrorList.Trim(0, rollingErrorCount);
redisCombinedErrorList.Trim(0, rollingErrorCount);
}
}
catch (Exception suppressRedisException)
{
Log.Error("Could not append exception to redis service error logs", suppressRedisException);
}
}
var responseDto = CreateResponseDto(request, responseStatus);
if (responseDto == null)
{
throw ex;
}
return new HttpResult(responseDto, null, HttpStatusCode.InternalServerError);
}
Otherwise for normal ASP.NET web services frameworks I would look at the Global.asax events, specifically the 'Application_BeginRequest' event which Fires each time a new request comes in.
I don't know if this is what you are looking for ,just add this to you WCF config file after the ""
It will create very extensive logging that you will be able to read using the Microsoft Service Trace Viewer
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging" switchValue="Warning, ActivityTracing">
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="Default">
<filter type="" />
</add>
<add name="ServiceModelMessageLoggingListener">
<filter type="" />
</add>
</listeners>
</source>
<source name="System.ServiceModel" switchValue="Warning, ActivityTracing"
propagateActivity="true">
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="Default">
<filter type="" />
</add>
<add name="ServiceModelTraceListener">
<filter type="" />
</add>
</listeners>
</source>
</sources>
<sharedListeners>
<add initializeData="C:\ServiceLog.svclog"
type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
name="ServiceModelMessageLoggingListener" traceOutputOptions="Timestamp">
<filter type="" />
</add>
<add initializeData="C:\Tracelog.svclog"
type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
name="ServiceModelTraceListener" traceOutputOptions="Timestamp">
<filter type="" />
</add>
</sharedListeners>
</system.diagnostics>

Categories

Resources