How to Log Application Errors Using a Telegram Bot
A lightweight, free alternative to paid error tracking tools. Learn how to send Rails application errors directly to your Telegram chat using a bot and a few lines of code.
Error logging is one of those things every application needs, yet it’s often a pain to set up, especially for side projects or MVPs. Paid tools like Sentry or Appsignal are great, but they can feel like overkill when you just want something lightweight and free.
That’s where Telegram comes in. With just a few lines of code, you can set up a Telegram bot to send error logs directly to your chat. It’s fast, simple, and surprisingly handy for catching issues in real time.
Step 1: Set up your Telegram bot
If you don’t already have one, sign up for a Telegram account and create a bot.
- Open Telegram and start a chat with
@BotFather(this is the official Telegram bot for creating and managing bots). - Run the command
/newbotand follow the prompts. - Once your bot is created, BotFather will give you an API token. Save it to use later in your application.
- You’ll also need the chat ID of the conversation where your bot will post messages (this could be a private chat or a group). To find it:
- Add your bot to a chat or group.
- Send any message in that chat.
- Visit the URL below (replace
{API_TOKEN}with your bot’s token) and look for the"chat":{"id": ...field — this is your chat ID.https://api.telegram.org/bot{API_TOKEN}/getUpdates
Step 2: Add your environment variables
In your Rails app, add two environment variables to your .env file (or your deployment secrets) with the values from step 1.
TELEGRAM_BOT_TOKEN=your_bot_token_here
TELEGRAM_CHAT_ID=your_chat_id_here
Step 3: Add the gem
Add the telegram-bot-ruby gem to your Gemfile and install it.
bundle add telegram-bot-ruby
Step 4: Create a notification service
Now we’ll create a service class that handles sending messages to Telegram. This class will take care of formatting the message and posting to the chat.
# lib/telegram/notification.rb
require "telegram/bot"
class Telegram::Notification
TELEGRAM_MESSAGE_LIMIT = 4096 # Ensures we don’t exceed Telegram’s message length restriction.
TELEGRAM_BOT_TOKEN = ENV["TELEGRAM_BOT_TOKEN"]
TELEGRAM_CHAT_ID = ENV["TELEGRAM_CHAT_ID"]
def send_backend_error(user_email:, exception_class_name:, error_message:, class_name:)
message = build_error_log(
type: "Backend Error",
user_email: user_email,
class_name: class_name,
messages: [exception_class_name, error_message]
)
deliver(message)
end
def send_frontend_error(user_email:, error:, message:)
message = build_error_log(
type: "Frontend Error",
user_email: user_email,
messages: [message, error]
)
deliver(message)
end
private
def build_error_log(type:, user_email:, class_name: nil, messages: [])
message_parts = []
message_parts << "[#{type}]"
message_parts << "- [User: #{user_email || "Unknown"}]"
message_parts << "[#{class_name}]" if class_name
message_parts << messages.compact.join(": ")
message_parts.join(" ").slice(0, TELEGRAM_MESSAGE_LIMIT)
end
def deliver(message)
if Rails.env.production?
if TELEGRAM_BOT_TOKEN.blank? || TELEGRAM_CHAT_ID.blank?
Rails.logger.error("Telegram Bot token or chat ID not configured")
return
end
begin
Telegram::Bot::Client.run(TELEGRAM_BOT_TOKEN) do |bot|
bot.api.send_message(chat_id: TELEGRAM_CHAT_ID, text: message)
end
rescue => e
Rails.logger.error("Failed to send Telegram message: #{e.message}")
end
else
Rails.logger.info(message)
end
end
end
Step 5: Set up background jobs
Ideally, we want to post errors to Telegram asynchronously. So let’s add two background jobs — one for frontend errors and one for backend errors — to call the service.
# app/jobs/telegram/frontend/notification_job.rb
class Telegram::Frontend::NotificationJob < ApplicationJob
queue_as :default
def perform(user_email:, details:, title:)
Telegram::Notification.new.send_frontend_error(
user_email: user_email,
error: details,
message: title
)
end
end
# app/jobs/telegram/backend/notification_job.rb
class Telegram::Backend::NotificationJob < ApplicationJob
queue_as :default
def perform(user_email:, error_message:, exception_class_name:, class_name:)
Telegram::Notification.new.send_backend_error(
user_email: user_email,
exception_class_name: exception_class_name,
error_message: error_message,
class_name: class_name
)
end
end
Step 6: Capture frontend errors
For frontend issues, we’ll create a simple controller to receive error reports from the browser.
# app/controllers/errors_controller.rb
class ErrorsController < ApplicationController
def create
params = JSON.parse(request.body.read)
Telegram::Frontend::NotificationJob.perform_later(
user_email: current_user&.email,
title: params["title"],
details: params["details"]
)
render json: { status: true }
end
end
# config/routes.rb
resources :errors, only: [:create]
Then, in your JavaScript, wire up an error handler to post errors to that controller.
# app/javascript/application.js
import "@hotwired/turbo-rails"
import "controllers"
if (window.Stimulus) {
Stimulus.handleError = (error, message, detail) => {
// Log error to console
console.error(`${message}\n\n`, error, detail);
// Post error to server
fetch("/errors", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').getAttribute('content')
},
body: JSON.stringify({
title: message,
details: `${detail.identifier} - ${error.message}`
})
});
};
}
Whenever a frontend error occurs, this will send a notification to your Telegram chat.
Step 7: Capture backend errors
For backend exceptions, we can use a concern that catches errors globally and queues the notification job.
# app/controllers/concerns/error_handling.rb
module ErrorHandling
extend ActiveSupport::Concern
included do
rescue_from Exception do |exception|
Telegram::Backend::NotificationJob.perform_later(
user_email: current_user&.email,
exception_class_name: exception.class.name,
error_message: exception.message,
class_name: self.class.name
)
raise exception
end
end
end
class ApplicationController < ActionController::Base
include ErrorHandling
end
This ensures that even unexpected exceptions are logged to Telegram, while still being re-raised so your app behaves normally.
Wrapping up
And that’s it! Your Rails application can now send error logs straight to Telegram. It’s quick to set up, free to run, and perfect for side projects or MVPs. Now you'll get a message whenever one of your users runs into an error.
Here's a sample pull request for the full code snippet.