Client-Side Logging for ASP.NET Core with Loupe

Client-side logging is the best way to effectively collect log data for any web application that relies on JavaScript to do the heavy lifting, specifically for single-page applications. That’s why we are happy to introduce client-side logging for ASP.NET Core in the most recent version of the Loupe Agent.

The new release of Loupe.Agent.AspNetCore adds support for client-side (i.e., JavaScript) logging, so logs generated in the browser can be sent to your Loupe server along with the messages from your .NET code. Here, we have some information that should help you get started.

Configuring the Server

To use client-side logging in your ASP.NET Core MVC application, with the Loupe.Agent.AspNetCore package installed, you need to add a few lines to your Startup configuration class.

In the ConfigureServices method, append AddClientLogging() to the AddLoupe() call:

public void ConfigureServices(IServiceCollection services)
{
    services.AddLoupe()
        .AddClientLogging(); // Add client logging services

    services.AddControllersWithViews();
}

In the Configure method, you need to add the Loupe cookie-handling middleware into the app pipeline. This should go in before Authentication and Routing, but after Static Files if present.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...
    
    app.UseStaticFiles();

    app.UseLoupeCookies(); // Add Loupe cookie-handling

    app.UseRouting();

    app.UseAuthorization();
    
    // ...
}

Finally, you need to configure the endpoint where the Loupe client will send messages. The configuration process for .NET 5 and .NET Core 3.1 is the same, but .NET Core 2.1 requires a different setup. Here, we will go over both.

Add Endpoint in .NET Core 3.1 and .NET 5

In .NET 5 and .NET Core 3.1, this is handled in the UseEndpoints callback with MapLoupeClientLogger. You can specify a custom path for the logger or leave the default, which is /loupe/log.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
        
        endpoints.MapLoupeClientLogger(); // Add the /loupe/log endpoint
    });
}

MapLoupeClientLogger returns an IEndpointConventionBuilder instance, which you can use to customize authentication, CORS and other concerns for the logging endpoint.

Add Endpoint in .NET Core 2.1

In .NET Core 2.1, you will need to add the Loupe client logger to the IApplicationBuilder instance. Again, you can optionally specify a custom path or use the default /loupe/log.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...

    app.UseLoupeClientLogger();
}

Client-side Use

Loupe provides two NPM packages for JavaScript and TypeScript clients: one for general purpose use and one specifically for Angular applications.

Using the General Purpose Client

The Loupe Agent for JavaScript & TypeScript is distributed on NPM as @gibraltarsoftware/loupe-typescript. Once installed in your project, you can import the Agent and use it for logging in your client app.

import { LoupeAgent } from '@gibraltarsoftware/loupe-typescript/dist/loupe.agent';

const loupe = new LoupeAgent(window, document);

loupe.information('App', 'Loaded', 'Application loaded');

Using the Angular Client

We developed a dedicated NPM package for Angular applications, @gibraltarsoftware/loupe-angular, that provides an injectable Angular Service and can integrate with Angular sub-systems. For example, to log a message every time a Router navigation is triggered, you can subscribe to the router.events observable, as shown below.

import { Component } from '@angular/core';
import { Router, NavigationStart, RouterEvent } from '@angular/router';
import { LoupeService } from '@gibraltarsoftware/loupe-angular';
import { filter } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  title = 'app';

  constructor(
    private router: Router,
    private loupe: LoupeService
  ) {
    this.router.events.pipe(
      filter(x => x instanceof NavigationStart)
    ).subscribe((evnt: RouterEvent) => {
      this.loupe.information(
        "Angular", "NavigationStart", evnt.url,
        null, null, JSON.stringify(evnt), null
      );
    });
  }
}

What This Means for You

Loupe treats client-side data the same way it treats all other data. You can find these logs through searches, set up notifications, look at the stack trace, use the same error triage process, and more.

Loupe Server Stack trace of JavaScript error An Example stack trace in Loupe Server for a JavaScript error

We are excited to add client-side for ASP.NET Core agent because developers can leverage the same Loupe features they have been using with .NET code for client-side JavaScript as well. Instead of disappearing in the browser, you can now save these logs, thanks to Loupe. We look forward to folks using this new feature and finding new ways to improve their applications.

Download the Agent

Rock solid centralized logging

Unlimited applications, unlimited errors, scalable from solo startup to enterprise.