What is API Request and Response Logger

What is API Request and Response Logger

Mr. Shailesh Sakaria

,
3–5 minutes
Share on Social Media

    Get The Expert Advice To Grow Your Business Digitally
    Related Blogs
    What is the identity server in ASP.NE core ?
    Read More: What is the identity server in ASP.NE core ?
    What are JavaScript Arrow Functions?
    Read More: What are JavaScript Arrow Functions?
    What are solid principles with examples in C#
    Read More: What are solid principles with examples in C#
    What is distributed Lock Manager in C# and Redis
    Read More: What is distributed Lock Manager in C# and Redis

    Need of API Request/Response Logger

    We often need log of application data that may include the sequence of method calls or events, errors occurs, user actions step by step that application executes. This type of data indeed needs when we don’t have any clue or data through which we can reproduce the critical error – which is occurred in production environment. By logging request and response in Web Api is helpful in debugging and tracing and becomes easy in detecting problems.

    Similar like authentication, exception logging, filtering request – which is function that affects the entire application that should be centralized. The logging of request/response in web API is similar like to handle it centralized.

    How we can log every API request and response:

    There are many frameworks available to log unexpected errors like SeriLog, ELMAH etc., Similar like we can create our own “Message Handled” which will register once in web api to log each and every api request/response data.

    Building our custom logger for Web Api:

    Create a new Web API project in Visual Studio and save it with your desired name. We will be taking advantage of a custom delegating handler here to intercept the calls to the Web API.

    Let’s first create the class which will store all information from our request and responses, e.g. request url, request headers, request timestamp, request content type, request body, response status code, response body, response timestamp, response header etc.

    public class ApiLoggerModel
        {
            public long LoggerId { get; set; }                  // The (database) ID for the API log entry.
            public string Application { get; set; }             // The application that made the request.
            public string ApplicaionUser { get; set; }          // The user that made the request.
            public string Machine { get; set; }                 // The machine that made the request.
            public string RequestIpAddress { get; set; }        // The IP address that made the request.
            public string RequestContentType { get; set; }      // The request content type.
            public string RequestContentBody { get; set; }      // The request content body.
            public string RequestUri { get; set; }              // The request URI.
            public string RequestMethod { get; set; }           // The request method (GET, POST, PUT etc).
            public string RequestHeaders { get; set; }          // The request headers.
            public DateTime? RequestTimestamp { get; set; }     // The request timestamp.
            public string ResponseContentType { get; set; }     // The response content type.
            public string responseContentBody { get; set; }     // The response content body.
            public int? ResponseStatusCode { get; set; }        // The response status code.
            public string responseHeaders { get; set; }         // The response headers.
            public DateTime? ResponseTimestamp { get; set; }    // The response timestamp.
        }

    Now let’s implement custom class called CustomLogHandler which is essentially a message handler that extends the DelegatingHanlder class which has sendAsync method which we can override to log our each API request as well response.

    public class CustomLogHandler : DelegatingHandler
        { 
    protected ove rride async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                var apiLogEntry = CreateApiLogEntryWithRequestData(request);
                var response = (dynamic)null;
                await Task.Run(() =>
                {
                    if (request.Content != null)
                    {
                        request.Content.ReadAsStringAsync()
                            .ContinueWith(task =>
                            {
                                apiLogEntry.RequestContentBody = task.Result;
                            }, cancellationToken);
                    }
                });
                await base.SendAsync(request, cancellationToken)
                       .ContinueWith(task =>
                       {
                           response = task.Result;
                           Task.Run(() =>
                           {
                               apiLogEntry = CreateApiLogEntryWithResponseData(response, apiLogEntry);
                               ILoggerStorage apiLoggerStorage = factory.GetLoggerStorage(LoggerStorageOptions);
                               SendToLog (apiLogEntry);
                           }, cancellationToken);
                       });
                return response;
            }
    private async Task SendToLog(ApiLoggerModel apiLogEntry)
            {
                // TODO: Write code here to store the ApiLoggerModel instance to a pre-configured log store...
                return true;
            }
    }

    The method CreateApiLogEntryWithRequestData and CreateApiLogEntryWithReponseData used to assign all meta data related to API request as well response to ApiLoggerModel properties as well serialized the request/response header data.(See the code below).

    private ApiLoggerModel CreateApiLogEntryWithRequestData(HttpRequestMessage request)
            {
                var context = ((HttpContextBase)request.Properties["MS_HttpContext"]);
                string RequestHeaders = SerializeHeaders(request.Headers);
                return new ApiLoggerModel
                {
                    ApplicaionUser = context.User.Identity.Name,
                    Machine = Environment.MachineName,
                    RequestContentType = context.Request.ContentType,
                    RequestIpAddress = context.Request.UserHostAddress,
                    RequestMethod = request.Method.Method,
                    RequestTimestamp = DateTime.Now,
                    RequestHeaders = RequestHeaders,
                    RequestUri = request.RequestUri.ToString()
                };
            }
    private ApiLoggerModel CreateApiLogEntryWithResponseData(dynamic response, ApiLoggerModel apiLogEntry)
            {
                apiLogEntry.ResponseStatusCode = (int)response.StatusCode;
                apiLogEntry.ResponseTimestamp = DateTime.Now;
                if (response.Content != null)
                {
                    apiLogEntry.responseContentBody = response.Content.ReadAsStringAsync().Result;
                    apiLogEntry.ResponseContentType = response.Content.Headers.ContentType.MediaType;
                    apiLogEntry.responseHeaders = SerializeHeaders(response.Content.Headers);
                }
                return apiLogEntry;
            }
            private string SerializeHeaders(HttpHeaders headers)
            {
                var dict = new Dictionary<string, string="">();
                foreach (var item in headers.ToList())
                {
                    if (item.Value != null)
                    {
                        var header = String.Empty;
                        foreach (var value in item.Value)
                        {
                            header += value + " ";
                        }
                        // Trim the trailing space and add item to the dictionary
                        header = header.TrimEnd(" ".ToCharArray());
                        dict.Add(item.Key, header);
                    }
                }
                return JsonConvert.SerializeObject(dict, Formatting.Indented);
            }
    </string,>

    Note that you need to write the necessary code to store the ApiLoggerModel instance shown in SendToLog method to a pre-configured log target, i.e. file or database. I have preferred to write either on file or sql db or mongoDB.

    Building our custom logger for Web Api:

    To register this CustomMessageHandler class, you can take the advantage of the Application_Start() event in the Global.asax.cs file. The following code snippet demonstrates how you can register the custom handler.

    protected void Application_Start()
            {
                // Your as usual code here
    GlobalConfiguration.Configuration.MessageHandlers.Add(new CustomLogHandler());
            }

    Full source code on github is here which has logger facility to log either on text file or Sql DB or MongoDB with just changes in web.config file.

    Share on Social Media

      Get The Expert Advice To Grow Your Business Digitally
      Related Blogs
      Unlocking Business Success Online: Why Businesses Need The Best Digital Marketing Services in 2024
      Read More: Unlocking Business Success Online: Why Businesses Need The Best Digital Marketing Services in 2024
      Generative AI and Its Impact: Everything You Need to Know About This Creative Technology
      Read More: Generative AI and Its Impact: Everything You Need to Know About This Creative Technology
      5 best tips to become a skilled programmer
      Read More: 5 best tips to become a skilled programmer
      What is the difference between .NET and Node.js?
      Read More: What is the difference between .NET and Node.js?

      Stay ahead of the curve

      Get the latest insights, tutorials, and industry news delivered straight to your
      inbox. Join 10,000+ developers and tech leaders.

      Get In Touch