Last active
August 17, 2022 14:29
-
-
Save alfonmga/18e3211ee0cf2b00e894225bd1bc8070 to your computer and use it in GitHub Desktop.
Logrus -> Telegram
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package logrus2telegram | |
import ( | |
"fmt" | |
"time" | |
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" | |
"github.com/sirupsen/logrus" | |
"errors" | |
"net/http" | |
) | |
// TelegramBotHook is a hook for Logrus logging library to send logs directly to Telegram. | |
type TelegramBotHook struct { | |
levels []logrus.Level | |
notifyOn map[logrus.Level]struct{} | |
client *http.Client | |
token string | |
parseMode string // <https://core.telegram.org/bots/api#formatting-options> | |
chatIDs []int64 | |
format func(e *logrus.Entry) (string, error) | |
requestTimeout time.Duration | |
} | |
// config for hook. | |
type config struct { | |
levels []logrus.Level | |
notifyOn map[logrus.Level]struct{} | |
requestTimeout time.Duration | |
format func(e *logrus.Entry) (string, error) | |
client *http.Client | |
} | |
// Option configures the hook instance. | |
type Option func(*config) error | |
// NotifyOn enables notification in messages for specified log levels. | |
func NotifyOn(levels []logrus.Level) Option { | |
return func(h *config) error { | |
if len(levels) < 1 { | |
return errors.New("at least one level for notification is required") | |
} | |
for _, level := range levels { | |
h.notifyOn[level] = struct{}{} | |
} | |
return nil | |
} | |
} | |
// Levels allows to specify levels for the hook. | |
func Levels(levels []logrus.Level) Option { | |
return func(h *config) error { | |
if len(levels) < 1 { | |
return errors.New("at least one level is required") | |
} | |
h.levels = levels | |
return nil | |
} | |
} | |
// Format specifies the format function for the log entry. | |
func Format(format func(e *logrus.Entry) (string, error)) Option { | |
return func(h *config) error { | |
if format == nil { | |
return errors.New("the format function is nil") | |
} | |
h.format = format | |
return nil | |
} | |
} | |
// RequestTimeout specifies HTTP request timeout to Telegram API. | |
func RequestTimeout(requestTimeout time.Duration) Option { | |
return func(h *config) error { | |
if requestTimeout < 0 { | |
return errors.New("the request timeout must be positive") | |
} | |
h.requestTimeout = requestTimeout | |
return nil | |
} | |
} | |
func defaultFormat(entry *logrus.Entry) (string, error) { | |
m, err := entry.String() | |
if err != nil { | |
return "", fmt.Errorf("failed to serialize log entry: %w", err) | |
} | |
return m, nil | |
} | |
// NewHook creates new hook instance. | |
func NewHook(token string, chatIDs []int64, parseMode string, options ...Option) (*TelegramBotHook, error) { | |
if len(chatIDs) < 1 { | |
return nil, errors.New("at least one chatID is required") | |
} | |
c := &config{ | |
levels: []logrus.Level{logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel, logrus.WarnLevel, logrus.InfoLevel}, | |
format: defaultFormat, | |
notifyOn: make(map[logrus.Level]struct{}), | |
requestTimeout: 3 * time.Second, | |
client: &http.Client{}, | |
} | |
for _, option := range options { | |
err := option(c) | |
if err != nil { | |
return nil, err | |
} | |
} | |
return &TelegramBotHook{ | |
client: c.client, | |
token: token, | |
chatIDs: chatIDs, | |
format: c.format, | |
notifyOn: c.notifyOn, | |
levels: c.levels, | |
requestTimeout: c.requestTimeout, | |
parseMode: parseMode, | |
}, nil | |
} | |
// Fire sends the log entry to Telegram. | |
func (h *TelegramBotHook) Fire(entry *logrus.Entry) error { | |
text, err := h.format(entry) | |
if err != nil { | |
return fmt.Errorf("failed to format log entry: %w", err) | |
} | |
bot, err := tgbotapi.NewBotAPI(h.token) | |
if err != nil { | |
return fmt.Errorf("failed to create Telegram bot: %w", err) | |
} | |
for _, chatID := range h.chatIDs { | |
msg := tgbotapi.NewMessage(chatID, text) | |
msg.ParseMode = h.parseMode | |
msg.DisableNotification = !h.notify(entry.Level) | |
_, err := bot.Send(msg) | |
if err != nil { | |
return fmt.Errorf("failed to send message to chat %d: %w", chatID, err) | |
} | |
} | |
return nil | |
} | |
func (h *TelegramBotHook) notify(l logrus.Level) bool { | |
if len(h.notifyOn) == 0 { | |
return true | |
} | |
if _, notify := h.notifyOn[l]; notify { | |
return true | |
} | |
return false | |
} | |
// Levels define on which log levels this hook would trigger. | |
func (h *TelegramBotHook) Levels() []logrus.Level { | |
return h.levels | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment