Production Logging
Use Production Logging to provide users with a dialog to report issues and submit logs to technical support.
Production Logging collates JavaScript log messages, JavaScript errors, and StreamLink log messages. Log data is persisted in local web storage and included automatically with issue reports.
Use the 'Report an Issue' dialog in your application, or use the Production Logging APIs to write your own solution.
Reporting an issue
To report an issue, users click the Report an Issue command in the main menu. They are presented with a dialog in which to write a description of the issue. StreamLink and JavaScript logs are automatically included in the report submission.
To report an issue:
-
Click Main Menu > Report an Issue.
-
Enter a description of the issue.
-
Click Report Issue.
Your issue will be submitted to technical support and an issue ID will be returned to you. Please quote the issue ID in all further correspondence.
Getting started with Production Logging
Production Logging is enabled by default and integrates with a demonstration backend servlet, DemoLoggingServlet
. The servlet saves each reported issue to a file and returns a random issue ID. This servlet is provided for demonstration purposes only and is not supported for use in production environments.
To use Production Logging in a live environment, you must write a backend service that integrates with your own issue tracking workflow. See Processing logs on the server.
If you want to deploy FX Professional without first writing a backend service to store reported issues, you must disable Production Logging.
To disable Production Logging:
-
Set the LOGGING > ENABLED configuration property to false.
-
Set the LOGGING > SHOW_REPORT_ISSUE_MENU_ITEM configuration property to false.
For more information on setting configuration properties, see Configuration.
Writing custom log messages
Production Logging automatically logs JavaScript errors and StreamLink log messages. To log your own messages, use the Fell module. Use Fell in preference to caplin/core/log/Log
, which is now deprecated. Messages logged to caplin/core/log/Log
are automatically copied to Fell.
Introducing Fell
Fell is a lightweight logging module that supports:
-
namespacing of log messages
-
multiple log stores (logging destinations)
Namespacing of messages provides great flexibility. Fell can be configured to log at different levels for different namespaces, and log stores can be programmed to filter messages by namespace.
The namespace caplin is reserved for internal use by Production Logging. Do not write messages to the caplin namespace or its derivatives.
|
Production Logging’s use of Fell
FX Professional registers two log stores with Fell: one for JavaScript and one for StreamLink. The StreamLink log store processes messages in the caplin.sljs
namespace; the JavaScript log store processes messages in all namespaces other than caplin.sljs
.
Each log store stores messages in its own ring-buffer. By default, each ring buffer stores 1000 lines (messages) before overwriting older lines. To change the capacity of the ring buffers, edit the configuration options LOGGING > STREAMLINK_STORAGE > MAX_LINES and LOGGING > JS_STORAGE > MAX_LINES.
The ring buffers are periodically persisted to local web storage. By default, the ring buffers are persisted every 10 seconds. To change the interval between writes to local web storage, edit the configuration options LOGGING > STREAMLINK_STORAGE > WRITE_INTERVAL and LOGGING > JS_STORAGE > WRITE_INTERVAL.
The capacity of local web storage varies between browser vendors. Microsoft Internet Explorer accepts up to 10MB of data per domain; Google Chrome and Mozilla Firefox accept up to 5MB per domain. By default, the StreamLink and JavaScript log stores are configured to persist a maximum of 2MB of ring buffer data each. Serialised ring-buffer data is trimmed, line by line, until it fits within the 2MB limit. To change the maximum size of persisted data, edit the configuration options LOGGING > STREAMLINK_STORAGE > MAX_SIZE and LOGGING > JS_STORAGE > MAX_SIZE.
For more information on configuring Production Logging, see Configuration.
Writing a message
To log a message using Fell, get the logger for a Fell namespace, and then call the logger method that is equivalent to the severity level of the message.
The example below gets a logger for the 'news-blade' namespace, and writes a message with a severity-level of INFO.
var fell = require('fell');
var logger = fell.getLogger('news-blade');
logger.info('Requesting ' + this.topic + ' news');
Changing logging levels
The default logging level for all namespaces is DEBUG. The DEBUG level maps to StreamLink’s FINE level. Currently, no levels in Fell map to StreamLink’s FINER and FINEST levels; StreamLink messages with these levels will not be logged by Fell.
To change log levels at application start up, edit the call to Fell.configure
found in the definition of caplinps/logging/LoggingBootstrap
.
To change log levels at runtime, use the Fell.changeLevel
method.
Sending logs to the server
The 'Report an Issue' form uses the caplinps/logging/LogPoster
class to post issues and logs to the server. Use or extend this class to post logs for other use cases.
The LogPoster.sendLogs
method sends 5 x-www-form-urlencoded fields to the server:
-
jslogs — A JSON-formatted array of strings
-
streamlinklogs — A JSON-formatted array of strings
-
message — The message the user entered into the 'Report an Issue' form
-
username — The username of the user reporting the issue
-
appversion — The application version (see below)
The LogPoster.sendLogs
method returns a jQuery promise object.
-
On success, the promise object resolves to an object with a single property,
requestID
, which contains the requestID returned by your issue management system. -
On failure, the promise object resolves to an object with a single property,
error
, which contains the error message returned by the server.
The code sample below sends logs to the server and specifies handlers for success and failure.
var LogPoster = require('caplinps/logging/LogPoster');
var ServiceRegistry = require('br/ServiceRegistry');
var config = ServiceRegistry.getService('caplin.config-service').getProperty('LOGGING');
var logPoster = new LogPoster({url:config.BACKEND_URL});
logPoster.sendLogs()
.done(function(data){
window.console.log('Logs sent', data.requestId);
})
.fail(function(data){
window.console.log('Error sending logs', data.error);
});
Application version
The LogPoster.sendLogs
method includes the application version in the data it posts to the server. The application version is retrieved from the service br.app-meta-service
.
To set the application version at build, include the command-line parameter -v <version>
or --version=<version>
to the brjs app-build
command.
To override the application version at run time, include an appVersion
property in the configuration object passed to the LogPoster constructor.
Processing logs on the server
To use Production Logging in live deployments, you will need to write your own backend service to process issue reports posted by caplinps/logging/LogPoster
.
FX Professional includes a demonstration servlet, DemoLoggingServlet
, which processes an issue posted by the caplinps/logging/LogPoster
class. The servlet saves the issue to a file and returns a randomly-generated request ID.
The DemoLoggingServlet class is not suitable for production use. |
To use DemoLoggingServlet
, ensure that your J2EE web container has permission to write to the filesystem directory where DemoLoggingServlet
saves issue reports. To review the location of this directory, see the servlet definition for DemoLoggingServlet
in FX Professional’s web.xml
file.
Follow the guidelines below when writing your own backend service:
-
Collect as many of the following issue fields as you need:
-
username
-
message
-
appversion
-
jslogs
-
streamlinklogs
-
-
Consider collecting the following HTTP headers to aid in debugging:
-
User-Agent
-
Origin
-
Referer
-
-
The content type of the service’s response must be 'application/json'.
-
On success, the service must return a JSON-encoded object that contains a single property,
requestId
. The value of the property is the ID of the issue in your issue tracking system. The Report an Issue dialog will display this ID to the user.-
Example:
{"requestId":"12345"}
-
-
On failure, the service must return a JSON-encoded object that contains a single property,
error
. The value of the property is the error message that you wish to return to the web browser. The Report an Issue dialog will log the error message to the browser’s JavaScript console, and display a generic error message to the user.-
Example:
{"error":"There was an error processing your request."}
-
To get started with writing your own backend service, use the example code template below:
public class DemoLoggingServlet extends HttpServlet {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json");
Map<String, String> responseJSON = new HashMap<>();
// Collect form data and HTTP headers. Post this information to your issue tracking system.
String username = request.getParameter("username");
String message = request.getParameter("message");
String appVersion = request.getParameter("appversion");
String jsLogs = request.getParameter("jslogs");
String streamlinkLogs = request.getParameter("streamlinklogs");
String userAgent = request.getHeader('User-Agent');
String origin = request.getHeader('Origin');
String referer = request.getHeader('Referer');
if (username == null || (jsLogs == null && streamlinkLogs == null)) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
responseJSON.put("error", "Missing required parameters 'username' and ('jslogs' or 'streamlinklogs')");
} else {
try {
// Process the reported issue
// ==========================
//
// Post the issue to your issue tracking system
// and retrieve the issue's ID.
String issueId = postIssue( ... );
//
// Prepare the response to the client
response.setStatus(HttpServletResponse.SC_CREATED);
responseJSON.put("requestId", issueId);
//
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
responseJSON.put("error", e.getMessage());
}
}
Gson Gson = new Gson();
PrintWriter rw = response.getWriter();
rw.println(Gson.toJson(responseJSON));
rw.close();
}
}
Configuring Production Logging
The default configuration settings for FX Professional are declared in the definition of the caplinx.AppConfig
class, located at /default-aspect/src/caplinx/AppConfig.js
. Production Logging’s configuration is stored in the LOGGING property of caplinx.AppConfig.options
. The code sample below shows the caplinx.AppConfig.options.LOGGING
property, with other properties hidden for clarity:
caplinx.AppConfig = function() {
.
.
this.options = {
.
.
LOGGING: <object>
.
.
};
.
.
};
To override the default configuration settings, add your changes to the caplinx.ExtendedAppConfig
class, which extends caplinx.AppConfig
. The definition of caplinx.ExtendedAppConfig
is located at /default-aspect/src/caplinx/ExtendedAppConfig.js
.
caplinx.ExtendedAppConfig = function() {
caplinx.AppConfig.call(this);
.
.
this.options.LOGGING = <object>;
.
.
};
LOGGING configuration property
The LOGGING property holds the configuration for Production Logging.
This property takes a single object as its value.
Property | Data Type | Description |
---|---|---|
ENABLED |
Boolean |
Set to true to enable Production Logging. |
BACKEND_URL |
String |
The URL of the back-end service to post logs to. Used by the |
SHOW_REPORT_ISSUE_MENU_ITEM |
Boolean |
Display a menu entry for Production Logging’s "Report an Issue" dialog. |
JS_STORAGE |
Object |
Configuration options for the JavaScript ring-buffer and local web storage. See LOGGING > JS_STORAGE object. |
STREAMLINK_STORAGE |
Object |
Configuration options for the StreamLink ring-buffer and local web storage. See LOGGING > STREAMLINK_STORAGE. |
The LOGGING > JS_STORAGE property takes a single object as its value:
Property | Data Type | Description |
---|---|---|
MAX_LINES |
Integer |
The maximum capacity of the ring buffer used for JavaScript log entries. When the ring buffer is at full capacity, adding a new line to the ring buffer overwrites the oldest line. |
MAX_SIZE |
Integer |
The maximum number of bytes to persist in the web browser’s local web storage. If the serialised contents of the ring buffer exceeds MAX_SIZE in bytes, the data is trimmed, one line at a time, until its size is less than MAX_SIZE. JavaScript uses UTF-16 character encoding (2 bytes per character). A MAX_SIZE of 1048576 bytes (1MB) equates to 524,288 characters. |
WRITE_INTERVAL |
Integer |
The time, in milliseconds, between writes to local web storage. |
The LOGGING > STREAMLINK_STORAGE property takes a single object as its value:
Property | Data Type | Description |
---|---|---|
MAX_LINES |
Integer |
The maximum capacity of the ring buffer used for StreamLink log entries. When the ring buffer is at full capacity, adding a new line to the ring buffer overwrites the oldest line. |
MAX_SIZE |
Integer |
The maximum number of bytes to persist in the web browser’s local web storage. If the serialised contents of the ring buffer exceeds MAX_SIZE in bytes, the data is trimmed, one line at a time, until its size is less than MAX_SIZE. JavaScript uses UTF-16 character encoding (2 bytes per character). A MAX_SIZE of 1048576 bytes (1MB) equates to 524,288 characters. |
WRITE_INTERVAL |
Integer |
The time, in milliseconds, between writes to local web storage. |
Example:
caplinx.ExtendedAppConfig = function() {
caplinx.AppConfig.call(this);
.
.
this.options.LOGGING = {
ENABLED: true,
BACKEND_URL: './servlet/logger',
SHOW_REPORT_ISSUE_MENU_ITEM: true,
JS_STORAGE: {
MAX_LINES: 1000,
MAX_SIZE: 1048576, // 2 bytes per char = 2MB
WRITE_INTERVAL: 10*1000 // 10 seconds
},
STREAMLINK_STORAGE: {
MAX_LINES: 1000,
MAX_SIZE: 1048576, // 2 bytes per char = 2MB
WRITE_INTERVAL: 10*1000 // 10 seconds
};
.
.
};