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.