13 KiB
Шаг 1: Создание Telegram-бота Зарегистрируйте бота через BotFather:
Откройте Telegram и найдите @BotFather. Отправьте команду /start. Отправьте /newbot, чтобы создать нового бота. Следуйте инструкциям: Укажите имя бота (например, MyLogBot). Укажите username бота, заканчивающийся на bot (например, @MyLogAllBot). После создания вы получите токен (например, 8007457609:AAGnrXOSaoxODMSh4yCzUEIp0uxfH3Hk8ws). Сохраните его, он понадобится для API.
Настройка Vector для отправки логов в Gotify
Эта инструкция описывает, как установить Vector, настроить его для чтения JSON-логов из файла /opt/org_ccalm_main/logs/ccalm.log, фильтрации строк с уровнем ERROR и отправки уведомлений в Gotify.
0. ✅ Подключаемся к инфраструктуре
ssh ubuntu@almaty.ccalm.org -p 22
Предварительные требования
- ОС: Ubuntu/Debian (для других ОС уточните, чтобы адаптировать команды).
- Файл логов:
/opt/org_ccalm_main/logs/ccalm.logс JSON-строками (поляlevelиmessage). - Gotify: Доступный сервер Gotify с URL и токеном (например,
https://gotify.example.com/message?token=<your-token>). - Доступ к терминалу с правами
sudo.
Шаг 1: Установка Vector
Пробуем скачать deb пакет с сайта
curl -L https://packages.timber.io/vector/0.46.X/vector_0.46.1-1_amd64.deb -o vector_0.46.1-1_amd64.deb &&
sudo dpkg -i vector_0.46.1-1_amd64.deb &&
sudo apt-get install -f &&
vector --version
That make deleting:
sudo apt remove --purge vector
Шаг 2: Создание конфигурации Vector
Vector использует YAML для конфигурации. Настроим чтение логов, фильтрацию ERROR и отправку в Gotify.
- Создайте файл конфигурации
/etc/vector/vector.yaml:
cd /etc/vector &&
sudo tee vector.yaml > /dev/null <<'EOF'
data_dir: "/var/lib/vector"
sources:
ccalm_logs:
type: file
include:
- /opt/org_ccalm_main/logs/org-ccalm-main.log
read_from: beginning
ccalm_translation_logs:
type: file
include:
- /opt/org_ccalm_translation/logs/org-ccalm-translation.log
read_from: beginning
ccalm_dbms_logs:
type: file
include:
- /opt/org_ccalm_dbms/logs/org-ccalm-dbms.log
read_from: beginning
ccalm_jwt_logs:
type: file
include:
- /opt/org_ccalm_jwt/logs/org-ccalm-jwt.log
read_from: beginning
transforms:
parse_json_ccalm:
type: remap
inputs:
- ccalm_logs
source: |
structured, err = parse_json(.message)
if err != null {
abort
}
merged, err = merge(., structured)
if err != null {
abort
}
. = merged
parse_json_translation:
type: remap
inputs:
- ccalm_translation_logs
source: |
structured, err = parse_json(.message)
if err != null {
abort
}
merged, err = merge(., structured)
if err != null {
abort
}
. = merged
parse_json_dbms:
type: remap
inputs:
- ccalm_dbms_logs
source: |
structured, err = parse_json(.message)
if err != null {
abort
}
merged, err = merge(., structured)
if err != null {
abort
}
. = merged
parse_json_jwt:
type: remap
inputs:
- ccalm_jwt_logs
source: |
structured, err = parse_json(.message)
if err != null {
abort
}
merged, err = merge(., structured)
if err != null {
abort
}
. = merged
filter_errors_ccalm:
type: filter
inputs:
- parse_json_ccalm
condition: '.level == "ERROR" || .level == "WARN"'
filter_errors_translation:
type: filter
inputs:
- parse_json_translation
condition: '.level == "ERROR" || .level == "WARN"'
filter_errors_dbms:
type: filter
inputs:
- parse_json_dbms
condition: '.level == "ERROR" || .level == "WARN"'
filter_errors_jwt:
type: filter
inputs:
- parse_json_jwt
condition: '.level == "ERROR" || .level == "WARN"'
format_telegram_json_ccalm:
type: remap
inputs:
- filter_errors_ccalm
source: |
msg, err = string(.message)
if err != null {
msg = "Unable to parse message"
}
marker_str = ""
if exists(.marker) {
m, err = string(.marker)
if err == null && length(m) > 0 {
marker_str = "Marker: \n```text\n" + m + "\n```\n"
}
}
level_str = ""
icon_str = ""
if exists(.level) {
m, err = string(.level)
if err == null {
level_str = "level: \n```text\n" + m + "\n```\n"
}
if m == "ERROR" {
icon_str = "‼"
}
if m == "WARN" {
icon_str = "⚠️"
}
}
.message = "{\"chat_id\":\"-1002640082189\",\"message_thread_id\":2,\"text\":\"" + icon_str + " " + level_str + marker_str + "Message: \n```text\n" + msg + "\n```\", \"parse_mode\":\"Markdown\"}"
format_telegram_json_translation:
type: remap
inputs:
- filter_errors_translation
source: |
msg, err = string(.message)
if err != null {
msg = "Unable to parse message"
}
marker_str = ""
if exists(.marker) {
m, err = string(.marker)
if err == null && length(m) > 0 {
marker_str = "Marker: \n```text\n" + m + "\n```\n"
}
}
level_str = ""
icon_str = ""
if exists(.level) {
m, err = string(.level)
if err == null {
level_str = "level: \n```text\n" + m + "\n```\n"
}
if m == "ERROR" {
icon_str = "‼"
}
if m == "WARN" {
icon_str = "⚠️"
}
}
.message = "{\"chat_id\":\"-1002640082189\",\"message_thread_id\":24,\"text\":\"" + icon_str + " " + level_str + marker_str + "Message: \n```text\n" + msg + "\n```\", \"parse_mode\":\"Markdown\"}"
format_telegram_json_dbms:
type: remap
inputs:
- filter_errors_dbms
source: |
msg, err = string(.message)
if err != null {
msg = "Unable to parse message"
}
marker_str = ""
if exists(.marker) {
m, err = string(.marker)
if err == null && length(m) > 0 {
marker_str = "Marker: \n```text\n" + m + "\n```\n"
}
}
level_str = ""
icon_str = ""
if exists(.level) {
m, err = string(.level)
if err == null {
level_str = "level: \n```text\n" + m + "\n```\n"
}
if m == "ERROR" {
icon_str = "‼"
}
if m == "WARN" {
icon_str = "⚠️"
}
}
.message = "{\"chat_id\":\"-1002640082189\",\"message_thread_id\":9,\"text\":\"" + icon_str + " " + level_str + marker_str + "Message: \n```text\n" + msg + "\n```\", \"parse_mode\":\"Markdown\"}"
format_telegram_json_jwt:
type: remap
inputs:
- filter_errors_jwt
source: |
msg, err = string(.message)
if err != null {
msg = "Unable to parse message"
}
marker_str = ""
if exists(.marker) {
m, err = string(.marker)
if err == null && length(m) > 0 {
marker_str = "Marker: \n```text\n" + m + "\n```\n"
}
}
level_str = ""
icon_str = ""
if exists(.level) {
m, err = string(.level)
if err == null {
level_str = "level: \n```text\n" + m + "\n```\n"
}
if m == "ERROR" {
icon_str = "‼"
}
if m == "WARN" {
icon_str = "⚠️"
}
}
.message = "{\"chat_id\":\"-1002640082189\",\"message_thread_id\":4,\"text\":\"" + icon_str + " " + level_str + marker_str + "Message: \n```text\n" + msg + "\n```\", \"parse_mode\":\"Markdown\"}"
sinks:
telegram_ccalm:
type: http
inputs:
- format_telegram_json_ccalm
uri: "https://api.telegram.org/bot8007457609:AAGnrXOSaoxODMSh4yCzUEIp0uxfH3Hk8ws/sendMessage"
method: post
encoding:
codec: text
request:
headers:
Content-Type: "application/json"
batch:
max_events: 1
telegram_translation:
type: http
inputs:
- format_telegram_json_translation
uri: "https://api.telegram.org/bot8007457609:AAGnrXOSaoxODMSh4yCzUEIp0uxfH3Hk8ws/sendMessage"
method: post
encoding:
codec: text
request:
headers:
Content-Type: "application/json"
batch:
max_events: 1
telegram_dbms:
type: http
inputs:
- format_telegram_json_dbms
uri: "https://api.telegram.org/bot8007457609:AAGnrXOSaoxODMSh4yCzUEIp0uxfH3Hk8ws/sendMessage"
method: post
encoding:
codec: text
request:
headers:
Content-Type: "application/json"
batch:
max_events: 1
telegram_jwt:
type: http
inputs:
- format_telegram_json_jwt
uri: "https://api.telegram.org/bot8007457609:AAGnrXOSaoxODMSh4yCzUEIp0uxfH3Hk8ws/sendMessage"
method: post
encoding:
codec: text
request:
headers:
Content-Type: "application/json"
batch:
max_events: 1
EOF
curl https://api.telegram.org/bot8007457609:AAGnrXOSaoxODMSh4yCzUEIp0uxfH3Hk8ws/getUpdates
Пробую отправку через curl
curl -X POST -k "https://gotify.geovizor.top:8443/message?token=AYmcpr43YtPKDmZ" \
-H "Content-Type: application/json" \
-d '{"message": "Test message", "priority": 5}'
Проверяем что gotify работает:
curl -k -X POST -H "Content-Type: application/json" -H "Host: gotify.geovizor.top" -d '{"message":"Test message 00","priority":5}' --http1.1 https://gotify.geovizor.top:8443/message?token=AYmcpr43YtPKDmZ -v
Объяснение конфигурации
- Source (
ccalm_logs): Читает логи из файла, парсит JSON, поддерживает многострочные логи. - Transform (
filter_errors): Фильтрует логи сlevel: "ERROR". - Sink (
gotify): Отправляет отфильтрованные логи в Gotify через HTTP POST.
Шаг 3: Проверка конфигурации
Проверьте корректность YAML:
vector --config /etc/vector/vector.yaml validate
Ожидаемый вывод: Configuration is valid.
Шаг 4: Запуск Vector
Тестовый запуск (для отладки)
sudo vector --config /etc/vector/vector.yaml
Запуск как сервиса
- Включите и запустите сервис:
sudo systemctl enable vector
sudo systemctl start vector
- Проверьте статус:
sudo systemctl status vector
sudo systemctl stop vector
Шаг 5: Проверка отправки в Gotify
- Убедитесь, что Gotify доступен по указанному URL.
- Добавьте тестовую строку лога в
/opt/org_ccalm_main/logs/ccalm.log:
echo '{"level": "ERROR", "marker":"12345", "message": "Database connection failed 0", "timestamp": "2025-05-18T12:28:00Z"}' | sudo tee -a /opt/org_ccalm_jwt/logs/org-ccalm-jwt.log
- Проверьте Gotify (веб-интерфейс или приложение) — должно прийти уведомление с заголовком "CCALM Log Error" и сообщением "Database connection failed".
Шаг 6: Отладка
- Логи Vector:
/var/log/vector/vector.logили stdout (в тестовом режиме). - Проверьте логи при проблемах:
cat /var/log/vector/vector.log
Дополнительные настройки
- Права доступа к файлу логов:
sudo chown vector:vector /opt/org_ccalm_main/logs/ccalm.log
sudo chmod 644 /opt/org_ccalm_main/logs/ccalm.log
- Если JSON-формат отличается: Если поле
levelназывается иначе (например,log_level), замените.levelна.log_levelвcondition. - HTTP вместо HTTPS: Если Gotify использует HTTP, замените
https://наhttp://вuri.
Примечания
- Убедитесь, что Gotify настроен и токен действителен.
- Если логи не отправляются, проверьте сетевую доступность Gotify и правильность URL/токена.
- Для чтения только новых логов удалите
read_from: beginningв конфигурации.