Node.js Logging – How to Get Started - Papertrail (2024)

Last updated: September 2024

Node.js may be the run-time environment that lets you use a single programming language (JavaScript) for both server-side and client-side applications, but it has no shortage of logging methods. Even though there are clearly delineated trade-offs with each of the logging techniques, if you’re a newcomer, it can be difficult to make sense of them and to know which one is right for your environment.

Fortunately, Node.js provides support for logging out of the box, so taking your first steps is easy. From there, you can progress to more sophisticated logging modules that allow you to enable and disable log statements, customize where your log data is sent, and assign a severity to log messages.

In this article, we’ll show you how to quickly start writing logs with Node.js. We’ll gradually introduce new modules with more flexibility, so you can make the best choice for logging Node.js in your environment.

Node.js Logging – How to Get Started - Papertrail (1)

What Should I Log?

You should log information that helps you understand the flow of your application. That way, you can easily identify, troubleshoot, and debug issues at runtime. Some common information you can consider logging are:

  • Errors and Exceptions that happen on your application. This makes it easy to troubleshoot and diagnose issues.
  • Warnings that help you identify non-critical issues that need attention. For example, you may log warnings on deprecated packages or endpoints.
  • Application start and stop time. This is essential in troubleshooting application uptime.
  • User activities such as logging and subsequent actions in the application. Logging user activities is handy when you want to determine which user performed a certain action.

Built-in Node.js Libraries

The starter log choice for Node.js is its built-in console module. The console lets you focus solely on writing log messages to track your application’s behavior. After console, you can move up to the debug module, which wraps console and provides more flexibility. Let’s take a closer look at these two modules, and the pluses and minuses of each.

Node.jsconsole.logandconsole.error

The built-inconsolemodule in Node.js lets you write log messages to standard output (stdout) and standard error (stderr)using theloganderrorfunctions, respectively. This module is most definitely the simplest method for getting started, since it doesn’t require importing or configuring any third-party packages.

The following code snippet usesconsole.logto write a message tostdout:

console.log(‘A log message’);

Though console.log offers a hassle-free way to write log messages, it leaves a lot to be desired. For one, there’s no way to disable console.log calls. If you add calls to your code, your messages will appear on the console whether you want them there or not. Secondly, console.log doesn’t accept logging levels, which means that apart from the two log and error functions, you cannot differentiate between log messages with different severities. And that makes for noisy applications, especially when you’re writing production code or libraries used by other developers.

Lastly, console.log is implemented as part of the JavaScript runtime, but its behavior isn’t standardized across all implementations. In the vernacular of programming language designers, the console.log behavior is undefined. That means it can behave however it wants, and the way it works can change from release to release.

Normally, this undefined behavior isn’t a problem, but it can have consequences when you write to a console before it’s active. After you’ve called console.log, the console updates asynchronously; instead of queuing the text exactly as you’d expect it to appear, a reference is queued. If you change the queued object, the old value will be rendered on the console when it updates, not the new one.

There are ways to work around this oddity by converting whatever you’re printing to an immutable string, but the simplest answer is not to use console.log at all but instead a logging module.

While the console module is always available for quickly writing messages, a proper logging package allows you to disable all logging calls and have at least some control over which log messages you see. The debug module provides just such a feature.

The debug Module

A step up from usingconsole.logis thedebugmodule. This module is a tiny JavaScript debugging package that provides a simple mechanism for turning logging on and off inside your code.

Thedebugmodule provides a function that accepts your module’s name as its only parameter and returns a decoratedconsole.errorobject that writes log messages tostderr.

Here’s an example of usingdebugin afoomodule:

var debug = require('debug')('foo')function alarm() { debug('This is a debug message');}alarm();

If you run this code with no changes to your environment, nothing is printed on the console. But if you set theDEBUGenvironment variable tofoo,you’ll see the following output:

$ DEBUG=foo nodejs debug.js foo This is a debug message +0ms

That’s because the debug module disables all log messages by default, so you must specifically enable the ones you want. To enable them, use the DEBUG environment variable, which takes a string of module names separated by commas.

Of course, the debug module is still just a wrapper around the rudimentary console.log, and it doesn’t provide a granular way to turn log messages on and off. You need to look to other libraries to take advantage of a more feature-rich logging solution.

Best Node.js Logging Libraries

Below is a list of some of the best Node.js logging libraries.

Winston

Winston is a flexible logging library for Node.js. From its inception, Winston was designed to be simple yet configurable, so it includes support for custom log formats, sending log messages to multiple types of destinations, and colorizing logs. Critically, Winston also comes with a feature we’ve been missing in our discussions until now – logging levels.

No one wants to pick through log messages in the middle of a heated troubleshooting session to figure out whether, at that moment, a particular message is a vitally important or inconsequential noise.

The best logging frameworks offer logging levels so you can highlight the messages you care about. Use them correctly when writing code; they will save you a lot of time when you need to view only a select category of messages.

The logging levels available in Winston are those from npm. Ordered from most important to least important, the corresponding functions are error, warn, info, verbose, debug, and silly.

A transport is the component that sends log messages to their destination, and a Winston logger can use multiple transports at the same time. This comes in handy if you want to use Winston’s core transports to send debug or info logs to the console and use one of the community transports to send critical messages to a remote log management tool.

Here is an example of creating a simple logger object with Winston that writes a debug and error string:

const winston = require(‘winston’);const logger = winston.createLogger({ level: 'info', format: winston.format.simple(), transports: [ // // - Write to all logs with level `info` and below to `combined.log` // - Write all logs error (and below) to `error.log` // new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }) ]});logger.info(‘Info string’);logger.error(‘Error string’);

Writing a message with the highest priority level (error) writes to bothcombined.loganderror.log:

$ cat combined.log info: Info stringerror: Error string
Node.js Logging – How to Get Started - Papertrail (2)

Pino

Pino is a fast, low-overhead, and highly configurable Node.js JSON logging library. The low overhead and high performance make it suitable for use in a production environment. Some of its features include:

  • Child loggers: Create a child logger with inherited properties from the parent logger. Child loggers are good for organization and adding context-specific information to logs.
  • Log transport: Use transports to send logs to various destinations. This could be for files or logging management tools.
  • Log redaction: Redact sensitive data from log messages.
  • Log levels: Set log levels for different environments. For example, trace, debug, info, warn, and error log levels.

Here’s an example of creating a simple parent and child logger

const logger = require("pino")();logger.info("this is parent info log");const child = logger.child({ a: "property" });child.info("this is child info log");

Here’s the result

{"level":30,"time":1716577065425,"pid":86439,"hostname":"MBP.local","msg":"this is parent info log"}{"level":30,"time":1716577065426,"pid":86439,"hostname":"MBP.local","a":"property","msg":"this is child info log"}

Unlike most Node.js logging libraries, Pino logs the level using numbers that increase in 10s and not string.

Morgan

Morgan is a logging middleware for Node.js. It logs HTTP requests and responses and works with Node.js web frameworks like Express. Morgan logs various information for each request, including the IP address, the time the request was received, the URL requested and the HTTP method, the HTTP status code, and more.

Morgan follows the standard Apache common log output and provides different predefined formats, such as combined, common, dev, short, and tiny.

Here’s an example of creating a simple logger using the combined output

const express = require("express");const morgan = require("morgan");const app = express();

// Set up Morgan to log requests using the combined format

app.use(morgan("combined"));

// Define your routes and other middleware…

app.listen(3000, () => { console.log("Server started on port 3000");});

Here’s the result:

Server started on port 3000::1 - - [20/May/2024:19:25:41 +0000] "GET / HTTP/1.1" 404 139 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"

Best Practices for Node.js Logging

Ultimately, the Node.js logging module you pick for your code will come down to both personal taste and business requirements. But there’s one thing that everyone can agree on; it’s not just how you log that matters, it’s alsowhatyou log.

Here are a few best practices that everyone should follow for creating proper logs no matter which Node.js module you use.

Frustration-free log management. (It’s a thing.)

Aggregate, organize, and manage your logs with Papertrail

Use Correct Log Levels

Log levels allow you to categorize logs based on severity. By assigning levels, you can filter and manage your logs appropriately. Here’s a list of common levels:

  • Info: this gives information on the normal operation of your application.
  • Debug: although verbose, it gives detailed diagnostic information.
  • Warn: used for non-critical issues that require attention.
  • Error: gives information on errors and exceptions preventing the application from running successfully.
  • Fatal: used to capture information on severe errors that lead the app to terminate abruptly or crash.

When you use the correct logging levels, you ensure that your logs are meaningful. As a result, the logs provide the right level of detail based on your environment’s specific needs.

Add Enough Context to Your Logs

Providing sufficient context in log messages is relevant for effective debugging and troubleshooting. Without adequate context, it can be challenging to understand the circ*mstances surrounding a logged event. In addition to the logged message, add metadata that gives context to the application’s state. For example, you can include user information like ID or email, request details (URL, methods, headers), application version, request IDs, transaction IDs, and more.

Here’s an example of providing context to your logs using Pino

const logger = require("pino")();logger.info( { userID: "0809bd4e-58fa-46c6-a9bd-b1f8d171affa" }, "User updated successfully");

The result:

{"level":30,"time":1716582173671,"pid":13771,"hostname":"MBP.local","userID":"0809bd4e-58fa-46c6-a9bd-b1f8d171affa","msg":"User creation successful"}

The userID adds context to the log output, allowing you to identify the updated user easily.

Avoid Logging Sensitive Information

Logging sensitive information, such as passwords, API keys, or personal data, can pose a significant security risk if an unauthorized individual gains access to the logs. One way to prevent this is to use environment variables where the sensitive data is not hardcoded into the source code. Another way is to ensure that you configure your logging library to redact specific fields based on a pattern.

Here’s an example of how you can configure Pino to redact passwords.

const pino = require("pino");const logger = pino({ redact: ["password"],});logger.info( { password: "myPassword", other: "data" }, "Sensitive log message");

This is the log:

{"level":30,"time":1716580274830,"pid":3748,"hostname":"MBP.local","password":"[Redacted]","other":"data","msg":"Sensitive log message"}

Add Timestamps

Knowing exactly when a log message was created allows you to retrace your app’s events and find an issue’s root cause. In other words, timestamps make sure you’re looking at a list of events in the correct order. And timestamps become even more important when you’re correlating log data across multiple apps and services.

Winston allows you to add them to your log messages through itsformatoption. These formats are implemented inlogform, a separate module to winston. Let’s expand our example to customize the format of your log messages using theprintffunction.

const { createLogger, format, transports } = require('winston');const { combine, timestamp, label, printf } = format;const myFormat = printf(({ level, message, timestamp }) => { return `${timestamp} ${level}: ${message}`;});const logger = createLogger({ level: 'info', format: combine( timestamp(), myFormat ), transports: [ // // - Write to all logs with level `info` and below to `combined.log` // - Write all logs error (and below) to `error.log`. // new transports.File({ filename: 'error.log', level: 'error' }), new transports.File({ filename: 'combined.log' }) ]});logger.info('Info string');logger.error('Error string');

In this example, the resulting output in thecombined.logfile looks like this:

2019-04-24T11:58:55.786Z info: Info string2019-04-24T11:58:55.787Z error: Error string

Include Tags in Your Logs

When working with complex apps that have many components, including tags in your logs lets you filter out messages you don’t want so you can focus on the ones that matter. Beyond the benefit tags provide when searching through log files manually with grep, cloud-based log aggregation tools run searches and filter log data through tags as well.

For winston, theformatoption comes to the rescue once again. Let’s expand our example further to include your script’s filename in the log messages, creating a kind of namespace:

const { createLogger, format, transports } = require('winston');const { combine, timestamp, label, printf } = format;const myFormat = printf(({ level, message, label, timestamp }) => { return `${timestamp} [${label}] ${level}: ${message}`;});var filename = module.filename.split('/').slice(-1);const logger = createLogger({ level: 'info', format: combine( label({ label: filename }), timestamp(), myFormat ), transports: [ // // - Write to all logs with level `info` and below to `combined.log` // - Write all logs error (and below) to `error.log`. // new transports.File({ filename: 'error.log', level: 'error' }), new transports.File({ filename: 'combined.log' }) ]});logger.info('Info string');logger.error('Error string');

The output of combined.log now looks like this:

2019-04-19T21:00:28.731Z [winston-label.js] info: Info string2019-04-19T21:00:28.733Z [winston-label.js] error: Error string

Use JSON for Structured Log Data

Combing through thousands of lines of log data is a monumental task that’s made trivial by one simple change to your logger—usingJSONoutput. JSON is a form of structured data that is both human and machine readable, making it perfect for log data that might be read by developers and also log management tools.

const { createLogger, format, transports } = require('winston');const { combine, timestamp, label, printf } = format;const myFormat = printf(({ level, message, label, timestamp }) => { return `${timestamp} [${label}] ${level}: ${message}`;});var filename = module.filename.split('/').slice(-1);const logger = createLogger({ level: 'info', format: combine( label({ label: filename }), timestamp(), myFormat, format.json() ), transports: [ // // - Write to all logs with level `info` and below to `combined.log` // - Write all logs error (and below) to `error.log`. // new transports.File({ filename: 'error.log', level: 'error' }), new transports.File({ filename: 'combined.log' }) ]});logger.info('Info string');logger.error('Error string');
Node.js Logging – How to Get Started - Papertrail (3)

Using Papertrail for Log Aggregation

Cloud-based log aggregation tools such as SolarWinds®Papertrailmake uncovering insights in your log data easy. Using Papertrail, you can collect all logs into asingle locationand use advancedsearchingandfilteringto spot trends and troubleshoot issues using your logs.

Papertrail also offerslive tailwhich lets you monitor your logs in real time and run filters against incoming messages by time period, origin, or message content.

To send logs to your Papertrail account, you can use the following code snippet:

require('winston-papertrail').Papertrail;var winstonPapertrail = new winston.transports.Papertrail({ host: 'logs.papertrailapp.com', port: 12345});var logger = new winston.Logger({ transports: [winstonPapertrail]});

Be sure to use thehostandportvalues from thePapertrail log destination settingspage of your account.

Conclusion

It’s quick to get started with Node.js logging thanks to its run-time support for console.log, console.error, and the third-party debug module. These options represent the path of least resistance for quick and dirty debugging. However, the benefits end there. If you need more flexible logging, a module such as Winston offers advanced features like logging levels, custom formats, and support for multiple logging destinations.

It may be easy to get started with logging in Node.js, but its real advantage is that it’s just a few more steps to a customized logging setup that’s suitable for even the most demanding environments.

Node.js Logging – How to Get Started - Papertrail (2024)
Top Articles
The five pillars of successful mentoring – AltusQ
Bread Market Size Report | Growth & Trends 2030
Oldgamesshelf
No Hard Feelings Showtimes Near Metropolitan Fiesta 5 Theatre
O'reilly's In Monroe Georgia
Pollen Count Central Islip
How Quickly Do I Lose My Bike Fitness?
Craigslist Chautauqua Ny
R Tiktoksweets
Rosemary Beach, Panama City Beach, FL Real Estate & Homes for Sale | realtor.com®
“In my day, you were butch or you were femme”
The Banshees Of Inisherin Showtimes Near Regal Thornton Place
Chic Lash Boutique Highland Village
Craigslist Panama City Fl
Dr Adj Redist Cadv Prin Amex Charge
Pekin Soccer Tournament
Air Force Chief Results
Unity - Manual: Scene view navigation
Msu 247 Football
Aris Rachevsky Harvard
Moving Sales Craigslist
Crawlers List Chicago
Long Island Jobs Craigslist
Schedule An Oil Change At Walmart
Apple Original Films and Skydance Animation’s highly anticipated “Luck” to premiere globally on Apple TV+ on Friday, August 5
EASYfelt Plafondeiland
zom 100 mangadex - WebNovel
Governor Brown Signs Legislation Supporting California Legislative Women's Caucus Priorities
Cpt 90677 Reimbursem*nt 2023
Ou Football Brainiacs
What Is Opm1 Treas 310 Deposit
Purdue Timeforge
Utexas Baseball Schedule 2023
What Is Xfinity and How Is It Different from Comcast?
Bt33Nhn
Justin Mckenzie Phillip Bryant
Strange World Showtimes Near Atlas Cinemas Great Lakes Stadium 16
T&J Agnes Theaters
Missouri State Highway Patrol Will Utilize Acadis to Improve Curriculum and Testing Management
Mta Bus Forums
3400 Grams In Pounds
Collier Urgent Care Park Shore
PruittHealth hiring Certified Nursing Assistant - Third Shift in Augusta, GA | LinkedIn
Puretalkusa.com/Amac
Discover Wisconsin Season 16
Paul Shelesh
Bekkenpijn: oorzaken en symptomen van pijn in het bekken
Hsi Delphi Forum
How To Win The Race In Sneaky Sasquatch
Houston Primary Care Byron Ga
Grandma's Portuguese Sweet Bread Recipe Made from Scratch
Public Broadcasting Service Clg Wiki
Latest Posts
Article information

Author: Arielle Torp

Last Updated:

Views: 5992

Rating: 4 / 5 (61 voted)

Reviews: 92% of readers found this page helpful

Author information

Name: Arielle Torp

Birthday: 1997-09-20

Address: 87313 Erdman Vista, North Dustinborough, WA 37563

Phone: +97216742823598

Job: Central Technology Officer

Hobby: Taekwondo, Macrame, Foreign language learning, Kite flying, Cooking, Skiing, Computer programming

Introduction: My name is Arielle Torp, I am a comfortable, kind, zealous, lovely, jolly, colorful, adventurous person who loves writing and wants to share my knowledge and understanding with you.