Setting SMS server alerts with MessageBird

⏱ 30 min build time || Download the Code

Why build SMS server alerts?

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 Primer with Winston

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).

Getting started

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

Building a MessageBird transport

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:

  • A functioning MessageBird API key.
  • An originator, that is, a sender ID for the messages.
  • One or more recipients, that is, the phone numbers of the system engineers that should be informed about problems with the server.

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 SDK
this.messagebird = require('messagebird')(opts.apiKey);
// Store required options
this.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 entry
var 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 SDK
this.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).

Configuring Winston and our Transport

In index.js, the primary file of our application, we start off by loading the dependencies and the custom transport class:

// Load dependencies
var 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 file
require('dotenv').config();

Copy env.example to .env and store your information:

MESSAGEBIRD_API_KEY=YOUR-API-KEY
MESSAGEBIRD_ORIGINATOR=Winston
MESSAGEBIRD_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 Logger
var 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:

  • The default Console transport, where we log everything starting with the debug level.
  • A default File transport, where we log info and higher into a file called app.log.
  • Our previously created custom MessageBirdTransport with all the configuration options taken from our environment file. We convert the comma-separated recipients into an array with split(','). This transport only handles log events with the error level.

Configuring Winston for Express

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 express
app.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.

Testing

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:

  • Four messages printed on the console.
  • Three log items written to the app.log file (open it with a text editor or with tail in a new console tab).
  • One error message on your phone.

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!

Start building!

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!

Questions?

We’re always happy to help with code or other doubts you might have! Check out our Quickstarts, API Reference, Tutorials, SDKs, or contact our Support team.

Cookie Settings