#!/usr/bin/python
|
# -*- coding: GBK -*-
|
|
"""
|
Îļþ¹ÜÀíÄ£¿é
|
¸ºÔðÈÕÖ¾ÎļþµÄ´´½¨¡¢Ð´ÈëºÍ¹ÜÀí
|
"""
|
|
import os
|
import time
|
import datetime
|
import threading
|
from collections import namedtuple
|
from packet_logger import packet_logger
|
from clients_manager import ClientsMgr
|
|
# ¶¨ÒåÎļþÐÅÏ¢Ôª×é
|
FileInfo = namedtuple('FileInfo', ['server_id', 'event_type'])
|
|
|
class FileWriteStats:
|
"""ÎļþдÈëͳ¼Æ"""
|
|
def __init__(self):
|
self.lock = threading.Lock()
|
self.success_count = 0
|
self.fail_count = 0
|
|
def add_success(self):
|
with self.lock:
|
self.success_count += 1
|
|
def add_fail(self):
|
with self.lock:
|
self.fail_count += 1
|
|
def get_success_count(self):
|
with self.lock:
|
return self.success_count
|
|
def get_fail_count(self):
|
with self.lock:
|
return self.fail_count
|
|
def reset(self):
|
with self.lock:
|
self.success_count = 0
|
self.fail_count = 0
|
|
|
class WriteFile:
|
"""µ¥¸öÎļþдÈëÆ÷"""
|
|
def __init__(self, filename, success_callback, fail_callback):
|
self.filename = filename
|
self.file_handle = None
|
self.is_ready = False
|
self.success_callback = success_callback
|
self.fail_callback = fail_callback
|
self.open_file()
|
|
def open_file(self):
|
"""´ò¿ªÎļþ"""
|
try:
|
# È·±£Ä¿Â¼´æÔÚ
|
dir_path = os.path.dirname(self.filename)
|
if dir_path and not os.path.exists(dir_path):
|
os.makedirs(dir_path)
|
|
# ÒÔ×·¼Óģʽ´ò¿ªÎļþ
|
self.file_handle = open(self.filename, 'ab')
|
self.is_ready = True
|
except Exception as e:
|
self.is_ready = False
|
print('[WriteFile] Failed to open file: %s, error: %s' % (self.filename, str(e)))
|
|
def write_string(self, data):
|
"""дÈë×Ö·û´®Êý¾Ý"""
|
if not self.is_ready:
|
if self.fail_callback:
|
self.fail_callback()
|
return False
|
|
try:
|
# Ìí¼Ó Windows »»Ðзû²¢×ª»»Îª×Ö½Ú£¨¶þ½øÖÆÄ£Ê½£©
|
line = (data + '\r\n').encode('gbk')
|
self.file_handle.write(line)
|
self.file_handle.flush()
|
|
if self.success_callback:
|
self.success_callback()
|
return True
|
except Exception as e:
|
print('[WriteFile] Failed to write: %s, error: %s' % (self.filename, str(e)))
|
self.is_ready = False
|
if self.fail_callback:
|
self.fail_callback()
|
return False
|
|
def close(self):
|
"""¹Ø±ÕÎļþ"""
|
if self.file_handle:
|
try:
|
self.file_handle.close()
|
except:
|
pass
|
self.file_handle = None
|
self.is_ready = False
|
|
def __del__(self):
|
self.close()
|
|
|
class FileMgr:
|
"""Îļþ¹ÜÀíÆ÷"""
|
|
def __init__(self, config):
|
self.config = config
|
self.lock = threading.Lock()
|
self.file_map = {} # FileInfo -> WriteFile
|
self.stats = FileWriteStats()
|
self.log_path = self.config.get_log_file_path()
|
|
# È·±£ÈÕ־Ŀ¼´æÔÚ
|
if not os.path.exists(self.log_path):
|
try:
|
os.makedirs(self.log_path)
|
except Exception as e:
|
print('[FileMgr] Failed to create log dir: %s, error: %s' % (self.log_path, str(e)))
|
|
def get_instance(self, cid, event_type):
|
"""»ñÈ¡ÎļþдÈëʵÀý"""
|
# »ñÈ¡¿Í»§¶ËServer ID
|
clients_mgr = ClientsMgr.instance()
|
server_id = clients_mgr.get_client_server_id(cid)
|
|
if server_id == 0:
|
packet_logger.log_text('FileMgr: WARNING - cid=%d has no server_id set! Defaulting to S0' % cid, 'WARN')
|
|
file_info = FileInfo(server_id, event_type)
|
|
with self.lock:
|
# ¼ì²éÊÇ·ñÐèÒª´´½¨ÐÂÎļþ
|
if file_info not in self.file_map:
|
self._create_file_writer(file_info)
|
else:
|
write_file = self.file_map[file_info]
|
if not self._check_file_valid(write_file, file_info):
|
# ÎļþÎÞЧ,´´½¨ÐÂÎļþ
|
write_file.close()
|
del self.file_map[file_info]
|
self._create_file_writer(file_info)
|
|
return self.file_map[file_info]
|
|
def _get_file_path(self, file_info):
|
"""»ñÈ¡Îļþ·¾¶"""
|
write_mode = self.config.get_write_mode()
|
|
# Îļþ¼ÐÃû³Æ: S + Server ID
|
server_folder = 'S%d' % file_info.server_id
|
folder_path = os.path.join(self.log_path, server_folder)
|
|
# Ìí¼ÓÈÕÆÚ×ÓĿ¼: S1\2026-01-10
|
date_str = datetime.datetime.now().strftime('%Y-%m-%d')
|
date_folder = os.path.join(folder_path, date_str)
|
|
if write_mode == 1: # °´Ìì´æ´¢
|
filename = '%s_%s.txt' % (file_info.event_type, date_str)
|
elif write_mode == 2: # °´Ð¡Ê±´æ´¢
|
date_time_str = datetime.datetime.now().strftime('%Y-%m-%d_%H')
|
filename = '%s_%s.txt' % (file_info.event_type, date_time_str)
|
elif write_mode == 3: # °´Îļþ´óС´æ´¢
|
date_time_str = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
|
filename = '%s_%s.txt' % (file_info.event_type, date_time_str)
|
else: # µ¥¸öÎļþ
|
filename = '%s.txt' % file_info.event_type
|
|
return os.path.join(date_folder, filename)
|
|
def _check_file_valid(self, write_file, file_info):
|
"""¼ì²éÎļþÊÇ·ñÓÐЧ(ÊÇ·ñÐèÒªÇл»)"""
|
if not write_file.is_ready:
|
return False
|
|
write_mode = self.config.get_write_mode()
|
|
# ¼ì²éÈÕÆÚ±ä»¯£¨ÊÊÓÃÓÚËùÓÐģʽ£©
|
current_date = datetime.datetime.now().strftime('%Y-%m-%d')
|
if current_date not in write_file.filename:
|
return False
|
|
if write_mode == 3: # °´Îļþ´óС¼ì²é
|
max_size = self.config.get_max_file_size() * 1024 # KB -> Bytes
|
try:
|
file_size = os.path.getsize(write_file.filename)
|
if file_size >= max_size:
|
return False
|
except:
|
pass
|
|
return True
|
|
def _create_file_writer(self, file_info):
|
"""´´½¨ÎļþдÈëÆ÷"""
|
filepath = self._get_file_path(file_info)
|
write_file = WriteFile(
|
filepath,
|
self.stats.add_success,
|
self.stats.add_fail
|
)
|
self.file_map[file_info] = write_file
|
|
def write_event(self, cid, event_type, event_data):
|
"""дÈëʼþÊý¾Ý"""
|
write_file = self.get_instance(cid, event_type)
|
return write_file.write_string(event_data)
|
|
def get_success_count(self):
|
"""»ñÈ¡³É¹¦Ð´Èë´ÎÊý"""
|
return self.stats.get_success_count()
|
|
def get_fail_count(self):
|
"""»ñȡʧ°ÜдÈë´ÎÊý"""
|
return self.stats.get_fail_count()
|
|
def close_all(self):
|
"""¹Ø±ÕËùÓÐÎļþ"""
|
with self.lock:
|
for write_file in self.file_map.values():
|
write_file.close()
|
self.file_map.clear()
|