Fix formating message from zabbix now get Host, history url, host's ip and description of trigger

Trying to fix logger, add logrotation by app
This commit is contained in:
Влад Зверев 2024-08-01 15:22:33 +05:00
parent 3838645d7e
commit 75809aaafc

View File

@ -5,6 +5,7 @@ from dotenv import load_dotenv
import hashlib
import logging
from logging.config import dictConfig
import zipfile
from threading import Thread, Lock, Timer
import sqlite3
import time
@ -29,10 +30,13 @@ ENABLE_FILE_LOGGING = os.getenv('ENABLE_FILE_LOGGING', 'true').lower() in ['true
# Define log paths
LOG_PATH_ERRORS = os.getenv('LOG_PATH_ERRORS', 'logs/errors.log')
LOG_PATH_EVENTS = os.getenv('LOG_PATH_EVENTS', 'logs/events.log')
LOG_PATH_FLASK = os.getenv('LOG_PATH_FLASK', 'logs/flask.log')
LOG_ARCHIVE_PATH = os.getenv('LOG_ARCHIVE_PATH', 'logs/archive')
# Create log directories if they do not exist
os.makedirs(os.path.dirname(LOG_PATH_ERRORS), exist_ok=True)
os.makedirs(os.path.dirname(LOG_PATH_EVENTS), exist_ok=True)
os.makedirs(LOG_ARCHIVE_PATH, exist_ok=True)
class UTF8StreamHandler(logging.StreamHandler):
def __init__(self, stream=None):
@ -44,29 +48,47 @@ class UTF8StreamHandler(logging.StreamHandler):
if hasattr(stream, 'reconfigure'):
stream.reconfigure(encoding='utf-8')
# Define handlers based on environment variables
# Конфигурация логирования
handlers = {}
if ENABLE_CONSOLE_LOGGING:
handlers['console'] = {
'class': 'telezab.UTF8StreamHandler',
'stream': 'ext://sys.stdout', # Output to console
'stream': 'ext://sys.stdout', # Вывод в консоль
'formatter': 'console',
}
if ENABLE_FILE_LOGGING:
handlers['file_errors'] = {
'class': 'logging.FileHandler',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': LOG_PATH_ERRORS,
'when': 'midnight',
'interval': 1,
'backupCount': 7,
'formatter': 'error',
'encoding': 'utf-8', # Ensure UTF-8 encoding
'encoding': 'utf-8', # Убедитесь, что используется кодировка UTF-8
}
handlers['file_events'] = {
'class': 'logging.FileHandler',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': LOG_PATH_EVENTS,
'when': 'midnight',
'interval': 1,
'backupCount': 7,
'formatter': 'event',
'encoding': 'utf-8', # Ensure UTF-8 encoding
'encoding': 'utf-8', # Убедитесь, что используется кодировка UTF-8
}
# Configure Flask logger
flask_handlers = {
'flask': {
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': LOG_PATH_FLASK,
'when': 'midnight',
'interval': 1,
'backupCount': 7,
'formatter': 'default',
'encoding': 'utf-8',
}
}
# Configure logging
# Include flask_handlers in dictConfig
dictConfig({
'version': 1,
'formatters': {
@ -83,16 +105,63 @@ dictConfig({
'format': '[%(asctime)s] EVENT %(module)s: %(message)s',
},
},
'handlers': handlers,
'handlers': {**handlers, **flask_handlers},
'loggers': {
'default': {
'level': 'INFO',
'handlers': ['console', 'file_events'],
},
'error': {
'level': 'ERROR',
'handlers': ['console', 'file_errors'],
'propagate': False,
},
'flask': {
'level': 'INFO',
'handlers': ['flask'],
'propagate': False,
}
},
'root': {
'level': 'INFO',
'handlers': list(handlers.keys()),
'handlers': ['console', 'file_events'],
}
})
# Set Flask app logger to use the 'flask' logger
# Определение функции архивирования логов
def archive_old_logs():
yesterday_date = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
for log_file in [LOG_PATH_ERRORS, LOG_PATH_EVENTS]:
log_dir, log_filename = os.path.split(log_file)
for filename in os.listdir(log_dir):
if filename.startswith(log_filename) and filename != log_filename:
log_file_path = os.path.join(log_dir, filename)
archive_name = f"{log_filename}_{yesterday_date}.zip"
archive_path = os.path.join(LOG_ARCHIVE_PATH, archive_name)
with zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
zipf.write(log_file_path, arcname=filename)
os.remove(log_file_path)
# Create loggers
logger = logging.getLogger('default')
error_logger = logging.getLogger('error')
# Call archive_old_logs function after logging setup
archive_old_logs()
# Initialize Flask application
app = Flask(__name__)
app.logger.handlers = []
app.logger.propagate = True
flask_logger = logging.getLogger('flask')
app.logger.addHandler(flask_logger.handlers[0])
# Get the token from environment variables
TOKEN = os.getenv('TELEGRAM_TOKEN')
ZABBIX_URL = os.getenv('ZABBIX_URL')
@ -120,20 +189,19 @@ user_states = {}
user_timers = {}
# Initialize SQLite database
def init_db():
try:
with db_lock:
conn = sqlite3.connect('telezab.db')
cursor = conn.cursor()
# Create events table
cursor.execute('''CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hash TEXT UNIQUE,
data TEXT,
delivered BOOLEAN)''')
# Create subscriptions table with username and active flag
cursor.execute('''CREATE TABLE IF NOT EXISTS subscriptions (
chat_id INTEGER,
region_id TEXT,
@ -141,33 +209,28 @@ def init_db():
active BOOLEAN DEFAULT TRUE,
skip BOOLEAN DEFAULT FALSE,
UNIQUE(chat_id, region_id))''')
# Create whitelist table
cursor.execute('''CREATE TABLE IF NOT EXISTS whitelist (
chat_id INTEGER PRIMARY KEY)''')
# Create regions table with active flag
cursor.execute('''CREATE TABLE IF NOT EXISTS regions (
region_id TEXT PRIMARY KEY,
region_name TEXT,
active BOOLEAN DEFAULT TRUE)''')
# Create user events table for logging
cursor.execute('''CREATE TABLE IF NOT EXISTS user_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chat_id INTEGER,
username TEXT,
action TEXT,
timestamp TEXT)''')
# Insert sample regions
cursor.execute('''INSERT OR IGNORE INTO regions (region_id, region_name) VALUES
('01', 'Адыгея'),
('02', 'Башкортостан (Уфа)'),
('04', 'Алтай'),
('19', 'Республика Хакасия')''')
conn.commit()
logger.info("Database initialized successfully.")
except Exception as e:
error_logger.error(f"Error initializing database: {e}")
finally:
conn.close()
@ -285,16 +348,20 @@ def format_regions_list(regions):
return '\n'.join([f"{region_id} - {region_name}" for region_id, region_name in regions])
# Log user events
def log_user_event(chat_id, username, action):
timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
try:
with db_lock:
conn = sqlite3.connect('telezab.db')
cursor = conn.cursor()
query = 'INSERT INTO user_events (chat_id, username, action, timestamp) VALUES (?, ?, ?, ?)'
app.logger.debug(f"Executing query: {query} with chat_id={chat_id}, username={username}, action={action}, timestamp={timestamp}")
logger.debug(f"Executing query: {query} with chat_id={chat_id}, username={username}, action={action}, timestamp={timestamp}")
cursor.execute(query, (chat_id, username, action, timestamp))
conn.commit()
logger.info(f"User event logged: {chat_id} ({username}) - {action} at {timestamp}.")
except Exception as e:
error_logger.error(f"Error logging user event: {e}")
finally:
conn.close()
@ -717,43 +784,6 @@ def process_remove_region(message):
bot.send_message(chat_id, "Неверный формат. Используйте: <region_id>")
show_settings_menu(chat_id)
# # Handle admin whitelist management commands
# def prompt_admin_for_whitelist(chat_id, action):
# if action == 'add':
# bot.send_message(chat_id, "Введите ID пользователя, которого хотите добавить в белый список")
# bot.register_next_step_handler_by_chat_id(chat_id, process_add_whitelist)
# elif action == 'remove':
# bot.send_message(chat_id, "Введите ID пользователя, которого хотите удалить из белого списка")
# bot.register_next_step_handler_by_chat_id(chat_id, process_remove_whitelist)
#
#
# def process_add_whitelist(message):
# chat_id = message.chat.id
# try:
# new_chat_id = int(message.text.split()[0])
# add_to_whitelist(new_chat_id)
# bot.send_message(chat_id, f"Chat ID {new_chat_id} добавлен в белый список.")
# app.logger.info(f"Admin {chat_id} added {new_chat_id} to the whitelist.")
# log_user_event(new_chat_id, "N/A", f"Added to whitelist by {chat_id} (@{message.from_user.username})")
# except (IndexError, ValueError):
# bot.send_message(chat_id, "Неверный формат. Используйте: <chat_id>")
# show_settings_menu(chat_id)
# def process_remove_whitelist(message):
# chat_id = message.chat.id
# try:
# remove_chat_id = int(message.text.split()[0])
# remove_from_whitelist(remove_chat_id)
# bot.send_message(chat_id, f"Chat ID {remove_chat_id} удален из белого списка.")
# app.logger.info(f"Admin {chat_id} removed {remove_chat_id} from the whitelist.")
# log_user_event(remove_chat_id, "N/A", f"Removed from whitelist by {chat_id} (@{message.from_user.username})")
# except (IndexError, ValueError):
# bot.send_message(chat_id, "Неверный формат. Используйте: <chat_id>")
# show_settings_menu(chat_id)
# Handle displaying active subscriptions for a user
def handle_my_subscriptions(message):
chat_id = message.chat.id
@ -957,20 +987,28 @@ def format_message(data):
try:
status_emoji = "⚠️" if data['status'].upper() == "PROBLEM" else ""
priority_map = {
'High': 'Высокая',
'Disaster': 'Авария'
'4': 'Высокая',
'5': 'Авария'
}
priority = priority_map.get(data['severity'], 'Неизвестно')
if data['status'].upper() == "PROBLEM":
message = (
f"{status_emoji} Host: {data['host']}\n"
f"Сообщение: {data['msg']}\n"
f"Дата получения: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(data['date_reception'])))}\n"
f"Критичность: {priority}\n"
f"Теги: {data['tags']}\n"
f"Статус: {data['status']}"
f"⚠️ {data['host']} ({data["ip"]})\n"
f"{data['msg']}\n"
f"Критичность: {data['severity']}"
)
if 'link' in data:
message += f'\nСсылка на график: {data['link']}'
message += f'\nURL: {data['link']}'
return message
else:
message = (
f"{data['host']} ({data["ip"]})\n"
f"{data['msg']}\n"
f"Критичность: {data['severity']}\n"
f"Проблема устранена!\n"
f"Время устранения проблемы: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(data['date_reception'])))}"
)
return message
except KeyError as e:
app.logger.error(f"Missing key in data: {e}")
@ -985,19 +1023,12 @@ def add_user():
if telegram_id and chat_id:
add_to_whitelist(chat_id, telegram_id)
app.logger.info(f"User {telegram_id} added to whitelist.")
return jsonify({"status": "success"}), 200
else:
app.logger.error("Invalid data received for adding user.")
return jsonify({"status": "failure", "reason": "Invalid data"}), 400
# def format_message(data):
# return (f"Zabbix Alert\n"
# f"Host: {data['host']}\n"
# f"Item: {data['item']}\n"
# f"Trigger: {data['trigger']}\n"
# f"Value: {data['value']}")
# Handle active triggers
def handle_active_triggers(message):
chat_id = message.chat.id
@ -1076,7 +1107,7 @@ def handle_group_selection(call):
if triggers is None:
bot.send_message(chat_id, "Не удалось подключиться к Zabbix API. Пожалуйста, попробуйте позже.")
elif not triggers:
bot.send_message(chat_id, "Нет активных проблем по указанной группе за последние 24 часа.")
bot.send_message(chat_id, "Нет активных проблем по указанной группе уровня HIGH и DISASTER за последние 24 часа.")
else:
for trigger in triggers:
bot.send_message(chat_id, trigger, parse_mode="html")
@ -1191,41 +1222,6 @@ def get_zabbix_triggers(group_id):
logging.error(f"Error connecting to Zabbix API: {e}")
return None
# def split_message(message):
# max_length = 4096
# if len(message) <= max_length:
# return [message]
#
# parts = []
# while len(message) > max_length:
# split_index = message[:max_length].rfind('\n')
# if split_index == -1:
# split_index = max_length
# parts.append(message[:split_index])
# message = message[split_index:]
#
# parts.append(message)
# return parts
# def split_message_by_delimiter(messages, delimiter="---"):
# max_length = 4096
# parts = []
# current_part = ""
#
# for message in messages:
# if len(current_part) + len(message) + len(delimiter) <= max_length:
# if current_part:
# current_part += f"\n\n{delimiter}\n\n"
# current_part += message
# else:
# parts.append(current_part)
# current_part = message
#
# if current_part:
# parts.append(current_part)
#
# return parts
async def send_group_messages(chat_id, messages):
for message in messages:
@ -1245,7 +1241,6 @@ def simulate_event(message):
}
app.logger.info(f"Simulating event: {test_event}")
# Use requests to simulate a POST request
response = requests.post('http://localhost:5000/webhook', json=test_event)
@ -1274,7 +1269,7 @@ def run_polling():
if __name__ == '__main__':
init_db()
print('Bootstrap wait...')
# Start Flask app in a separate thread
Thread(target=app.run, kwargs={'port': 5000, 'host': '0.0.0.0', 'debug': True, 'use_reloader': False}, daemon=True).start()