NodeJs uses Telegram to track bugs and send event notifications
Introduction
In this article we are going to set up a little nodejs environment that will notify every critical event to our Telegram channel. Not only critical events can be sent to telegram but also payment notifications, support requests, etc. We know that we need a simple way to log and notify everything on a simple manner. That’s why we are going to overwrite to default console functions and add some add custom console.notify
function. This way you will be alerted every time an event happens.
At bit4you.io we use a lot of different technologies depending on the needs. We mainly develop in GoLang and JavaScript. Our front-end has been developed using VueJs so of course NodeJs was a big need to have in our company. bit4you.io is a crypto assets exchange offering market pairs with the most common used crypto’s today (Bitcoin, Ethereum, Ripple, EOS, …). Everyone working with blockchain technologies will tell you that open sourcing the codes is the key. That’s why we want to share a little bit of our experience with you today :)
We need to be alerted on every single event that happens on bit4you.io, for instance when our stocks are too low, when some suspect activity happens, when an unknown bug happens etc. So we decided to write a little code helping us monitoring everything and send notifications to our Telegram channel. Today we are going to simplify this code and rewrite it in order to be compatible with other projects.
Getting started with NodeJs and telegram
We are lucky today because NodeJS has a lot of packages. We are going to use the package named “telegraf” which will allow us to create a Telegram bot.
So first of all you will need to initiate a new project, in our example we will name it “monitor-sample”. Open your terminal into the directory where you want to create your project and create it in an empty directory using the following commands.
As you can see, we just created an empty directory and initiated our node project. We have also directly installed the telegraf package because we know we will need it into our project.
So let’s try out this package by sending a simple helloworld to telegram !
As you can see on the samples provided by this package on their page (https://www.npmjs.com/package/telegraf), we need a Telegram token in order to continue. So the question is, how can we obtain this token ??
Create a telegram api token
Well, just open the BotFather channel on Telegram and send him this message/command: /newbot
. The BotFather will instantly require you to provide a name for your bot, you just need to answer him with the name of the desired chat room, for instance bit4you.io test
. Then we are going to chose the username for the bot, this username has to end with bot. Maybe we can call him bit2018Bot
. Now that the BotFather generated an api token, you can use it to setup telegraf. Don’t forget to click on the link of the channel showed in the last message received from the BotFather. This link will allow you to join the channel and receive your notifications.
Use the api token (securly) in our project
So let’s try out our first bot following the sample provided by the telegraf package. In their sample we can see that an environment variable called BOT_TOKEN is used in order to instantiate our bot object. That’s not a wrong idea at all! We highly recommend to not keep any kind of api key / password / authentication config anywhere in your code ! Why not ? Well that’s a very bad idea since everyone of your organisation will have access to your servers and will be able to send for instance notifications to your telegram when they are not authorized. Your authentication tokens will also been present in your git history. So if someone get access to your git, you just give him all the accesses he needs to your full infrastructure. Forget the time where you stored your credentials into your code and let’s use some smarter methods. You can use environment variables or even create some encrypted files that are only available into your production infrastructure. If you’re using Kubernetes, you can easily store your secrets in your cluster and mount it into your container. This way you can use a local config file while you’re in development phase with some test credentials and only use your production credentials when the release is pushed online.
Graahhh, we are not here to discuss about security so let’s try to run our first sample. We are creating a file “telegram.js” with the following code:
const Telegraf = require('telegraf')
const bot = new Telegraf(process.env.BOT_TOKEN)bot.start((ctx) => ctx.reply('Welcome to the new crypto world!'))
bot.hears('hi', (ctx) => ctx.reply('bit4you.io is awesome'))
bot.startPolling()console.log('Telegram bot started.')
Chatting with our bot
When we run this code using node telegram.js
, we can see that the node process remains up and running. When you go back to the Telegram channel and send a simple hi
message, you will get an instant answer of your bot. So that’s nice because it means that our bot correctly works and that we can start sending some messages to telegram every time a particular event happens !
But wait ? This package only acts as a bot which could answer to your questions. That’s nice because you can query server stats or anything else from your server, but what if we want to push notifications without waiting your interaction ? At b, we have multiple channels where we wants to be able to send notifications at any time. Therefore we need to first get the channel id so we can send notifications at any time without waiting any activity from anyone’s side.
Get the channel id
You will need to replace the following line: bot.hears('hi', (ctx) => ctx.reply('bit4you.io is awesome'))
with a small code showing the chat id every time someone says hi. This way we can initiate a chat channel that will be reused for every push notification by sending hi from our device to the bot. You can just copy and paste this code to replace the previous one:
bot.hears(‘hi’, (ctx) => {
console.log(‘ctx:’, ctx.update.message.chat.id)
ctx.reply(‘bit4you.io is awesome on channel: ’ + ctx.update.message.chat.id)
})
After you restarted your node process, open Telegram on your bot’s channel and send ‘hi’. The bot will send you back the channel id that can be used in the future. You can also invite other members of your team to join this existing channel.
Now your bot is able to listen to new channels and answer to received commands on that channel. For instance you could set up some commands to get the number of registered users, how many users are connected etc… But you’re also able to push notifications to a particular channel without waiting any message from that channel.
How can we send a message to the created channel ?
You can test sending messages to your new channel by adding the following assyc code. This one is going to send a simple hello world to your channel.
bot.telegram.sendMessage(process.env.CHAT_ID, ‘hello world from bit4you.io’).catch(console.error);
once you added this line at the end of your code, you can run your code and you will receive a message on your telegram channel. That means that we can start pushing notifications every time a critical event happens. For instance on bit4you.io we’re notified when our APIs are slowing down, or when our stocks are to low.
creating console hooks
When you call a code that’s making an HTTP request, you always face the possibility that an error may occur. It may be a problem with your connectivity, or telegram has a problem, or even something else ? Well those problems do not have to affect your code. Most of time you don’t want your app to crash just because a notification has not been send correctly. You also don’t want to slow done your api’s just because you are sending notifications to your telegram channel.
That’s why we created async hooks that send to notification without slowing down our api’s and without risking any unexpected exception when executing the code.
First of all let’s create a custom console.notify
function that will work like the normal console.log
function, but will additionally to this send the logged message to telegram. We can use this function in the future to notify events like hi a new payment has been done
easily to our telegram channel.
//Create a telegram log transformer function
function logToTelegram() {
//Map every argument to a string
const msg = Object.values(arguments).map((msg) => {
if(typeof(msg) === “object”) {
return msg.stack ? msg.stack : JSON.stringify(msg)
} else {
return String(msg)
}
}).join(“ “); //Send to mapped arguments to telegram
return bot.telegram.sendMessage(chat_id, msg);
}//Add notify function to the console object
console.notify = function() {
//Catch every exception because we don’t want exception to affect our code
try {
//Log the given message to the console
console.log(…arguments);
logToTelegram(…arguments).catch(console.error);
} catch(e) {
console.error(e)
}
}//Test our code
console.notify('Hello bit4you.io', new Error("test"))
Once you have added this code to your product, you can test your code again node telegram.js
. You will see the log appearing in your console and on your telegram channel :) It’s a very easy trick, but it allows us to notify everything on a comfortable way with only one line of code.
Now it’s also nice to log every error log and uncaught exception. Depending on how your project has been designed, an error log could be critical. At bit4you.io if a error log is used, it really means that something is going wrong and that we need to check that ASAP. In other cases we will use a warning or debug log. We are going to overwrite to default error log function with our own one.
Overwriting error log:
//Overwrite the console.error function
const standardErrorLog = console.error.bind(console);
console.error = function() {
try {
//Log the given message to the console
standardErrorLog(…arguments);
logToTelegram(‘🔺’, …arguments).catch(console.error);
} catch(e) {
console.error(e)
}
}//Test our code
console.error(“bit4you.io critical error test!”)
Capture all uncaught exceptions
//Capture every uncaught exception
process.on('uncaughtException', (err) => {
//Simple log it to our console
console.error(err)
});
What’s our project able to do right now ?
We have just written a simple code that needs to be setup only once somewhere in project and that will be accessible everywhere through the standard console
object. All the exceptions that could crash your project are logged into your terminal and sent to telegram, additionally to this every error that is logged, will been sent to telegram ! You can play with console.notify
and console.error
to notify everything in your dedicated telegram channel !
This functionalities gives you an amazing logging/notifications flexibility that can be used for a fast tracking of bugs and events.
What’s next
Now that you have an awesome notification and tracking ability into your project, you will need to setup a more advanced tracking tool like sentry.io. Telegram is only used to be alerted every-time something happens. In our next chapter we will also describe how to connect your NodeJS project with Prometheus and Grafana. You will be able to follow up all the statistics of your project in your Grafana dashboard. We will also link Grafana alerts to our Telegram channel.
Stay connected for future guides and don’t forget to take a look at bit4you.io
Thank you :)