Logging Web API Requests

Inevitably, if you’re building an API, you’re going to want to monitor requests made to that API. A big step in doing that is logging each API request so that you can capture and determine important information about your endpoints, such as data being sent in, data being returned, how many times endpoints were invoked, and how long it took endpoints to serve their requests.

ASP.NET Web API makes logging API requests straightforward with a DelegatingHandler, which is part of the message handlers responsible for processing various parts of the Web API request pipeline. Implementing your own DelegatingHandler allows you to intercept API calls so that you can take additional action, such as validating API keys, performing authentication, or in this case, logging API requests.

The ApiLogEntry

For this demonstration, we’re going to log the data that came in as part of the request and the data being returned as part of the response. To capture this set of data we have a class named ApiLogEntry, and this is what we’ll persist to our database.

The ApiLogHandler

With that in place we can take a look at the ApiLogHandler class, our custom DelegatingHandler used to intercept all API calls:

Some things to note about this class:

  • It inherits from DelegatingHandler; this is important.
  • It uses ContinueWith statements to help avoid potential deadlock issues.
  • It uses the Newtonsoft.Json library to perform serialization.
  • You have to get the calling app from somewhere, such as the Authorization header, query parameter, etc.
  • You have to write the code to save the API log entry to your database.

Registering ApiLogHandler

Now that we have our custom DelegatingHandler, we need to let the Web API infrastructure know it exists, which we do by registering it in the Application_Start method of global.asax:

Done and Done

And that’s it. This pattern can be used as-is or tweaked to satisfy other needs, but as you can see, being able to add your own custom message handler (in the form of as DelegatingHandler) takes minimal effort for something that could have a big benefit to your application.

Featured Image: Some rights reserved by Rick Payette

27 comments

  1. Attila says:

    Good article!

    How would you sanitize the logged response content? For privacy/security reasons you should not log email address, credit card data and such.

  2. Mayagadu says:

    Absolutely Great Article!!!

    I would like to know how I can store this into a database.

    I look forward to hear from you soon.

    • Sagar Nanivadekar says:

      Use Entity Framework to save to DB. It will make the work very easy.

      create a DataContext class as below:

      public class LoggingContext : DbContext
      {
      public DbSet ReqRespLogEntry { get; set; }
      public LoggingContext() : base(“DefaultConnection”)
      {
      }
      }

      Here “DefaultConnection” is entry in your config file for DB connection.

      Then create object of ‘apiLogEntry’ and save it to DB.
      new ApiLogEntry
      {
      ApiLogEntryId = 1,
      Application = “My Service”,


      }

      Use entity framework to save to DB.
      using (var db = new WebAPIRequestLogging.DataContext.LoggingContext())
      {
      db.ReqRespLogEntry.Add(apiLogEntry);
      db.SaveChanges();
      }

      Hope this helps.

  3. Brian says:

    The line
    var routeData = request.GetRouteData();

    is always returning null. Is there something I need to do to get this to work? If I do this

    routeData = request.GetConfiguration().Routes.GetRouteData(request);

    I at least get a non null value but when I go to serialize it I get an error

    “Self referencing loop detected for property ‘Configuration’ with type ‘System.Web.Http.HttpConfiguration’

    • Ram says:

      I was able to log simple route data but I mostly use attribute routing. below is my sample code. and I’m getting — Self referencing loop detected for property ‘aggregateRoute’ with type ‘System.Web.Http.Routing.RouteCollectionRoute’. Path ‘Route[0].DataTokens.actions[0].Configuration.Initializer.target0’.”

      sample code:
      [RoutePrefix(“api/v1/users/me/activities”)]
      public class UserActivityController : ApiController
      {
      [Route(“recent”)]
      [HttpGet]
      public virtual IHttpActionResult recentActivities(int count)
      {

      return Ok(“2 activities found”);
      }
      }

      I tried adding JsonSerializerSettings but no luck. any one resolved this self referencing loop issue??

      private string SerializeRouteData(IHttpRouteData routeData)
      {
      return JsonConvert.SerializeObject(routeData, Formatting.Indented,
      new JsonSerializerSettings
      {
      PreserveReferencesHandling = PreserveReferencesHandling.Objects
      });
      }

  4. Burak Ozkan says:

    Hi,

    @Brian: Try this;
    return JsonConvert.SerializeObject(routeData, Formatting.Indented, new JsonSerializerSettings()
    {
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
    });

    @Nadav & Abdallah: You can set identity.Name property with this;
    identity.AddClaim(new Claim(ClaimTypes.Name, “UserName”));

  5. Sagar Nanivadekar says:

    Good stuff.
    My Request contains Credit card information which i dont want to Log Database, for obvious security reasons.
    How can we ‘remove’ that piece of info to be logged?

    Thanks in advance.

  6. naresh says:

    how do you handle an error scenario i.e. lets say I have a global error handler which gets called during an exception and I want to log the request received date time. How do you handle the request received time in the Global error handler and log it to the database.

Leave a Reply

Your email address will not be published. Required fields are marked *