ChatOps на практике: создание бота для мониторинга логов

от автора

Недавно на работе меня попросили придумать рабочую задачку для студентов. Поскольку я работаю в инфраструктурной команде, мои повседневные задачи вряд ли подходят для их домашек или курсовых работ. Чтобы найти подходящую идею, я начал перебирать инструменты, которыми мы с командой часто пользуемся. Большинство из них интегрированы с чатами и ботами, и один из ключевых инструментов — это Алерт Бот. Он отслеживает логи и отправляет оповещения, если происходит что-то необычное. Это позволяет нам быстрее обнаруживать и устранять инциденты.

Когда я придумал эту задачу для студентов, мне пришло в голову, что подобный функционал может быть полезен многим. Возможно, кто-то сможет адаптировать это решение под свои нужды и облегчить себе жизнь, автоматизируя процесс мониторинга и реагирования на инциденты. Собственно этим я бы и хотел поделиться в своей статье.

Описание задачи

Итак, студентам предлагается разработать бота, который будет анализировать логи сервера и отправлять уведомления в Telegram, если обнаруживает ошибки или другие аномалии. Основные требования к проекту:

  1. Мониторинг логов: Бот должен периодически проверять указанный файл логов и искать в нем ошибки.

  2. Фильтрация данных: Необходимо отфильтровать важные сообщения (например, с уровнем ERROR или CRITICAL) и игнорировать менее значимые (DEBUG, INFO).

  3. Отправка уведомлений: При обнаружении ошибок бот должен отправлять сообщение в Telegram-чат.

  4. Конфигурируемость: Настройки, такие как путь к логам, частота проверки и идентификатор чата, должны быть легко изменяемы.

Архитектура решения

Наш бот будет состоять из следующих основных компонентов:

  1. Лог-файл: Файл, содержащий журналы событий сервера, который бот будет анализировать.

  2. Анализатор логов: Модуль, который читает файл логов и извлекает из него важные сообщения.

  3. Telegram-бот: Модуль, который отправляет найденные ошибки в указанный чат.

Эти компоненты должны взаимодействовать между собой, обеспечивая непрерывный мониторинг логов и оперативное уведомление о проблемах.

Генерация логов

Давайте начнем с моделирования деятельности сервера, чтобы у нас были логи для анализа. Мы создадим скрипт на Go, который будет генерировать логи с разными уровнями сообщений (например, INFO, WARNING, ERROR) в случайном порядке.

Предположим, что наш «сервер» будет периодически генерировать события с различными уровнями важности. Для этого напишем скрипт, который будет записывать логи в файл.

1. Создание функции для чтения и записи строчек в файл

Сначала создадим функции, которые будут считывать логи и писать их:

func readLines(fileName string) ([]string, error) {   file, err := os.Open(fileName)   if os.IsNotExist(err) {     return []string{}, nil   } else if err != nil {     return nil, err   }   defer file.Close()    var lines []string   scanner := bufio.NewScanner(file)   for scanner.Scan() {     lines = append(lines, scanner.Text())   }   return lines, scanner.Err() }  func writeLines(lines []string, fileName string) error {   file, err := os.Create(fileName)   if err != nil {     return err   }   defer file.Close()    writer := bufio.NewWriter(file)   for _, line := range lines {     fmt.Fprintln(writer, line)   }   return writer.Flush() }

2. Функции для генерации логов

Теперь создадим функции, которые будут генерировать нам непосредственно сами логи

var (   logLevels = []string{"DEBUG", "INFO", "ERROR"}   mu        sync.Mutex )  func generateLog() {   mu.Lock()   defer mu.Unlock()    lines, err := readLines(logFileName)   if err != nil {     fmt.Println("Error reading log file:", err)     return   }    if len(lines) >= maxLines {     lines = lines[len(lines)-maxLines+1:]   }    logLine := fmt.Sprintf("%s [%s] %s\n", time.Now().Format(time.RFC3339), logLevels[rand.Intn(len(logLevels))], generateRandomMessage())   lines = append(lines, logLine)    err = writeLines(lines, logFileName)   if err != nil {     fmt.Println("Error writing to log file:", err)   } }  func generateRandomMessage() string {   messages := []string{     "User logged in",     "File uploaded",     "Error processing request",     "User logged out",     "Database connection established",     "Invalid input received",   }   return messages[rand.Intn(len(messages))] }

3. Основная функция для генерации логов

Теперь объединим всё вместе в основной функции, которая будет периодически генерировать и записывать лог-сообщения:

const (   logFileName = "logs.log"   maxLines    = 200 )  func main() {   rand.Seed(time.Now().UnixNano())    go func() {     for {       generateLog()       time.Sleep(time.Millisecond * 100)     }   }()    select {} }

Важно помнить, что логи не должны храниться бесконечно. Со временем их количество может значительно возрасти, что приведет к увеличению объема файла и возможным проблемам с производительностью. Чтобы этого избежать, следует установить разумные ограничения на количество сохраняемых записей. В данном примере мы ограничиваемся хранением последних 200 логов

Продолжение разработки бота на Go

Теперь, когда у нас есть сгенерированные логи, приступим к разработке бота, который будет их анализировать и отправлять уведомления в Telegram. Ниже описаны основные шаги для создания такого бота.

1. Установка необходимых библиотек

Для работы с Telegram и анализа логов нам потребуются несколько внешних библиотек. В Go это можно сделать с помощью go get:

go get -u github.com/go-telegram-bot-api/telegram-bot-api/v5 

Эта библиотека предоставит нам интерфейс для взаимодействия с Telegram.

2. Чтение и анализ логов

Первый шаг в нашем боте — это чтение логов и поиск в них ошибок. Мы будем использовать функцию для чтения файла и фильтрации важных сообщений.

package main  import (     "bufio"     "fmt"     "os"     "strings"     "time" )  func readLogs(logFilePath string) ([]string, error) {     file, err := os.Open(logFilePath)     if err != nil {         return nil, err     }     defer file.Close()      var importantLogs []string     scanner := bufio.NewScanner(file)      for scanner.Scan() {         logLine := scanner.Text()         if strings.Contains(logLine, "ERROR") || strings.Contains(logLine, "CRITICAL") {             importantLogs = append(importantLogs, logLine)         }     }      if err := scanner.Err(); err != nil {         return nil, err     }      return importantLogs, nil } 

3. Отправка уведомлений в Telegram

Следующим шагом будет отправка уведомлений в Telegram. Для этого воспользуемся библиотекой telegram-bot-api.

package main  import (     "log"     "github.com/go-telegram-bot-api/telegram-bot-api/v5" )  func sendTelegramNotification(bot *tgbotapi.BotAPI, chatID int64, message string) {     msg := tgbotapi.NewMessage(chatID, message)     _, err := bot.Send(msg)     if err != nil {         log.Printf("Error sending message: %v", err)     } } 

4. Основная функция для мониторинга логов и отправки уведомлений

Теперь объединим всё вместе в основной функции. Она будет периодически проверять логи и отправлять уведомления, если обнаружены ошибки.

package main  import (     "fmt"     "log"     "time"     "os"      "github.com/go-telegram-bot-api/telegram-bot-api/v5" )  func main() {     logFilePath := "server.log"     botToken := "YOUR_TELEGRAM_BOT_TOKEN"     chatID := int64(YOUR_CHAT_ID)      bot, err := tgbotapi.NewBotAPI(botToken)     if err != nil {         log.Panic(err)     }      log.Printf("Authorized on account %s", bot.Self.UserName)      for {         importantLogs, err := readLogs(logFilePath)         if err != nil {             log.Printf("Error reading log file: %v", err)             continue         }          for _, logMessage := range importantLogs {             sendTelegramNotification(bot, chatID, logMessage)         }          time.Sleep(10 * time.Second) // Проверяем логи каждые 10 секунд     } } 

5. Конфигурируемость

Для удобства можно вынести параметры, такие как путь к логам, токен бота и идентификатор чата, в конфигурационный файл или использовать переменные окружения. Это упростит адаптацию бота к разным условиям эксплуатации.

Пример использования переменных окружения:

package main  import (     "os"     "log" )  func main() {     logFilePath := os.Getenv("LOG_FILE_PATH")     botToken := os.Getenv("TELEGRAM_BOT_TOKEN")     chatID := os.Getenv("TELEGRAM_CHAT_ID")      if logFilePath == "" || botToken == "" || chatID == "" {         log.Fatal("Missing necessary environment variables")     }      // Остальной код... } 

Теперь, когда бот готов, его можно развернуть на сервере или в облаке и использовать для мониторинга логов в реальных условиях. Опытные разработчики, вероятно, уже знакомы с подобными задачами, но надеюсь, что эта статья оказалась полезной для тех, кто только начинает свой путь в программировании, работает над своими пет-проектами или делает первые шаги в индустрии. Создание таких инструментов — отличный способ приобрести навыки, которые пригодятся в реальной работе.


ссылка на оригинал статьи https://habr.com/ru/articles/835756/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *