Click here to Skip to main content
15,891,033 members
Articles / Web Development / ASP.NET

Configuring your JavaScript loggers with JSNLog

Rate me:
Please Sign up or sign in to vote.
4.93/5 (15 votes)
4 Apr 2014CPOL15 min read 46.7K   31   7
Shows how to configure your JavaScript loggers with JSNLog, a JavaScript logging library that integrates well with your .Net site

Shows how to configure your JavaScript loggers with JSNLog, a JavaScript logging library that is well integrated with .Net. It lets you configure loggers in your web.config. And it lets you receive log messages from the client and store them on the server, without any coding.

Contents of this series

Configuring your loggers in web.config

If you have an ASP.NET MVC or WebForms web site, you can configure your client side loggers from your server side web.config.

Note that after installation, JSNLog doesn't need any additional configuration. All you need to do to start logging is add loggers to your JavaScript code<text>. You only need to update your web.config if you want to change the default configuration.

During installation, a <jsnlog> Element will have been added to your web.config. This is the container for the other elements.

<jsnlog> Element

The root element for all JSNLog configuration elements.

Definition

<configuration> 
    <jsnlog
        enabled="true|false"
        maxMessages="number"
        serverSideLogger="string"
        serverSideLevel="number|TRACE|DEBUG|INFO|WARN|ERROR|FATAL" 
        serverSideMessageFormat="string"
        productionLibraryPath="string" >
    </jsnlog>
</configuration> 

Remarks

The <jsnlog> element can have the following attributes:

Attribute Default Description
enabled
optional
true If false, all loggers are disabled.
maxMessages
optional
no maximum Limits total number of messages sent to the server. See remarks below.
serverSideLogger
optional
(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.

serverSideLevel
optional
(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.

serverSideMessageFormat
optional
%message See remarks.
productionLibraryPath
optional
(empty) Path to the jsnlog.js file. JSNLog generates a <script> tag with this url.

If you do not specify this, JSNLog will not generate the <script> tag. This makes sense if you for example combine jsnlog.js with your other JavaScript files and you use your own <script> tag to load this combined file.

maxMessages and buffering

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 number of messages sent. When the counter gets to zero or below, no more messages will be sent.

However, this is affected by batching and buffering.

Take a situation where maxMessages is set to 5 and 2 messages have already been sent - so the message counter is now 3. If 8 messages had been stored in a buffer and those messages are now sent, they will be all sent. That means the server will receive a total of 2 + 8 = 10 messages. After this, no more messages will be sent, because the number of messages sent (10) exceeds maxMessages (5).

This means that maxMessages is not a precise limit on the number of messages sent to the server. On the other hand, buffered messages are sent together in a single request to the server, minimizing bandwidth. And buffered messages are often useful in solving exceptions, so there is value in receiving them.

serverSideMessageFormat

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
%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 of the sender
%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. This request id will be automatically generated for you.
%newline Newline character

Examples

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, Brower: %userAgent - %message" >

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

<jsnlog serverSideLogger="jslogger">

This disables all client side loggers.

<jsnlog enabled="false">

<logger> Element

Configures a logger.

Definition

<configuration> 
    <jsnlog>
        <logger 
            name="string" 
            level="number|TRACE|DEBUG|INFO|WARN|ERROR|FATAL|OFF|ALL"
            userAgentRegex="regular expression"
            ipRegex="regular expression"
            disallow="regular expression" 
            appenders="Appender Name(s)" >
            <onceOnly regex="regular expression" />
        </logger> 
    </jsnlog>
</configuration> 

Remarks

The <logger> element can have the following attributes:

Attribute Default Description
name
optional
(empty) Name of the logger you want to configure. To configure the root logger, simply omit the name attribute.

In your server side logs, the root logger is called ClientRoot.

level
optional
(inherited from parent logger) Only log messages with a severity equal or higher than this can be sent to the server.
userAgentRegex
optional
(inherited from parent logger) If not empty, log messages only get processed if this regular expression matches the user agent string of the browser.
ipRegex
optional
(inherited from parent logger) If not empty, log messages only get processed if this regular expression matches the IP address of the browser.
disallow
optional
(inherited from parent logger) If not empty, log messages are suppressed if they match this regular expression. If an object is being logged, it is converted to a JSON string, which is then matched.
appenders
optional
(inherited from parent logger) One or more appenders for the logger to send its log messages to. See the examples.
onceOnly
optional
(inherited from parent logger) One or more regular expressions. When a message matches a regular expression, then any subsequent messages matching that same regular expression will be suppressed. See the remarks and examples.

Logger names and option inheritance

Loggers not only get their options through the <logger> element, but also through inheritance. This is based on the name of each logger.

Assume that you have a method "method1" in a namespace "namespace1". Then it would make sense to use a naming scheme for you loggers like this: "namespace1.method1.logger1", "namespace1.method1.logger2", etc. This way, there are no name clashes and it makes keeping track of your loggers easy.

Just as a namespace may contain methods, and a method may contain loggers, so you can think of these logger names as making up a hierarchy:

  • The parent of "namespace1.method1.logger1" is "namespace1.method1";
  • The parent of "namespace1.method1" is "namespace1";
  • The parent of "namespace1" is the root logger (the logger without a name).

You're not limited to just 3 levels, you can have as many as want.

If you don't set an option using the <logger> element<text>, the logger inherits that option from its parent. If you do not use <logger> element at all, every logger will have the same options as the root logger.

Root logger and default appender

When the library loads, it creates the root logger. It also creates a default appender for use by the root logger.

Because every logger inherits from the root logger (unless you override this with the <logger> element), you can start logging right away without having to create an appender.

The root logger is created with these options:

Option Default Value
level DEBUG
userAgentRegex (empty)
ipRegex (empty)
disallow (empty)
appenders (default appender)

Note that because the default level for root logger is DEBUG, by default only log messages with severity DEBUG or higher get processed.

You can change the options used with the root logger in the same way as any other logger, using the <logger> element. See the examples below.

The default appender is created with these options (description of options):

Option Default Value
level TRACE
userAgentRegex (empty)
ipRegex (empty)
disallow (empty)
storeInBufferLevel ALL
sendWithBufferLevel OFF
bufferSize 0
batchSize 1
url jsnlog.logger

Suppressing duplicate messages with onceOnly

You may have loggers inside code that gets called multiple times. As a result, you may get a series of messages that are essentially the same. Using onceOnly, you can suppress the duplicate messages, so only the first message is sent to the server.

This works by setting one or more regular expressions. When a log message matches one of the regular expressions, the logger remembers that there has been a message that matched that regular expression. Then when another message arrives that matches that same regular expression, it is suppressed.

For example, if you receive these messages:

Parameter x too high - x = 5
Parameter x too high - x = 6
Parameter x too high - x = 7
...
Parameter x too high - x = 49
Parameter x too high - x = 50

Then you can use the regular expression:

Parameter x too high - x =

To only receive the very first message:

Parameter x too high - x = 5

See the examples for how to set the regular expression.

You can set multiple regular expressions. These work independently. So if a message matches the first regular expression, then if a second message matches the second regular expression but not the first, then the second message gets through because it is not a duplicate of the first message.

You can log not only strings but also objects. If you log an object, the object is translated to a JSON string. That string is than matched against the regular expressions.

Similar to other attributes, loggers inherit onceOnly from their parents. However, this is all or nothing. If you set onceOnly regular expressions for a logger, than any onceOnly regular expressions that its parent may have had are disregarded.

Examples

This sets the level of logger "a.b" to 3000.

<logger name="a.b" level="3000" />

This sets the level of the root logger.

<logger level="3000" />

This sets the level of logger "a.b" to INFO (which is the same as setting it to 3000).

<logger name="a.b" level="INFO" />

This sets the level to 4000. It also disables the logger for all browsers, except those whose user agent string contains MSIE 7|MSIE 8 (that is, it is version 7 or 8 of Internet Explorer).

<logger name="a.b" userAgentRegex="MSIE 7|MSIE 8" level="4000" />

This creates an appender "appender" and then tells the logger "a.b" to send all log messages to it.

<ajaxAppender name="appender" />
<logger name="a.b" appenders="appender" />

This creates two appenders and then tells the logger "a.b" to send all log messages to them.

<ajaxAppender name="appender1" />
<ajaxAppender name="appender2" />
<logger name="a.b" appenders="appender1;appender2" />

Suppress duplicate messages that match the regular expression "Parameter x too high - x =".

<logger name="a">
    <onceOnly regex="Parameter x too high - x =" />
</logger>

Suppress duplicate messages that match the regular expression "Parameter x too high - x =". Also suppress duplicate messages that match "x = \d+ and y = \d+".

<logger name="a">
    <onceOnly regex="Parameter x too high - x =" />
    <onceOnly regex="x = \d+ and y = \d+" />
</logger>

Loggers inherit onceOnly from their parents. Assume you have a logger "a.b" whose parent "a" suppresses duplicates, but you want logger "a.b" to not suppress duplicates. To make that happen, give it a onceOnly collection without any regular expressions:

<logger name="a.b">
    <onceOnly  />
</logger>

<ajaxAppender> Element

Configures an Ajax Appender.

Definition

<configuration> 
    <jsnlog>
        <ajaxAppender 
            name="string" 
            level="number|TRACE|DEBUG|INFO|WARN|ERROR|FATAL|OFF|ALL"
            userAgentRegex="regular expression"
            ipRegex="regular expression"
            disallow="regular expression"
            storeInBufferLevel=
                "number|TRACE|DEBUG|INFO|WARN|ERROR|FATAL|OFF|ALL"
            sendWithBufferLevel=
                "number|TRACE|DEBUG|INFO|WARN|ERROR|FATAL|OFF|ALL" 
            bufferSize="number" 
            batchSize="number" 
            url="string" />
     </jsnlog>
</configuration> 

Remarks

Loggers do not process log messages themselves. Instead, they pass them on to an appender. That way, the attributes that have to do with for example sending log messages to the server are centralized, making them more easy to manage.

JSNLog creates a default appender, so there is no need to create one yourself in order to start logging. Details about this default appender, and how to associate loggers with appenders, are here<text>.

The <ajaxAppender> element can have the following attributes:

Attribute Default Description
name
required
Name of the AjaxAppender you want to configure.
level
optional
TRACE Only log messages with a severity equal or higher than this can be sent to the server.
userAgentRegex
optional
(empty) If not empty, log messages only get processed if this regular expression matches the user agent string of the browser.
ipRegex
optional
(empty) If not empty, log messages only get processed if this regular expression matches the IP address of the browser.
disallow
optional
(empty) If not empty, log messages are suppressed if they match this regular expression. If an object is being logged, it is converted to a JSON string, which is then matched.
storeInBufferLevel
optional
ALL If the severity of the log message is equal or greater than this, but smaller than level, the log message will not be sent to the server, but stored in an internal buffer.

If bufferSize is 0 or less, the log message is simply ignored.

sendWithBufferLevel
optional
OFF If the severity of a log message is equal or greater than this, not only the log message but also all log messages stored in the internal buffer will be sent to the server.

This allows you to store low priority trace messages in the internal buffer, and only send them when a high priority fatal message is sent.

bufferSize
optional
0 Sets the size of the buffer used with sendWithBufferLevel and storeInBufferLevel.
batchSize
optional
1 Allows you to improve performance by sending multiple log messages in one go, rather than one by one.
url
optional
jsnlog.logger All log messages will be sent to this URL. The default url is as expected by the server side code of JSNLog.

Logger level and appender level

Notice that both loggers and appenders have a level. This means that a log message must have a severity that is equal or higher than both these levels in order to be processed.

Examples

This creates an appender with the behaviour below and than attaches it to the root logger:

  • It has an internal buffer that stores at most 20 log messages;
  • Log messages with severity smaller than TRACE are ignored.
  • Log messages with severity equal or greater than TRACE and lower than WARN are stored in the internal buffer, but not sent to the server;
  • Log messages with severity equal or greater than WARN and lower than FATAL are sent to the server on their own;
  • Log messages with severity equal or greater than FATAL are sent to the server, along with all messages stored in the internal buffer.
<ajaxAppender 
    name="appender1" 
    storeInBufferLevel="TRACE" 
    level="WARN" 
    sendWithBufferLevel="FATAL" 
    bufferSize="20"/>
<logger appenders="appender1"/>

<consoleAppender> Element

Configures a Console Appender.

Definition

<configuration> 
    <jsnlog>
        <consoleAppender 
            name="string" 
            level="number|TRACE|DEBUG|INFO|WARN|ERROR|FATAL|OFF|ALL"
            userAgentRegex="regular expression"
            ipRegex="regular expression"
            disallow="regular expression"
            storeInBufferLevel=
                "number|TRACE|DEBUG|INFO|WARN|ERROR|FATAL|OFF|ALL"
            sendWithBufferLevel=
                "number|TRACE|DEBUG|INFO|WARN|ERROR|FATAL|OFF|ALL" 
            bufferSize="number" 
            batchSize="number" 
        />
    </jsnlog>
</configuration> 

Remarks

JSNLog is based on the idea of integrating client side and server side logging - using the <ajaxAppender> Element to send log items to the server.

However, during development it can be useful to simply send log items to the console provided by the F12 developer tools in Chrome, Firefox and Internet Explorer.

That way, the log items appear immediately below your main browser window, so you don't have to check the server side log.

Sending log items from a logger to the console

To see the log items that are generated by a logger in the console, you first create a ConsoleAppender and then use the <logger> Element of the logger to send all log items to that ConsoleAppender:

<!-- "mylogger" logs to just the console -->
<consoleAppender name="consoleAppender" />
<logger name="mylogger" appenders="consoleAppender" />

However, this means that all log items will only go to the console, and no longer to the server. If you want the log items to go to both the console and the server, send them to an AjaxAppender as well as to a ConsoleAppender:

<!-- "mylogger" logs to both the server and the console -->
<consoleAppender name="consoleAppender" />
<ajaxAppender name="ajaxAppender" />
<logger name="mylogger" appenders="ajaxAppender;consoleAppender" />

Sending log items from every logger to both the console and the server

It may be easiest to get all loggers to log to both the console and the server. You can achieve this by using the <logger> Element of the root logger. Through inheritance, every other logger will start logging to both the console and the server as well.

<!-- Debugging: all loggers log to both the server and the console -->
<consoleAppender name="consoleAppender" />
<ajaxAppender name="ajaxAppender" />
<logger appenders="ajaxAppender;consoleAppender" />

Switching off logging to the console

Once in production, you may want to stop logging to the console. You can achieve this by removing the ConsoleAppender:

<!-- Production: loggers log to the server only -->
<ajaxAppender name="ajaxAppender" />
<logger appenders="ajaxAppender" />

This means you don't have to go through your code to remove logging code - a simple change in the web.config is enough. Keep in mind that if you associate individual loggers with the ConsoleAppender, you will have to remove that as well in order to stop all logging to the console.

Attributes

The <consoleAppender> element can have the following attributes:

Attribute Default Description
name
required
Name of the ConsoleAppender you want to configure.
level
optional
TRACE Only log messages with a severity equal or higher than this can be sent to the server.
userAgentRegex
optional
(empty) If not empty, log messages only get processed if this regular expression matches the user agent string of the browser.
ipRegex
optional
(empty) If not empty, log messages only get processed if this regular expression matches the IP address of the browser.
disallow
optional
(empty) If not empty, log messages are suppressed if they match this regular expression. If an object is being logged, it is converted to a JSON string, which is then matched.
storeInBufferLevel
optional
ALL If the severity of the log message is equal or greater than this, but smaller than level, the log message will not be sent to the server, but stored in an internal buffer.

If bufferSize is 0 or less, the log message is simply ignored.

sendWithBufferLevel
optional
OFF If the severity of a log message is equal or greater than this, not only the log message but also all log messages stored in the internal buffer will be sent to the server.

This allows you to store low priority trace messages in the internal buffer, and only send them when a high priority fatal message is sent.

bufferSize
optional
0 Sets the size of the buffer used with sendWithBufferLevel and storeInBufferLevel.
batchSize
optional
1 Allows you to improve performance by sending multiple log messages in one go, rather than one by one.

Logger level and appender level

Notice that both loggers and appenders have a level. This means that a log message must have a severity that is equal or higher than both these levels in order to be processed.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Australia Australia
Twitter: @MattPerdeck
LinkedIn: au.linkedin.com/in/mattperdeck
Current project: JSNLog JavaScript Logging Package

Matt has over 9 years .NET and SQL Server development experience. Before getting into .Net, he worked on a number of systems, ranging from the largest ATM network in The Netherlands to embedded software in advanced Wide Area Networks and the largest ticketing web site in Australia. He has lived and worked in Australia, The Netherlands, Slovakia and Thailand.

He is the author of the book ASP.NET Performance Secrets (www.amazon.com/ASP-NET-Site-Performance-Secrets-Perdeck/dp/1849690685) in which he shows in clear and practical terms how to quickly find the biggest bottlenecks holding back the performance of your web site, and how to then remove those bottlenecks. The book deals with all environments affecting a web site - the web server, the database server and the browser.

Matt currently lives in Sydney, Australia. He recently worked at Readify and the global professional services company PwC. He now works at SP Health, a global provider of weight loss web sites such at CSIRO's TotalWellBeingDiet.com and BiggestLoserClub.com.

Comments and Discussions

 
QuestionHow can i insert the session ID in logs? , and is there a way to format client side time? Pin
amit454019-Feb-16 3:26
amit454019-Feb-16 3:26 
AnswerRe: How can i insert the session ID in logs? , and is there a way to format client side time? Pin
Matt Perdeck20-Feb-16 16:06
Matt Perdeck20-Feb-16 16:06 
1) I'm not sure what you mean with session IDs. You can log request ids, as shown :
here. Otherwise, you can create a handler for the OnLogging event, which lets you insert any data you want.

2) To include the client side log time in a specific format, OnLogging event is probably your best bet as well. This gives you access to all logging information, and lets you add any text you want to the final log message.
GeneralRe: How can i insert the session ID in logs? , and is there a way to format client side time? Pin
amit45403-Mar-16 20:22
amit45403-Mar-16 20:22 
SuggestionLog 4 Net Configuration Pin
Ewert Bergh15-Sep-15 0:15
Ewert Bergh15-Sep-15 0:15 
GeneralMy vote of 5 Pin
Volynsky Alex5-Apr-14 5:09
professionalVolynsky Alex5-Apr-14 5:09 
GeneralMy vote of 5 Pin
Yashfi13-Jan-13 19:24
Yashfi13-Jan-13 19:24 
GeneralMy vote of 5 Pin
WebMaster13-Jan-13 17:22
WebMaster13-Jan-13 17:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.