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:
parent
3838645d7e
commit
75809aaafc
229
telezab.py
229
telezab.py
@ -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()
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user