In this MessageBird Developer Tutorial, you’ll learn how to quickly report error logs of your server via SMS server alerts with the MessageBird API to ensure a faster response time.
For any online service advertising guaranteed uptime north of 99%, being available and reliable is extremely important; therefore, it is essential that any errors in the system are fixed as soon as possible, and the prerequisite for that is that error reports are delivered quickly to the engineers on duty. Providing those error logs via SMS ensures a faster response time compared to email reports and helps companies keep their uptime promises.
We’ll show you how to build an integration of SMS alerts into a Node.js application that uses the Winston logging framework.
Logging is the default approach for gaining insights into running applications. Before we start building our sample application, let's take a minute to understand two fundamental concepts of logging: levels and transports.
Levels indicate the severity of the log item. Common log levels are debug, info, warning and error. For example, a user trying to login could have the info level, a user entering the wrong password during login could be a warning since it may be a potential attack, and a user not able to access the system due to a subsystem failure would trigger an error.
Transports are different channels into which the logger writes its data. Typical channels are the console, files, log collection servers and services, or communication channels such as email, SMS or push notifications.
It's possible and common to set up multiple kinds of transport for the same logger but set different levels for each. In our sample application, we write entries of all severities to the console and a log file; the application will send SMS notifications only for log items that have the error level (or higher, when using more levels).
First things first, the sample application is built in Node.js and uses Winston as the logging library, so you’ll need Node and npm, you can easily install them for free.
We have also included an example using Express and express-winston (don't confuse it with winston-express...) to demonstrate web application request logging.
The source code is available in the MessageBird Developer Tutorials GitHub repository which you can either clone with git or from where you can download a ZIP file with the source code to your computer.
Let's now open the directory where you've stored the sample code and run the following command to install the MessageBird SDK and other dependencies:
npm install
Winston enables developers to build custom transports and use them with the logger just like built-in transports such as the file or console transports. They are extensions of the Transport class and need to implement a constructor for initialization as well as the log() method. We have created one in the file MessageBirdTransport.js.
Our SMS alert functionality needs the following information to work:
To keep the custom transport self-contained and independent from the way the application wants to provide the information, we take all as parameters in our constructor. Here's the code:
const Transport = require('winston-transport');/*** This is a MessageBird Transport for Winston*/module.exports = class MessageBirdTransport extends Transport {constructor(opts) {super(opts);// Load and initialize MessageBird SDKthis.messagebird = require('messagebird')(opts.apiKey);// Store required optionsthis.recipients = opts.recipients;this.originator = opts.originator;}
As you can see, the constructor calls the super()-constructor to keep basic custom transport behavior intact, then loads and initializes the MessageBird SDK with the key and stores the other the necessary configuration fields as members of the object.
In the log() method, again we start with some default code from the basic custom transport class:
log(info, callback) {setImmediate(() => {this.emit('logged', info);});
Then, we shorten the log entry, to make sure it fits in the 160 characters of a single SMS so that notifications don't incur unnecessary costs or break limits:
// Shorten log entryvar text = (info.message.length > 140) ? info.message.substring(0, 140) + ' ...' : info.message;
Finally, we call messagebird.messages.create() to send an SMS notification. For the required parameters originator and recipients we use the values stored in the constructor, and for body we use the (shortened) log text prefixed with the level:
// Send notification with MessageBird SDKthis.messagebird.messages.create({originator : this.originator,recipients : this.recipients,body : '[' + info.level +'] ' + text}, function(err, response) {console.log(err, response);});
The MessageBird API call is asynchronous and uses a callback function. In this callback function we only log the response to the console and don't do anything else (we can't record it with Winston here because then we might get stuck in an infinite loop).
In index.js, the primary file of our application, we start off by loading the dependencies and the custom transport class:
// Load dependenciesvar winston = require('winston');var express = require('express');var expressWinston = require('express-winston');var MessageBirdTransport = require('./MessageBirdTransport');
Let’s also use dotenv to load configuration data from a .env file:
// Load configuration from .env filerequire('dotenv').config();
Copy env.example to .env and store your information:
MESSAGEBIRD_API_KEY=YOUR-API-KEYMESSAGEBIRD_ORIGINATOR=WinstonMESSAGEBIRD_RECIPIENTS=31970XXXXXXX,31970YYYYYYY
You can create or retrieve an API key in your MessageBird account. The originator can be a phone number you registered through MessageBird or, for countries that support it, an alphanumeric sender ID with at most 11 characters. You can provide one or more comma-separated phone numbers as recipients. Keep in mind that alphanumeric senders are not supported in every country including the United States, so it’s important to check the country restrictions. If you can't use alphanumeric IDs, use a real phone number instead. You can check our originator article in Help Center to learn more about this topic.
Back to index.js, it's time to set up the logger:
// Set up Loggervar logger = winston.createLogger({format: winston.format.simple(),transports: [new winston.transports.Console({level: 'debug'}),new winston.transports.File({filename: 'app.log',level: 'info'}),new MessageBirdTransport({apiKey : process.env.MESSAGEBIRD_API_KEY,originator : process.env.MESSAGEBIRD_ORIGINATOR,recipients : process.env.MESSAGEBIRD_RECIPIENTS.split(','),level: 'error'})]});
The winston.createLogger() method takes a variety of optional configuration parameters. Using the transports parameter, you can define one or more transports. As you see in the example, we have added three transports:
After setting up an Express app, you can call app.use() to specify a middleware. Middlewares are extensions to Express that touch each request, and they are useful for globally required functionality such as authentication or, in our example, logging:
// Configure Winston logging for expressapp.use(expressWinston.logger({statusLevels : true,winstonInstance : logger}));
We provide the previously initialized winstonInstance _logger, so the same logger is used for automated Express request logging and custom log entries. The statusLevels parameter enables a built-in behavior of express-winston that logs requests that report a server error, that is, have response codes in the 5xx range with error level, and uses the info level for successful requests with a 2xx response code. This behavior is fully in line with our intention since we want to report only server errors through our MessageBirdTransport.
You’re done! We have added some test log entries in index.js and we also created an Express test route to simulate a 500 server response. It’s time to test your application! Go to your console and type the following command:
node index.js
You should see:
Open http://localhost:8080/ in your browser, and along with the successful request, you’ll see a log entry on the console and in the file.
Open http://localhost:8080/simulateError and, along with the request error on your console and the log file, another notification will arrive at your phone.
You can now take these elements and integrate them into a Node.js production application. Don't forget to download the code from the MessageBird Developer Tutorials GitHub repository.
Nice work! 🎉
You've learned how to log with Winston and express-winston to create a custom MessageBird transport using Node.js!
Want to build something similar but not quite sure how to get started? Feel free to let us know at support@messagebird.com; we'd love to help!