Ogg Vorbis
Ogg Vorbis

Reputation: 23

Logging URL in Blazor Server using Nlog

I'm currently using NLog with Blazor Server and storing generated logs in a database. One of the data points I would like to store is the URL that the user was on when the log was created.

In other asp.net core projects, I would use the aspnet-request-url layout renderer, but on Blazor server this always seems to return https://localhost/_blazor. Is there a way to get the current Blazor URL and include that in the log?

Upvotes: 2

Views: 2269

Answers (3)

Rolf Kristensen
Rolf Kristensen

Reputation: 19867

Alternative ways of retrieving Razor-specific details:

  • Getting page name/path - RouteData.Values["page"]

    ${aspnet-request-routeparameters:items=page}
    
  • Route Template parameters defined as part of the @page. Ex. @page "{title?}" or @page "{handler?}"

    ${aspnet-request-routeparameters:items=title}
    
  • Alternative get page name/path from HttpContext - HttpContext.Request.Path

    ${aspnet-request-url:properties=path}
    
  • If you have handler to redirect url - HttpContext.Request.Query["handler"]

    ${aspnet-request-querystring:items=handler}
    
  • Combined lookup of Razor Page Handler Name:

    ${aspnet-request-routeparameters:items=handler:whenEmpty=${aspnet-request-querystring:items=handler}}
    
  • Getting the EndPoint DisplayName - HttpContext.GetEndPoint().DisplayName

    ${aspnet-request-endpoint}
    

See also: https://nlog-project.org/config/?tab=layout-renderers&search=package:nlog.web.aspnetcore

Upvotes: 0

Syed Rafay
Syed Rafay

Reputation: 1500

Right now, it doesn't seem to be possible to use NLog layout renderers such as ${aspnet-request-url} to log the correct request url for Blazor Server applications, but you can still make it work by appending the request URL to the logger yourself by using the ${event-properties} layout renderer (see code below).

The reason is that ${aspnet-request-url} layout renderer searches HttpContext.Request to get the request URL (NLog AspNetRequestUrlRenderer.cs), which works fine for other asp.net core projects, but Blazor handles things differently.

In-case of Blazor Server applications, clients only send an HTTP request to the application just once (to https://localhost/_blazor), in which they download all the necessary files (html / css / js / images) and Blazor establishes a SignalR connection with the client. Once a SignalR connection is established, it uses transport methods other than HTTP (usually WebSockets) for communicating between the client-server and no HTTP request is made after that. All subsequent requests are made through SignalR connection and therefore, you always get https://localhost/_blazor in ${aspnet-request-url} layout renderer.

As per the documentation, the defined way to get current requests URI is to inject NavigationManager in your Blazor components. You can get the Uri property from NavigationManager which gives you an absolute URI in string format. NLog loggers can be enriched with context information and custom properties (using WithProperty or WithProperties method) which can then be logged using Event Properties layout renderer. So what you can do is attach the NavigationManager.Uri to your logger and then access that property by defining it in your layouts in NLog.config.

Solution #01 Example:

/* Counter.razor */
@page "/counter"

@inject NavigationManager navigationManager
    
/* Define Logger */
NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
    
/* Attaching a property to a logger returns a new enriched logger */
var loggerWithProps = _logger.WithProperty("navigation-manager-uri", navigationManager.Uri);

/* Use this logger which has request url attached in key 'navigation-manager-uri' */
loggerWithProps.Info("Sample message which contains NavManager.Uri property");

Now in your NLog.config file, you can access the request uri using the layout renderer ${event-properties:item=navigation-manager-uri}. Your config file will look like this:

<!-- NLog.config -->
<targets>
    <target xsi:type="Console" layout="${event-properties:item=navigation-manager-uri} ${message}">

    <!-- Or skip logging this property if it is empty (uses ${when} condition) -->    
    <!-- <target xsi:type="Console" layout="${when:when='${event-properties:item=navigation-manager-uri}'!='':Inner=${event-properties:item=navigation-manager-uri} }${message}"> -->
    </target>
</targets>

which gives the output logs:

https://localhost:7123/counter Sample message which contains NavManager.Uri property

[Recommended] Solution #02 Example (thanks @Rolf Kristensen)

Instead of enriching a logger by attaching a property to it (which creates 2 loggers, one with your property and one without it) and using ${event-properties} layout renderer, you can simple add NavigationManager.Uri into HttpContext.Items in OnInitialized lifecycle hook. Since NLog has HttpContext Item Layout Renderer which can access HttpContext.Items, you can access it in NLog.config by using ${aspnet-item:variable=YOUR_VARIABLE_NAME}

/* Counter.razor */
@page "/counter"

/* @* Inject NavigationManager to get current URI *@ */
@inject NavigationManager navigationManager

/* @* Inject IHttpContextAccessor to get access to current HttpContext *@ */
@inject IHttpContextAccessor httpContextAccessor

@code {
    /* Define a simple logger */
    NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    protected override void OnInitialized()
    {
        /* Add NavigationManager.Uri into HttpContext Items */
        /* This will then be accessible in NLog.config like ${aspnet-item:variable=navigation-manager-uri} */
        httpContextAccessor?.HttpContext?.Items.Add("navigation-manager-uri", navigationManager.Uri);
        
        /* Or make it fail safe so that it doesn't give 'key' already present error when switching to multiple pages */
        // if (httpContextAccessor is not null && httpContextAccessor.HttpContext is not null && httpContextAccessor.HttpContext.Items.ContainsKey("navigation-manager-uri"))
            // httpContextAccessor?.HttpContext?.Items.Remove("navigation-manager-uri");
        // httpContextAccessor?.HttpContext?.Items.Add("navigation-manager-uri", navigationManager.Uri);

        /* Now logging anything will have correct URL displayed in logs */
        /* Use this logger everywhere in this component */
        logger.Info("This will have correct URL attached from NavigationManager.Uri");
    }
}

Now in your NLog.config file, you can access the request URI which you pushed to HttpContext.Items using the layout renderer ${aspnet-item:variable=navigation-manager-uri}. Your config file will look like this:

<!-- NLog.config -->
<targets>
    <target xsi:type="Console" layout="${aspnet-item:variable=navigation-manager-uri} ${message}">

    <!-- Or skip logging this property if it is empty (uses ${when} condition) -->    
    <!-- <target xsi:type="Console" layout="${when:when='${aspnet-item:variable=navigation-manager-uri}'!='':Inner=${aspnet-item:variable=navigation-manager-uri} }${message}"> -->
    </target>
</targets>

which gives the output logs:

https://localhost:7123/counter This will have correct URL attached from NavigationManager.Uri

This solution is easier because all you need to do is add 3 lines of code to make it work and NLog handles the rest.

  1. Inject NavigationManager
  2. Inject IHttpContextAccessor
  3. Add NavigationManager.Uri into HttpContext.Items

Right now these are the only ways to log the request URLs for Blazor applications with NLog. See NLog Issue - Provide support for logging Blazor request information for more information.

Upvotes: 3

Burak Akgerman
Burak Akgerman

Reputation: 21

The NLog.Web.AspNetCore library uses the HttpContext as the source for almost all of its layout renderers. In a few cases, IHostEnvironment is used. In this case, the property HttpContext.Request.Url is used. Middleware is not utilized.

Perhaps examining {aspnet-request-routeparameters} could be of assistance? I don't know enough about Blazor to know how it populates the HttpContext.

Upvotes: 0

Related Questions