Node.js Monitoring
Features
Germain APM can monitor availability, performance and usage of any Node.js application. The main features of the Germain APM Node.js monitoring are:
Error and Exception monitoring
Warning monitoring
Exit monitoring
HTTP monitoring
Express monitoring
Event loop latency monitoring
Startup duration monitoring
Transaction monitoring
Process resources monitoring
OS resources monitoring
Disk monitoring
Multiple promise resolve/rejection monitoring
Requirements
npm package manager integration
Node.js v14 or higher
Installation
npm Installation
Germain APM monitoring package is available here: https://www.npmjs.com/package/@germainapm/nodejs-monitoring
Run the following command to add our monitoring into your Node.js application:
npm i --save @germainapm/nodejs-monitoring
Yarn Installation
Germain APM monitoring package is available here: https://yarnpkg.com/package/@germainapm/nodejs-monitoring
Run the following command to add our monitoring into your Node.js application:
yarn add @germainapm/nodejs-monitoring
Initialization
Initialize germain APM monitoring in your main application javascript file (e.g. server.js). In the simplest integration only the Germain APM server URL (where the monitoring data will be sent - contact the germain Team if you don't know it) must be provided and the rest will be autoconfigured.
Integration example using RequireJS:
const GermainAPM = require('@germainapm/nodejs-monitoring');
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
const apm = new GermainAPM(config);
apm.start();
Integration example using ES6:
import GermainAPM from '@germainapm/nodejs-monitoring';
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
const apm = new GermainAPM(config);
apm.start();
Germain APM UX with Incoming HTTP
Germain APM allows you to tag UX monitoring data and correlate it with incoming http traffic in Node.js. To allow this correlation make sure UX is configured to tag data and Node.js is configured to allow custom http headers.
Germain APM UX configuration (taggingEnabled flag must be set to true):
const settings = germainApm.getDefaultSettings(loaderArgs, agentConfig);
settings.plugins.network.trackingEnabled = true;
Node.js configuration to allow germain-apm-sequence
custom http header:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, germain-apm-sequence");
next();
});
Configuration
In addition to the initialization and auto-configuration we allow customers to define custom configuration and enable/disable monitoring components. Please review GermainAPMConfiguration
documentation for the complete list of options and setters.
Type definition for GermainAPMConfiguration
object:
type GermainAPMConfiguration = {
/** Germain APM environment URL (e.g. http://localhost:8080). */
servicesUrl: string;
/** Config to enable/disable/customize what is monitored */
monitoring: {
/** How often periodic monitoring should be collected. Default: 60 */
frequencySeconds: number;
/** Whether to enable correlation of data across async callbacks. Default: true */
asyncCorrelation: boolean;
/** Unhandled Exception monitoring */
unhandledExceptions: {
/** Enable monitoring. Default: true */
enabled: boolean;
/** Whether to exit the Node process on error. Default: true */
exitOnError: boolean;
/** Callback to be fired when an error occurs. */
callbackOnError?: (error: Error) => void;
},
/** Incoming HTTP monitoring */
httpIncoming: {
/** Enable monitoring. Default: true */
enabled: boolean;
/** Function to extract a sessionId from an incoming request */
sessionExtractor?: (request: IncomingMessage) => string;
},
/** Outgoing HTTP monitoring */
httpOutgoing: {
/** Enable monitoring. Default: true */
enabled: boolean;
/** Function to extract a sessionId from an incoming request */
sessionExtractor?: (response: ClientRequest) => string;
},
/** Node Startup time monitoring. Default: true */
startup: true,
/** Node Process monitoring. Default: true */
pid: boolean;
/** OS monitoring (CPU, Memory). Default: true */
os: boolean;
/** Disk monitoring. Default: true */
disk: boolean;
/** Node Warning monitoring. Default: true */
warning: boolean;
/** Unhandled Promise Rejection count monitoring. Default: true */
unhandledPromiseRejection: boolean;
/** Event Loop Latency monitoring. Default: true */
eventLoopLatency: boolean;
/** Exit monitoring - when Node shuts down. Default: true */
exit: boolean;
/** Multiple Promise Resolve monitoring. Default: true */
multiplePromiseResolves: boolean;
},
/** Logging configuration */
logging: {
/** Level of logging to enable. Default: 'info' */
level: 'error' | 'warn' | 'info' | 'verbose' | 'debug',
/** Filename to log to. Default: 'GermainAPM.log' */
filename: string;
/** Max number of rolling files to keep. Default: 10 */
maxFiles: number;
/** Max size of a single rolling log file. Default: 10Mb */
maxSize: number;
},
/** Data sending settings */
datapoints: {
/** Whether to send data to the server or just discard. This is a debug setting as should always be set true. Default: true */
sendData: boolean;
/** Whether outgoing data should be compressed to reduce network overhead. Default: true */
compress: boolean;
/** Max number of datapoints to keep in-memory before sending to the server. Default: 250 */
maxNumInMemory: number;
/** Max time to wait between sending data to server. Default: 60 */
maxFlushTimeSeconds: number;
},
/** Default data to be added to datapoints */
defaults: {
/** Default user */
user?: UserDimension;
/** Default system */
system?: SystemDimension;
/** Default application */
application?: ApplicationDimension;
/** Default pid */
pid?: string;
}
/** Exclusions to be applied to collected datapoints */
exclusions: FieldExclusionConfig[]
}
Monitoring Frequency
Some monitoring components such as Node.js process resource monitoring run periodically. By default they run every 60 seconds but you can be customized as follows
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.frequencySeconds = 60;
Asynchronous Correlation
By default Germain APM performs correlation between incoming HTTP requests and asynchronous outgoing HTTP requests. This feature has a minor performance overhead and can be disabled as follows.
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.asyncCorrelation = false;
Distribution Frequency
By default Germain APM monitoring sends data back to the Germain APM Server every 30 seconds. You can update this frequency by setting maxFlushTimeSeconds
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.datapoints.maxFlushTimeSeconds = 60;
Application
You can associate an application name with all collected data. The simplest way to provide an application name is by passing it as the second (optional) argument, when creating the default settings:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/', 'APP_NAME');
Alternatively you can provide the application name and application component by providing a configuration object:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.defaults.application = { name: 'APP_NAME', component: 'APP_COMPONENT' };
Logging output
By default, logging (info level and above) is enabled when Germain APM monitoring gets initiated. You can change this configuration through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.logging.level = 'debug';
config.logging.filename = 'GermainAPM_custom.log';
config.logging.maxFiles = 20;
config.logging.maxSize = 20000000; // in bytes
Data Exclusion
By default all captured data is sent to the APM server, however it may be necessary to exclude or anonymize some data due to security concerns. The exclusions section of the GermainAPMConfiguration
allows to configure which fields (per fact type or globally) should be excluded or anonymized.
Masking - The string will be replaced with * characters.
Anonymization - The string will be hashed so the original string cannot be read. This allows the same value to be hashed globally so it can be used to anonymously associate data.
Exclusion - The string will be replaced with an empty string.
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.exclusions = [{
fieldName: 'sessionId', // The field that will be anonymized
factType: '', // The fact type that this exclusion applies to, if empty it will apply to all facts
type: 'MASK', // The type of exclusion to apply, one of 'MASK', 'ANONYMIZE' or 'EXCLUDE'
name: 'SessionId Anonymization', // A readable name for this exclusion
pattern: '', // A regexp to allow anonymization/exclusion of matching groups within a string
preserveLength: true, // If true (and type is 'MASK') the length of the excluded string will be preserved
preserveWhitespace: true, // If true (and type is 'MASK') the whitespaces within the excluded string will be preserved
enabled: true // Allows to enable/disable this exclusion
}, {
fieldName: 'requestBody',
factType: 'NodeJS:Outbound',
type: 'ANONYMIZE',
name: 'Request Body Exclusion',
pattern: '',
preserveLength: true,
preserveWhitespace: false,
enabled: true
}];
Integration
Error and Exception Monitoring
This monitoring can collect all types of custom and standard errors (AssertionError, EvalError, SyntaxError, RangeError, ReferenceError, TypeError, URIError
) and all system errors including:
Permission denied
Address already in use
Connection refused
Connection reset by peer
File exists
Is a directory
Too many open files in system
No such file or directory
Not a directory
Directory not empty
Operation not permitted
Broken pipe
Operation timed out
Configuration
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.unhandledExceptions.enabled = true; // Enable/Disable automatic unhandled exception monitoring. Default: true.
config.monitoring.unhandledExceptions.exitOnError = true; // Process exit on unhandled exception (with exit code 1). Default: true.
config.monitoring.unhandledExceptions.callbackOnError = null; // Callback on unhandled exception provided as a function if exitOnError=false. Callback function will receive Error instance as the only parameter. Default: empty / no callback.
Unhandled exceptions
Caught by process
If you already catching all unhandled exceptions by listening to the uncaughtException
event on the process
then you should explicitly collect the exception in your exception handler like this and disable config.monitoring.unhandledExceptions
:
process.on('uncaughtException', function (err) {
GermainAPM.collectError(err);
});
Uncaught by process
If you don't catch unhandled exceptions yet, you have 2 options to use our monitoring to catch them:
Exit on Error
If you set the exitOnError:true
then, once the exception caught, we will exit from the process with id 1 (process.exit(1)
). Node.js does recommend to exit from the process following an unhandled exception.
Correct configuration for this scenario:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.unhandledExceptions.enabled = true;
config.monitoring.unhandledExceptions.exitOnError = true;
config.monitoring.unhandledExceptions.callbackOnError = null;
Callback on Error
If you set the exitOnError:false
but provide a function callback to the callbackOnError
then, once the exception is caught, we will execute your callback by passing the Error instance as the only parameter. Node.js doesn't recommend to proceed with any logic if an unhandled exception occurs.
Correct configuration for this scenario:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.unhandledExceptions.enabled = true;
config.monitoring.unhandledExceptions.exitOnError = false;
config.monitoring.unhandledExceptions.callbackOnError = function(error){
// your logic to process the exception
};
Disable automatic unhandled exceptions monitoring
You can disable this monitoring through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.unhandledExceptions.enabled = false;
Handled exceptions
You can collect an exception / an error explicitly by adding the following code to your handled exception logic:
try {
// ...
} catch(err){
GermainAPM.collectError(err);
// your logic here ...
}
Warning Monitoring
By default Germain APM monitors Node.js warnings (bad coding practices, bugs, or security vulnerabilities). You can disable this monitoring through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.warning = false;
Exit Monitoring
By default Germain APM monitors Node.js process exit events. You can disable this monitoring through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.exit = false;
HTTP Monitoring
Incoming HTTP Monitoring
By default Germain APM monitors incoming http traffic. You can disable this monitoring through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.httpIncoming.enabled = false;
Session extraction
In addition you can provide a session extractor function to extract session ids from the incoming http traffic. By default, no session extractor is provided.
Example below shows how you can extract an existing session id from incoming http requests' headers and pass it on to the germain APM monitoring :
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.httpIncoming.sessionExtractor = function(incomingMessage) { // you receive an instance of the <http.incomingMessage>
return incomingMessage.headers.sessionId;
};
Outgoing HTTP Monitoring
By default Germain APM monitors outgoing http traffic. You can disable this monitoring through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.httpOutgoing.enabled = false;
Session extraction
In addition you can provide a session extractor function to extract session ids from the outgoing http traffic. By default, no session extractor is provided.
Example below shows how you can extract an existing session id from outgoing http requests' headers and pass it on to the germain APM monitoring :
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.httpOutgoing.sessionExtractor = function(clientRequest) { // you receive an instance of the <http.clientRequest>
return clientRequest.getHeader('sessionId');
};
Event Loop Latency Monitoring
By default Germain APM monitors Node.js event loop latency. You can disable this monitoring through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.eventLoopLatency = false;
Startup Monitoring
By default Germain APM monitors Node.js startup duration as time between the current Node.js process startup and Germain APM initialization. You can disable this automatic feature and call it explicitly when you think the startup has been fully completed:
GermainAPM.collectStartup();
You can disable this monitoring through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.startup = false;
Transaction Monitoring
Germain APM allows you to monitor all kinds of transactions in your Node.js application. To enable this monitoring you need to programmatically set the start of a transaction (GermainAPM.startTransaction()
) and the end of a transaction (GermainAPM.endTransaction()
).
Start a transaction:
GermainAPM.startTransaction('UNIQUE_TRANSACTION_NAME', 'AdditionalInformation'); // AdditionalInformation is optional, you can pass in null value
End started transaction:
GermainAPM.endTransaction('UNIQUE_TRANSACTION_NAME');
Process resources Monitoring
By default Germain APM monitors CPU, Memory Usage, Heap Size and Heap Usage of your Node.js process. You can disable this monitoring through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.pid = false;
OS resources Monitoring
By default Germain APM monitors CPU and Memory Usage of the server on which your Node.js application runs. You can disable this monitoring through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.os = false;
Disk Monitoring
By default Germain APM monitors Disk Usage of the server on which your Node.js application runs. You can disable this monitoring through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.disk = false;
Multiple Promise Resolve/Rejection Monitoring
By default Germain APM monitors when a promise has been resolved or rejected multiple times. You can disable this monitoring through GermainAPMConfiguration
:
const config = GermainAPM.getDefaultConfiguration('http://GERMAIN_APM_SERVER/');
config.monitoring.multiplePromiseResolves = false;