README
lib-logger
Logger
Install
yarn add @luxuryescapes/lib-logger
Usage
import { createLogger } from '@luxuryescapes/lib-logger'
// Alternatively, for a service hosted in Heroku, we recommend using the contextual logger. See below for details.
const logger = createLogger({ env: "development", service: "svc-good-times", logLevel: "info" })
export default logger
import logger from '/path/to/lib/logger'
logger.error(error)
Options
Option | Required | Description |
---|---|---|
env | Yes | The service environment, e.g. development, production |
service | Yes | The service name e.g. svc-public-offer |
logLevel | Yes | The lowest log level to output |
additionalFormats | No | Additional Winston formats that should be used |
Additional Formatters
You can pass additional formatters to customize your log output. For example:
const contextFormatter = winston.format((info) => {
info.hello = 'world'
return info;
});
createLogger({
logLevel: config.logLevel,
service: config.herokuAppName,
env: config.appEnv,
additionalFormats: [contextFormatter()],
});
See Creating Custom Formats for more info.
Contextual Logger
The contextual logger uses Node's async_hooks
functionality to add Request IDs to the log output.
import { createContextualLogger, loggingMiddleware } from '@luxuryescapes/lib-logger';
import requestAwareLogger from "@luxuryescapes/lib-logger/lib/requestAwareLogger"
const loggerConfig = {
logLevel: config.logLevel,
service: config.herokuAppName,
env: config.environment
};
const logger = createContextualLogger(loggerConfig);
app.use(express.json()) // Cannot go after requestAwareLogger.getMiddleware.
app.use(loggingMiddleware())
NOTE: If you are using bodyParser.json() or express.json() middleware in your project you need to set them before using logging Middleware. Otherwise logging context will be lost
File Logger
If you specify a file when configuring the logger, the logger will log at log level debug to that file if the env is development or spec.
This is handy if you want to tail the logs when generating verbose SQL statements for example:
const loggerConfig = {
logLevel: 'warn',
service: 'my-service',
env: 'development',
filename: 'combined.log'
};
Migration existing project to lib-logger
Migrating existing project based on console.log to lib-logger can take a fair amount of effort even though it's not a hard work - just a lot of typing depending on the size of the project.
Below are the steps you need to take to successfully convert your project.
- Update following dependencies to the latest versions:
- @luxuryescapes/lib-logger (required) This library keeps track of request context and formatting.
- @luxuryescapes/lib-authenticated-http-client We use this library to make http calls and log related details including errors. It is crucial to make sure that this lib logs everything correctly in JSON format
- @luxuryescapes/lib-auth-middleware All authentication is done by this library, which makes it really important to get correct logs from this lib.
- Define logger, if not done already. You'll need to create a logger.js/ts file with content described above in "Contextual Logger" section.
- Add requestAware logger middleware. Add following to server.js/ts.
// Imports import { loggingMiddleware } from "@luxuryescapes/lib-logger"; // Middleware definitions app.use(loggingMiddleware())
- If your service uses express.json() or bodyParser.json() middleware you must set them before requestAwareLogger.
- Make sure that you don't have express.json() or bodyParser.json() specified in your routes
router.post(route, bodyParser.json(), // WILL NOT WORK. Remove it and replace with global express.json() middleware in server.js auth.cachedVerifyUser(), actions.actions.clone);
- Pass logger to lib-auth-middleware.
const logger = require("../../logging/logger"); const cachedVerifyUser = () => auth.verifyUser({ cache: redis, logger }); // Notice, logger
- Pass logger to lib-authenticated-http-client.
import logger from "../logging/logger"; const authenticatedHttpClient = new AuthenticatedHttpClient({ serviceName: config.herokuAppName, apiHost: config.hosts.api, serviceUsername: config.serviceUserEmail, servicePassword: config.serviceUserPassword, logRequests: true, timeRequests: true, logger: logger // Notice, logger });
- Update all
console.log
with logger.info/warn/error. Winston logger works differently to console.log. So attention needs to be paid how to convert all console logs to the new format. Have a look at the examples below. They cover most of the scenarios.console.log("my message") logger.info("my message") const myVar = "var1" console.log("my var:", myVar) logger.info(`my var: ${myVar}`) const myError = new Error("error happened") console.log("Something went wrong", myError) logger.error("Something went wrong", myError) // will print "Something went wrong error happened" and append stacktrace as another field console.log("Request Body:", req.body) logger.info("Request Body", {requestBody: req.body}) const varA = {a: 1, b: 2} const varB = "aaabbbccc" console.log("Params:", varA, varB) logger.info("Params", {params: {varA, varB}})
- Update .eslintrc.json to start throwing errors for console.log
"rules": { "prettier/prettier": "error", "no-undef": "error", "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], "no-unused-expressions": "error", "no-console": "error" // <=================== },
- You can still leave some console logs in scripts and files that are not part of production code.
Sending logs to NewRelic
lib-logger by itself is not sufficient for your logs to appear in NewRelic. This page has more information how to do that: https://aussiecommerce.atlassian.net/wiki/spaces/TEC/pages/2192080997/Deploying+a+new+service+in+Heroku