Documentation Menu


Top level configuration settings. Also the root for all other JSNLog configuration elements.


        productionLibraryPath="string" >
public class JsnlogConfiguration
    public bool enabled { get; set; }
    public uint maxMessages { get; set; }
    public string defaultAjaxUrl { get; set; }
    public string corsAllowedOriginsRegex { get; set; }
    public string serverSideLogger { get; set; }
    public string serverSideLevel { get; set; }
    public string serverSideMessageFormat { get; set; }
    public string dateFormat { get; set; }
    public string productionLibraryPath { get; set; }
    public bool insertJsnlogInHtmlResponses { get; set; }
    public List<Logger> loggers { get; set; }
    public List<AjaxAppender> ajaxAppenders { get; set; }
    public List<ConsoleAppender> consoleAppenders { get; set; }


This element has two sets of attributes:

  • Those that apply to JSNLog's client side library jsnlog.js, dealing with loggers and appenders;
  • Those that apply to JSNLog's server side component, dealing with CORS and processing of log messages.

Client side attributes

Attribute Default Description
true If false, all loggers are disabled.
no maximum Limits total number of messages sent to the server. See remarks below.
see remarks Default url used by ajax appenders when no url is set. See remarks below.

Server side attributes

Attribute Default Description
see remarks CORS related. Matches allowed domains. See remarks below.
(empty) By default, all log messages from your JavaScript code are passed on to your server side logging package with the name of the JavaScript logger.

However, if you want to use a specific logger name for all your JavaScript log messages, you can specify that with this attribute.

(empty) By default, all log messages from your JavaScript code are passed on to your server side logging package with the same level as the original JavaScript log message.

However, if you want to use a specific level, you can specify that with this attribute.

%message See remarks.
o Custom or standard date format string that determines how dates are formatted in the log. The default adheres to the ISO 8601 standard.
(empty) If this is given, the UseJSNLog method and jl-javascript-logger-definitions tag helper generate a <script> tag with this url (alternatives).
false If true, the UseJSNLog method automatically inserts JSNLog into all HTML responses.


Provides the default url if no url is set on the AjaxAppender. Also sets the url for the default appender.

If defaultAjaxUrl is not set, the url /jsnlog.logger is used.

However, if your site sits in an application below the root application, then it will have a url like rather than JSNLog caters for this situation by adding the application path to the default url, resulting in a default url like /site/jsnlog.logger.


When setting defaultAjaxUrl or the url attribute on an AjaxAppender, you can use a url with a domain different from the domain you use for your site. For example, your site is on, but you want to send log entries to or

To make this happen, set corsAllowedOriginsRegex to a regular expression that matches all domains that JSNLog should accept log requests from. Otherwise your log requests will be ignored (examples).

This only applies to cross domain logging. You don't have to set corsAllowedOriginsRegex if you send log entries to the same domain as where your site is located (the most common case).


You use maxMessages to limit the number of messages sent to the server, per page load.

When the page is loaded by the client, a counter is set to maxMessages. Each time messages are sent to the server, that counter is decremented by the appender by the number of log messages sent. When the counter gets to zero or below, no more messages will be sent.

This can lead to unexpected behaviour if:

Logging through multiple appenders

As you saw, the counter is decreased by the appender. This means that if you associate a logger with 2 appenders, each time that logger sends a message, the counter is decreased twice (once by each appender).

The counter is decreased by the appender and not by the logger, because:

  • If you have multiple appenders sending messages to the same server, decreasing the counter per appender more accurately limits the load to your server.
  • Only the appender knows whether a message will actually be sent, or if it gets stored in a buffer due to its storeInBufferLevel option.

Buffering trace messages

Take a situation where maxMessages was set to 5 and 2 messages have already been sent - so the counter is now 3. Also, 8 trace messages are stored in the buffer.

If a high severity message is logged causing the trace messages to be sent as well, 1 + 8 = 9 messages will be sent. So the server has now received 2 + 9 = 11 messages. After that, no more messages will be sent, because the number of messages sent (11) exceeds maxMessages (5).

Trace messages are allowed to go over the maxMessages limit, because:

  • This way you don't miss out on trace messages that may help you solve an exception in your JavaScript.
  • The number of excess messages is limited by the size of the trace messages buffer, as set in the appender option bufferSize.
  • The trace messages are sent in a single log request to the server, minimizing bandwidth.

Using the batching feature

Lets assume that:

  • You set the appender option batchSize to 5, so each log request to the server will have up to 5 log messages.
  • You are not using a batch timeout (appender option batchTimeout).
  • There are 2 messages in the batch buffer, so only after 3 more have been added will the entire batch be sent.
  • However, only 1 more message can be accepted before maxMessages is reached.

To make sure that the 2 messages already in the batch buffer are not stranded, next time a log message is generated, the appender will see that this is the very last message that will be sent, and send a batch with the last 2+1=3 log messages.

However, if you use multiple appenders, the appender dealing with the very last message won't tell the other appenders to flush their batch buffers, creating the potential of stranded messages. This is one of the perils of using maxMessages in conjunction with multiple appenders.

maxMessages relates to log messages, not to log requests

If you use log message batching, keep in mind that maxMessages relates to the individual log messages going to the server, not to the number of log requests to the server.


Your server not only receives the the original message passed into the JavaScript logger, but also the logger, the level, a timestamp, etc.

You can choose to have this additional information logged on the server along with the original message, by setting serverSideMessageFormat to a format string with one or more place holders:

Place holder Is replaced by
%message Original message given to the JavaScript logger.
%jsonmessage Original message given to the JavaScript logger, as a valid JSON value. See below.
%utcDate Date and time in UTC when the message was generated, according to the client's clock.
%utcDateServer Date and time in UTC when the message was received by the server, according to the server's clock.
%date Date and time when the message was generated, according to the client's clock. This equals %utcDate converted to the server's local time.
%dateServer Date and time in the server's local time when the message was received by the server, according to the server's clock.
%level Level of the message, as provided by the JavaScript code.
%userAgent Identifies the make of the browser.
%userHostAddress IP address(es) of the sender of the request (details below).
%url Url of the page on which the message was generated.
%logger JavaScript logger that generated the message.
%requestId Identifies the request for which the log message was created.

If you use server side configuration this request id will be generated for you. Otherwise, set this request id in your JavaScript using the JL.setOptions method.

%entryId Number that uniquely identifies the event within the request.
%newline* Newline character

* If you use Elmah, %newline won't result in a newline in the elmah.axd page, because Elmah doesn't convert line breaks to HTML <br /> tags.

An alternative way to add information to your log messages is writing your own logging event handler. This gives you access to the cookies, request headers and query parameters of the incoming log request. It also allows you stop a message from being logged based on its content.

%message and %jsonmessage

Most people store log messages as unstructured strings of text. If that is you, you can skip this section. However, if you store log messages as valid JSON objects, read on.

Both %message and %jsonmessage are replaced by a string that represents the message that was logged. The difference is that whereas %message is replaced by the log message as-is, %jsonmessage is replaced by a string that can be used in a JSON message.

To clarify the issue, see the table below:

JavaScript %message %jsonmessage
JL().error("O'Connell"); O'Connell "O\u0027Connell"
JL().error({"f1": "v1", "f2": "v2"}); {"f1": "v1", "f2": "v2"} {"f1": "v1", "f2": "v2"}

As you see, there is no difference when logging an object (in the second row). But when logging a simple string, %message is replaced by the string, while for %jsonmessage the string is escaped and quoted.

This is important if you want to write valid JSON strings to your log. Suppose you attempted to do that using:

serverSideMessageFormat="{ 'Message': %message }"

That would work well if you only logged objects. But if you logged the string O'Connell, that would result in:

{ 'Message': O'Connell }

Which is not a valid JSON string. However, if you'd use:

serverSideMessageFormat="{ 'Message': %jsonmessage }"

The result would be a valid JSON string:

{ 'Message': "O\u0027Connell" }

serverSideMessageFormat and structured logging

If you create structured logs using Serilog, make sure that your format string results in a valid JSON string. Otherwise it will be logged as a string rather than an object.

Take for example:

serverSideMessageFormat="Sent: %date, Browser: %userAgent, Message: %message"

This will result in a string such as this in your log:

"{ 'Sent': 2014-08-19 21:02:42,603, 'Browser': 'Mozilla/5.0 ...', 'Message': {\"x\":5,\"y\":88} }"

To get a structured object instead, make sure that the result of your format string is a valid JSON string. For example:

serverSideMessageFormat="{ 'Sent': '%date', 'Browser': '%userAgent', 'Message': %jsonmessage }"


  • The curly braces;
  • Field names have been quoted.
  • %date and %userAgent have been quoted as well, to make them strings.
  • %message has been replaced by %jsonmessage, which has not been quoted. That way, when you log objects, they will be logged as objects rather than strings.

This will result in an object such as this in your log:

{ "Sent" : "2014-08-19 21:02:42,603", "Browser" : "Mozilla/5.0 ...", "Message" : { "x" : 5, "y" : 88 } }

Finally, the default value of serverSideMessageFormat gives you structured logging out of the box, even though the default is %message. The reason is that the Serilog adapter that is used by JSNLog to log to Serilog regards all strings that are not valid JSON strings as simple strings that are to be logged as-is.

Working out the IP address of the sender of a request

Working out the IP address of original sender of an HTTP request (that is, the user's browser) is not as simple as looking at the source address in the request.

If your web server sits behind a load balancer, the source of the final request to the web server is not the browser, but the load balancer. The request may also have been passed on through intermediate proxies, causing the same issue.

The most common (but non-standard) solution to this is that proxies and load balancers (such as AWS' Elastic Load Balancer) send an X-Forwarded-For request header with the IP addresses of the browser and any proxies that the request passed through, except for the final source address. JSNLog uses this request header to work out the IP address of the actual browser and the proxies and/or load balancer that the request passed through.

JSNLog reports the IP address(es) of the sender(s) of the request as a string of text containing a comma separated list. First is the browser itself, then intermediate proxies (in the order in which they were reached) and then the load balancer if there is one:

Browser IP address, Proxy 1, ..., Proxy N [, Load Balancer]


This shows the date and time that the log message was created on the client as well as the user agent in each log message.

<jsnlog serverSideMessageFormat="Sent: %date, Browser: %userAgent - %message">
// Use in Configure method in Startup class
        var jsnlogConfiguration = 
  new JsnlogConfiguration {
      serverSideMessageFormat="Sent: %date, Browser: %userAgent - %message"

This processes all client side JavaScript log messages via the server side logger "jslogger".

<jsnlog serverSideLogger="jslogger">
// Use in Configure method in Startup class
        var jsnlogConfiguration = 
  new JsnlogConfiguration {

This disables all JavaScript loggers.

<jsnlog enabled="false">
// Use in Configure method in Startup class
        var jsnlogConfiguration = 
  new JsnlogConfiguration {