JSNLog
Top level configuration settings. Also the root for all other JSNLog configuration elements.
Definition
<configuration> <jsnlog enabled="true|false" maxMessages="number" defaultAjaxUrl="string" corsAllowedOriginsRegex="string" corsAllowedHeaders="string" serverSideLogger="string" serverSideLevel="number|TRACE|DEBUG|INFO|WARN|ERROR|FATAL" serverSideMessageFormat="string" dateFormat="string" productionLibraryPath="string" > </jsnlog> </configuration>
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 corsAllowedHeaders { 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; } }
Remarks
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 |
---|---|---|
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. |
defaultAjaxUrl optional |
(empty) | See Setting the url to send logs to. |
Server side attributes
Attribute | Default | Description |
---|---|---|
corsAllowedOriginsRegex optional |
(empty) | Matches domains from which log requests can be sent to different domains using CORS (details). |
corsAllowedHeaders optional |
(empty) | Comma delimited list of custom headers allowed when sending log requests to different domains using CORS (details). |
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. |
dateFormat optional |
o | Custom or standard date format string that determines how dates are formatted in the log. The default adheres to the ISO 8601 standard. |
productionLibraryPath optional |
(empty) | If this is given, the UseJSNLog method and jl-javascript-logger-definitions tag helper generate a <script> tag with this url (alternatives). |
insertJsnlogInHtmlResponses optional |
false | If true, the UseJSNLog method automatically inserts JSNLog into all HTML responses. |
maxMessages
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:
- You log through multiple appenders.
- You buffer trace messages with the appender option storeInBufferLevel.
-
You use the
batching feature
.
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.
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. |
%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
%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 }"
Note:
- 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.
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, Browser: %userAgent - %message"> </jsnlog>
// 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"> </jsnlog>
// Use in Configure method in Startup class var jsnlogConfiguration = new JsnlogConfiguration { serverSideLogger="jslogger" };
This disables all JavaScript loggers.
<jsnlog enabled="false"> </jsnlog>
// Use in Configure method in Startup class var jsnlogConfiguration = new JsnlogConfiguration { enabled=false };