Skip to main content

MVC - How to log requests that are coming to our web application

In this blog post we will see where we can hook our code if we need to log all the requests that are coming to our web application.
There are moments when we want to log all the requests that are coming to our web application. From the requests URL, to the query or form parameters.
This task can be done in different ways. In this blog post we will see four different ways to do this.

Action Filter
A solution could be to create an action filter that has access to all the request information. From request we access information like request URL, query params, form params and so on.
public class FooActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpRequestBase request =filterContext.RequestContext.HttpContext.Request;

        string requestType = request.HttpMethod;
        string formParams = request.Form.ToString();
        string queryParams = request.QueryString.ToString();
        string url = request.Path;
    }
}
Each action or controller that will have this action filter attached to it will log this information for us.

Base Controller
It is very common for controllers of a web application to have a base controller. This base controller can be used for this purpose also.
public class BaseController : Controller
{
    protected override void ExecuteCore()
    {
        HttpRequestBase request = this.Request;

        string requestType = request.HttpMethod;
        string formParams = request.Form.ToString();
        string queryParams = request.QueryString.ToString();
        string url = request.Path;
        ...
        base.ExecuteCore();
    }
}

HttpModule
Another option is to create and register an HttpModule that will log all the information that we need. If you need to access information from the Session also, than you should hook to the events that are executed after the session is loaded (PreRequestHandlerExecute). Also, even if you have an instance of the http context (HttpApplication) in the moment when you register to an event, you should never use it when an event is triggered. Each time you should get the context from the sender parameter of the event.
public class FooHttpModule : IHttpModule
    {
        private HttpApplication _context;

        private readonly bool _isActive;

        public FooHttpModule(IProfilingConfiguration profilingConfiguration)
        {
            _isActive = profilingConfiguration.IsProfileActive(ProfilingType.WebRequestContent);
        }

        public void Init(HttpApplication context)
        {
            if (!_isActive)
            {
                return;
            }

            _context = context;
            context.PreRequestHandlerExecute += PreRequestHandlerExecute;
        }

        private void PreRequestHandlerExecute(object sender, EventArgs e)
        {
            try
            {
                MvcApplication context = sender as MvcApplication;
                if (context == null)
                {
                    return;
                }

                HttpRequest request = context.Request;

                string requestExtension = VirtualPathUtility.GetExtension(request.FilePath).ToLower();
                if (!string.IsNullOrWhiteSpace(requestExtension))
                {
                    // js, gif, css and .XXX files request are not logged in the trace.
                    return;
                }

                string requestType = request.HttpMethod;
                string formParams = request.Form.ToString();                
                string queryParams = request.QueryString.ToString();
                string url = request.Path;

                string userInformation = CurrentUserInformation.IsUserLogin
                    ? CurrentUserInformation.AccountInformation.ToString()
                    : string.Empty;


                ...
            }
            catch (Exception exception)
            {
                ...
            }
        }

        public void Dispose()
        {
            lock (this)
            {
                if (_context == null)
                {
                    return;
                }

                _context.PreRequestHandlerExecute -= PreRequestHandlerExecute;
                _context = null;
            }
        }
    }
IIS Logs
If you don’t want custom logging and other things like this, you can active the login feature from IIS. There are 3 different levels of login that can be activated from ISS for this scenarios:

  • LogAll – logs all requests
  • LogSuccessful – Logs only successful requests (100-399)
  • LogError – Logs only unsuccessful requests (400-999)

The logs can be found under ‘C:\Windows\system32\LogFiles’ folder.
<httpLogging dontLog="false" selectiveLogging="LogError" />

In this blog post we saw 4 different ways of we can log the incoming traffic. Based on the needs and our preference we can go on one solution or another.
If we need a method to logs only the requests for specific actions or controllers, than Action Filter is our best friend. Using him we can control login in a granular way.
If we need to write custom logs for all the requests that are coming to our web application or filter them than a solution that we can use base controller or HttpModule. Personally I prefer the second one – HttpModule, I think that it is more clean and better solution.
If we need to log all the requests that are coming to our web application and it is not important to filter them, than IIS is our friend.

Comments

Popular posts from this blog

ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded

Today blog post will be started with the following error when running DB tests on the CI machine:
threw exception: System.InvalidOperationException: The Entity Framework provider type 'System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer' registered in the application config file for the ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded. Make sure that the assembly-qualified name is used and that the assembly is available to the running application. See http://go.microsoft.com/fwlink/?LinkId=260882 for more information. at System.Data.Entity.Infrastructure.DependencyResolution.ProviderServicesFactory.GetInstance(String providerTypeName, String providerInvariantName) This error happened only on the Continuous Integration machine. On the devs machines, everything has fine. The classic problem – on my machine it’s working. The CI has the following configuration:

TeamCity.NET 4.51EF 6.0.2VS2013
It seems that there …

Entity Framework (EF) TransactionScope vs Database.BeginTransaction

In today blog post we will talk a little about a new feature that is available on EF6+ related to Transactions.
Until now, when we had to use transaction we used ‘TransactionScope’. It works great and I would say that is something that is now in our blood.
using (var scope = new TransactionScope(TransactionScopeOption.Required)) { using (SqlConnection conn = new SqlConnection("...")) { conn.Open(); SqlCommand sqlCommand = new SqlCommand(); sqlCommand.Connection = conn; sqlCommand.CommandText = ... sqlCommand.ExecuteNonQuery(); ... } scope.Complete(); } Starting with EF6.0 we have a new way to work with transactions. The new approach is based on Database.BeginTransaction(), Database.Rollback(), Database.Commit(). Yes, no more TransactionScope.
In the followi…

GET call of REST API that contains '/'-slash character in the value of a parameter

Let’s assume that we have the following scenario: I have a public HTTP endpoint and I need to post some content using GET command. One of the parameters contains special characters like “\” and “/”. If the endpoint is an ApiController than you may have problems if you encode the parameter using the http encoder.
using (var httpClient = new HttpClient()) { httpClient.BaseAddress = baseUrl; Task<HttpResponseMessage> response = httpClient.GetAsync(string.Format("api/foo/{0}", "qwert/qwerqwer"))); response.Wait(); response.Result.EnsureSuccessStatusCode(); } One possible solution would be to encode the query parameter using UrlTokenEncode method of HttpServerUtility class and GetBytes method ofUTF8. In this way you would get the array of bytes of the parameter and encode them as a url token.
The following code show to you how you could write the encode and decode methods.
publ…