Genesys Cloud Client Logger
Logger to send client logs to a remote server.
See CHANGELOG.md for version updates.
Install
Using a package manager:
npm install genesys-cloud-client-logger
yarn add genesys-cloud-client-logger
Or directly from the CDN:
<script src="https://apps.mypurecloud.com/genesys-cloud-client-logger/v4/genesys-cloud-client-logger.min.js"></script>
<script src="https://apps.mypurecloud.com/genesys-cloud-client-logger/v4.0.1/genesys-cloud-client-logger.min.js"></script>
Note: the major and exact versions were added in v4.0.2
. The version hosted at .../genesys-cloud-client-logger/genesys-cloud-client-logger.min.js
will always remain as v4.0.1
to avoid breaking changes. However, it is strongly recommended that you upgrade to use a major version URL.
Basic Concept
Each Logger instance will have it's own configuration meaning you can have multiple apps using their own individual loggers. One thing to note is the loggers
will share a "log-uploader" for each given url
. For example, if app1
and app2
both POST logs to the same endpoint, they will have their own logger and
config, but will share the same uploader. Meaning only one POST request will happen at a time. This is to help reduce rate limiting by having multiple loggers
all sending POST requests to the same endpoint at the same time.
Usage
import { Logger } from 'genesys-cloud-client-logger';
const logger = new Logger({
url: 'https://yoursite.com/logs',
accessToken: 'your-access-token',
appVersion: '1.2.3',
appName: 'your-client-app1'
});
logger.info('Logger initialized');
Available options and their defaults:
interface ILoggerConfig {
accessToken: string;
url: string;
appVersion: string;
appName: string;
originAppName?: string;
originAppVersion?: string;
originAppId?: string;
initializeServerLogging?: boolean;
startServerLoggingPaused?: boolean;
logLevel?: LogLevel;
uploadDebounceTime?: number;
debugMode?: boolean;
stringify?: boolean;
logger?: ILogger;
formatters?: LogFormatterFn[]
}
Logging messages
log (message: string | Error, details?: any, opts?: ILogMessageOptions): void;
interface ILogMessageOptions {
skipDefaultFormatter?: boolean,
skipServer?: boolean,
skipSecondaryLogger?: boolean,
}
The default formatter handles extracting the message from an error object as well as prepending the app name to the
message that will be logged. For example, if your app name is "really cool app" and you do something like this:
logger.info('some message I care about', { favoriteColor: 'blue' });
It will be logged like this:
[really cool app] some message I care about {...}
If you were to log a message like this:
logger.info('some message I care about', { favoriteColor: 'blue' }, { skipDefaultFormatter: true });
It would be logged without the app name:
some message I care about {...}
How Formatters Work
Formatters are a great tool to handle unique logging situations. For example, let's say
you have an error that has the potential to expose or send information that is unfit to
be exposed. In a formatter, you can choose to manipulate the message or details, do
nothing, or skip logging the message entirely. A formatter will be provided a next
function in addition to the log message. If next is not called, the log will not be forwarded
to downstream formatters and will not make it to the actual logger. Example:
function myCustomFormatter (
level: LogLevel,
message: string | Error,
details: any | undefined,
options: ILogMessageOptions,
next: NextFn
) {
if (message.includes('[confidential]')) {
options.skipServer = true;
return next(level, 'this message is confidential and redacted', details, options);
}
if (message.includes('[top secret]')) {
return;
}
next();
}
const logger = new Logger({
url: 'https://yoursite.com/logs',
accessToken: 'your-access-token',
appVersion: '1.2.3',
appName: 'your-client-app1',
formatters: [ myCustomFormatter ]
});
logger.info('here is a message');
logger.info('here is a [confidential] message');
logger.info('here is a [top secret] message');
Pausing Server Logs
In version 4.1.0, "pausing" server logs was introduced. This is useful in a few of the following scenarios (not an exhaustive list):
POST
ing logs to an endpoint returns a 401
or 404
status code.- The consuming apps wants to be able to stop sending logs for a given period of time
and then start sending again.
- The consuming app wishes to construct a logger instance with server logging paused,
and then start sending logs further in the future.
The declaration of the available functions are:
class Logger {
startServerLogging (): void;
stopServerLogging (): void;
sendAllLogsInstantly (): Promise<any>[];
}
And available events:
logger.on('onError', (error: any) => { });
logger.on('onStart', () => { });
logger.on('onStop', (reason: StopReason) => { });
type StopReason = '401' | '404' | 'force';
You can also leverage the config option startServerLoggingPaused
(see above).
A few notes:
stopServerLogging()
does a few things:
- it will no longer queue up requests. In other words: it will not "retroactively"
send logs to the server while it was paused.
- it will clear out any queued up log items – meaning: any pending logs will be
dropped on the floor. If you want to send any pending items, call
sendAllLogsInstantly()
before stopping the server logging.
- If the logger receives a
404
, it will automatically stop sending server logs. - If the logger receives a
401
, it will automatically stop sending server logs. However,
once setAccessToken()
is called, it will start sending logs again.
- NOTE: if
stopServerLogging()
was called, setAccessToken()
will NOT
automatically start sending logs again. It will only start sending logs again if the
401
was the last event received inside the logger.