1.0.1 • Published 3 years ago

awdwadawdawdawdadawdawdadwadaw v1.0.1

Weekly downloads
-
License
UNLICENSED
Repository
-
Last release
3 years ago

Logging

Bluestone NPM module providing:

  • a logging library and patterns for structuring log entries
  • automatic logging of HTTP calls via middlewares

Build

  1. When changes are introduced in the NPM module, run this command to re-build it:
yarn install
yarn build
  1. Commit and push the dist/ folder in Git.

Installation as an NPM dependency

Add this to your package.json:

{
...
  "dependencies": {
    "@lambda-middleware/compose": "^1.2.0",
    "bluestone-logging": "file:../../utils/logging",
  }
...

If the NPM module has been recently updated, run the following command to retrieve its latest version (given there is currently no support for NPM versionning of the module):

cd my-service/
yarn install --force

Usage

Application logging

This NPM module provides a logger object that can be used anywhere in application code. Behind the scene, the logging framework it relies on is winston.

logger must be used for any type of application logs (instead of console.log).

In order to keep logs searchable and consistent, we provide below a few good and bad examples of structured log entries.

The benefit of structuring logs through this NPM modules is that it becomes easier to build functionality around it

  • ie. correlation IDs
  • ie. dashboard view
  • ie. signals filtered of all the logs with a given loan ID

Initial logger configuration

  1. Initialise the logger with:
export const loggerConfiguration = {
  service: 'my-service-name',
  level: <LogLevel>config.LOG_LEVEL,
  format: <LogFormat>format
};

export const logger = new Logger(loggerConfiguration);
  1. Change the service attribute value to be the name of the current application/service.

Notes:

  • The service name is used to search logs across multiple services in AWS CloudWatch Logs Insights.
  • The format is used to change the output depending on the environment variable called ENVIRONMENT.
    • In AWS (ENVIRONMENT = sandbox|staging|production), the format used is JSON. CloudWatch Logs Insights provides a query engine capable of searching the fields present in the JSON log entries.
    • Locally, ENVIRONMENT = local and the logs are using a prettified JSON output.
  • The level is used to search logs by level (ie. info, debug, warn, error) across multiple services in AWS CloudWatch Logs Insights.

Adding structured logs

  1. Import the logger
import { Logger } from 'bluestone-logging';
  1. Create application-specific standardised codes:

The convention used consists in using TypeScript enums named *Code.

For example:

export enum LambdaHandlerCode {
  OK = '@LAMBDA/HANDLER/OK',
  ERROR = '@LAMBDA/HANDLER/ERROR'
}

or

export enum LoanAccountCode {
  CLONE_OK = '@LOANACCOUNT/CLONE/OK',
  CLONE_ERROR = '@LOANACCOUNT/CLONE/ERROR'
}
  1. Adding application logs

Application logs can be added using logger.info(), logger.warn(), logger.error(), logger.debug().

All log messages have to be of type LogEntry.

Example:

try {
  ...
} catch (error) {
  logger.error({
    code: LOANACCOUNT.CLONE_ERROR,
    message: `Failed to clone Product ${process.env.MAMBU_LOANPRODUCT_ID}`,
    data: error
  });
}
  • The use of standardised codes enables log correlation and queries across different execution contexts or services.
  • message is used to provide human readable information message
  • data is optional and provide additional structured log data as an object.
  1. Advanced search filters

To provide advanced search filters, it is recommended to use the data attribute mentioned above.

Example:

try {
  ...
} catch (error) {
  logger.error({
    code: ProductCode.CLONE_ERROR,
    message: `Failed to clone Product ${process.env.MAMBU_LOANPRODUCT_ID}`,
    data: {
      loanId: 'MY-LOAN-ID'
    }
  });
}

The object { loanId: '...' } is application specific and should be typed, so that it can be re-used for adding multiple logs that can expose the loanId as a searchable field in AWS CloudWatch Logs Insights.

Good/bad examples

Bad example of use:

logger.info({
  code: 'MY-LOAN-ID',
  message: 'This is the loan ID in a log entry'
})

In contrast to the previous example, a good example would be:

logger.info({
  code: '@LOAN_ACCOUNT/DELETE/OK',
  message: 'This is the loan ID in a log entry',
  data: {
    loanId: 'MY-LOAN-ID'
  }
})

Recommendations:

  • Use code to describe actions, not data.
  • Use data to provide fields that are commonly used in searches for correlation purposes.

Mambu logging serverless middleware

This middleware intercepts all API calls to Mambu and logs request + response information.

To add the middleware to a serverless function, update the handler to follow this template:

import { APIGatewayEvent,
  APIGatewayProxyResult,
  ProxyHandler } from 'aws-lambda';
import { MambuLoggingMiddleware } from 'bluestone-logging';
import { compose } from '@lambda-middleware/compose';

async function lambda(
  event: APIGatewayEvent,
): Promise<APIGatewayProxyResult> {
 // lambda code
};

export const handler: ProxyHandler = compose(
  MambuLoggingMiddleware(loggerConfiguration)
)(lambda);

See the Initial Logger Configuration section(#Initial logger configuration) above for information on how to create the loggerConfiguration object.