6

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.

Naor
  • 23,465
  • 48
  • 152
  • 268
  • " I don't want to modify each web service since I have many" - Presumably you could use AOP, Otherwise I think you will have to. ... – Mitch Wheat Jan 08 '11 at 03:11
  • 2
    you could use server access logs –  Jan 08 '11 at 03:14
  • See the MSDN documentation on the SoapExtension class. It has an example of logging. – John Saunders Jan 08 '11 at 03:54
  • It's aspect oriented programming... what he's saying you could do is plug in a DI (dependency injection) container such as Unity, StructureMap, or whatever and add what's called an Interceptor to your code. That's a pretty common approach but I wouldn't say it's the easiest to do. Here's an example of it being done - http://hmadrigal.wordpress.com/2010/12/25/aspect-oriented-programming-and-interceptor-design-pattern-with-unity-2/ – phillip Jan 17 '11 at 19:00

7 Answers7

6

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>
csharptest.net
  • 62,602
  • 11
  • 71
  • 89
1

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)
    {

    }
tawman
  • 2,478
  • 1
  • 15
  • 24
  • Do you have example to the logging logic? In addition, this will log every request. even request of js files, css files and images. I don't need that. – Naor Jan 17 '11 at 16:45
  • @Naor: ((System.Web.HttpApplication)(sender)).Request will give you a System.Web.HttpRequest object that you can inspect. You can do a check for POST to any Url ending in .asmx. You only log the requests matching your conditional check. – tawman Jan 17 '11 at 17:01
  • If AOP and refactoring is not an option. This might be the best by filtering the request and only log for the webservice methods you would like to log. – Martin Buberl Jan 19 '11 at 13:48
1

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.

phillip
  • 2,618
  • 19
  • 22
  • they have a community edition and their snippets/examples show exactly what you want to accomplish which is very little change to existing code. – phillip Jan 19 '11 at 19:31
0

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.

mythz
  • 141,670
  • 29
  • 246
  • 390
  • 3
    @mythz: please disclose your connection to ServiceStack. Even though it's an open-source project, making recommendations like this still amounts to spam. This isn't the first time I've seen you recommend ServiceStack. Among other issues, you haven't shown how it would be used to solve this problem. – John Saunders Jan 19 '11 at 00:43
  • I've made a recommendation on how it would easily be able to be achieved had he be using another web services framework. The original question never specified which it was. My original answer also provided the location of the Global.asax events that will let him achieve his objective. Why don't you just provide a better answer yourself? personally I think your specification to a specific web service technology/framework is the primary motivation for your behaviour. – mythz Jan 19 '11 at 01:02
  • 1
    @mythz: it looked to me like he was specifying ASP.NET web services. That and the fact that I never heard of servicestack before may have had something to do with me asking you politely, to disclose. Thank you for doing so, and I hope you'll do that every time you recommend this framework. You would also be well advised to actually show how your framework would solve this problem. – John Saunders Jan 19 '11 at 01:13
  • servicestack is an ASP.NET web services framework. and I also provided an ASP.NET answer. But point noted, I will try to make to make disclosure more obvious. – mythz Jan 19 '11 at 01:16
  • 1
    @mythz: I am looking for solution to asp.NET web services. Do you have any solution? – Naor Jan 19 '11 at 01:26
  • yeah for sure :) check out http://servicestack.net or https://github.com/mythz/ServiceStack for info. Feel free to ask any questions at: http://groups.google.com/group/servicestack – mythz Jan 19 '11 at 01:35
0

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>

vanto
  • 3,134
  • 18
  • 28
  • I am using asp.Net. I don't believe pushing Apache Synapse is a good idea. I don't need another "system" that I should take care of. I don't like unnecessary integration - I believe there is a solution in the realms of .NET. – Naor Jan 17 '11 at 16:48
0

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.

Richard Poole
  • 3,946
  • 23
  • 29
  • I can use globa.asax the same as your approach. Your approach will log every request. even request of js files, css files and images. I don't need that. – Naor Jan 17 '11 at 16:49
-1

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>
Gaven
  • 371
  • 1
  • 6