本文目录导读:

我来介绍几种在Python中记录数据不一致日志的方法:
使用标准库 logging(推荐)
import logging
from datetime import datetime
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('data_inconsistency.log', encoding='utf-8'),
logging.StreamHandler() # 同时输出到控制台
]
)
logger = logging.getLogger(__name__)
class DataValidator:
def check_data_consistency(self, data1, data2):
"""检查数据一致性"""
inconsistencies = []
# 检查字段不匹配
if data1.get('id') != data2.get('id'):
inconsistencies.append({
'type': 'ID_MISMATCH',
'field': 'id',
'expected': data1.get('id'),
'actual': data2.get('id')
})
# 检查数值不一致
if data1.get('total_amount') != data2.get('total_amount'):
inconsistencies.append({
'type': 'AMOUNT_MISMATCH',
'field': 'total_amount',
'expected': data1.get('total_amount'),
'actual': data2.get('total_amount'),
'difference': data1.get('total_amount', 0) - data2.get('total_amount', 0)
})
# 记录不一致日志
if inconsistencies:
logger.warning(f"发现 {len(inconsistencies)} 条数据不一致")
for issue in inconsistencies:
logger.error(
f"数据不一致 - 类型: {issue['type']}, "
f"字段: {issue['field']}, "
f"期望值: {issue['expected']}, "
f"实际值: {issue['actual']}"
)
return inconsistencies
def log_record_mismatch(self, record_type, source, record_id, details):
"""记录单条记录不一致"""
logger.warning(
f"记录不一致 - 类型: {record_type}, "
f"源: {source}, "
f"ID: {record_id}, "
f"详情: {details}"
)
# 使用示例
validator = DataValidator()
data_source_a = {'id': 1, 'total_amount': 100.50, 'status': 'active'}
data_source_b = {'id': 1, 'total_amount': 99.50, 'status': 'pending'}
inconsistencies = validator.check_data_consistency(data_source_a, data_source_b)
带统计功能的日志记录
import logging
from collections import Counter
from datetime import datetime
import json
class InconsistencyLogger:
def __init__(self, log_file='data_inconsistency.log'):
self.logger = self._setup_logger(log_file)
self.stats = Counter()
self.inconsistencies_history = []
def _setup_logger(self, log_file):
logger = logging.getLogger('DataInconsistency')
logger.setLevel(logging.DEBUG)
# 文件处理器 - 详细日志
file_handler = logging.FileHandler(log_file, encoding='utf-8')
file_handler.setLevel(logging.INFO)
file_format = logging.Formatter('%(asctime)s | %(levelname)s | %(message)s')
file_handler.setFormatter(file_format)
# JSON 格式的专门日志
json_handler = logging.FileHandler('inconsistency_export.json', encoding='utf-8')
json_handler.setLevel(logging.WARNING)
logger.addHandler(file_handler)
logger.addHandler(json_handler)
return logger
def log_inconsistency(self, record_id, fields_issues, severity='WARNING'):
"""
记录数据不一致
Args:
record_id: 记录标识
fields_issues: 字段问题列表 [{'field': 'amount', 'expected': 100, 'actual': 99}, ...]
severity: 严重级别 ERROR/WARNING/INFO
"""
timestamp = datetime.now().isoformat()
# 更新统计
self.stats['total_inconsistencies'] += 1
for issue in fields_issues:
self.stats[f"field_{issue['field']}"] += 1
# 创建日志记录
log_entry = {
'timestamp': timestamp,
'record_id': record_id,
'severity': severity,
'issues': fields_issues,
'total_issues': len(fields_issues)
}
# 根据严重级别记录
if severity == 'ERROR':
self.logger.error(json.dumps(log_entry, ensure_ascii=False))
elif severity == 'WARNING':
self.logger.warning(json.dumps(log_entry, ensure_ascii=False))
else:
self.logger.info(json.dumps(log_entry, ensure_ascii=False))
# 保存到历史记录
self.inconsistencies_history.append(log_entry)
# 控制台输出(可选)
print(f"[{timestamp}] {severity}: 记录 {record_id} 发现 {len(fields_issues)} 个问题")
def log_comparison_result(self, source1_name, source2_name, mismatch_count, details):
"""记录源数据比较结果"""
log_msg = (
f"数据源比较: {source1_name} vs {source2_name} | "
f"不匹配数: {mismatch_count} | "
f"详情: {details}"
)
self.logger.info(log_msg)
print(log_msg)
def get_statistics(self):
"""获取不一致统计"""
return dict(self.stats)
# 使用示例
inconsistency_logger = InconsistencyLogger()
# 模拟数据不一致
issues = [
{'field': 'amount', 'expected': 100.50, 'actual': 99.50, 'difference': 1.00},
{'field': 'status', 'expected': 'active', 'actual': 'inactive'}
]
inconsistency_logger.log_inconsistency('RECORD_001', issues, 'ERROR')
inconsistency_logger.log_comparison_result('DatabaseA', 'DatabaseB', 5, '总记录数不匹配')
# 获取统计
stats = inconsistency_logger.get_statistics()
print(f"不一致统计: {stats}")
数据库比较日志
import logging
import pandas as pd
from datetime import datetime
class DatabaseComparisonLogger:
def __init__(self, source_db_name, target_db_name):
self.logger = self._setup_logger()
self.source_db = source_db_name
self.target_db = target_db_name
def _setup_logger(self):
logger = logging.getLogger('DBComparison')
logger.setLevel(logging.DEBUG)
# 主日志文件
handler = logging.FileHandler('db_comparison.log', encoding='utf-8')
handler.setLevel(logging.INFO)
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
# 错误日志单独文件
error_handler = logging.FileHandler('db_errors.log', encoding='utf-8')
error_handler.setLevel(logging.ERROR)
error_handler.setFormatter(formatter)
logger.addHandler(error_handler)
return logger
def compare_record_count(self, source_count, target_count):
"""比较记录数"""
if source_count != target_count:
self.logger.error(
f"记录数不一致 - {self.source_db}: {source_count}, "
f"{self.target_db}: {target_count}"
)
return False
else:
self.logger.info(
f"记录数一致 - 共 {source_count} 条"
)
return True
def compare_dataframes(self, df_source, df_target, key_column='id'):
"""比较两个DataFrame"""
mismatches = []
# 找出差异
merged = pd.merge(df_source, df_target,
on=key_column, how='outer',
suffixes=('_source', '_target'),
indicator=True)
# 记录缺失记录
missing_in_target = merged[merged['_merge'] == 'left_only']
missing_in_source = merged[merged['_merge'] == 'right_only']
if len(missing_in_target) > 0:
self.logger.warning(
f"在 {self.target_db} 中缺失 {len(missing_in_target)} 条记录"
)
for _, row in missing_in_target.iterrows():
self.logger.warning(
f"缺失记录 - ID: {row[key_column]}, "
f"来源: {self.source_db}"
)
if len(missing_in_source) > 0:
self.logger.warning(
f"在 {self.source_db} 中缺失 {len(missing_in_source)} 条记录"
)
# 比较字段值
common_records = merged[merged['_merge'] == 'both']
for col in df_source.columns:
if col == key_column:
continue
source_col = f"{col}_source"
target_col = f"{col}_target"
if source_col in common_records.columns and target_col in common_records.columns:
diff = common_records[common_records[source_col] != common_records[target_col]]
if len(diff) > 0:
mismatches.append({
'field': col,
'count': len(diff),
'records': diff[[key_column, source_col, target_col]].to_dict('records')
})
self.logger.warning(
f"字段 {col} 有 {len(diff)} 条不一致"
)
return mismatches
# 使用示例
comparison_logger = DatabaseComparisonLogger('DB_Main', 'DB_Backup')
# 模拟记录数比较
comparison_logger.compare_record_count(1000, 998)
# 模拟DataFrame比较
import pandas as pd
df1 = pd.DataFrame({'id': [1, 2, 3], 'value': [100, 200, 300]})
df2 = pd.DataFrame({'id': [1, 2, 4], 'value': [100, 250, 400]})
mismatches = comparison_logger.compare_dataframes(df1, df2)
带统计报表的日志系统
import logging
import json
from datetime import datetime, timedelta
from collections import defaultdict
class AdvancedInconsistencyTracker:
def __init__(self):
self.logger = self._setup_logger()
self.daily_stats = defaultdict(lambda: defaultdict(int))
self.alert_threshold = 10 # 触发告警的不一致数量
def _setup_logger(self):
logger = logging.getLogger('AdvancedTracker')
logger.setLevel(logging.DEBUG)
# 主日志
main_handler = logging.FileHandler('inconsistency_tracker.log')
main_handler.setLevel(logging.INFO)
logger.addHandler(main_handler)
# 详细日志
detail_handler = logging.FileHandler('inconsistency_details.log')
detail_handler.setLevel(logging.DEBUG)
logger.addHandler(detail_handler)
return logger
def log_with_context(self, inconsistency_type, context, details):
"""
记录带上下文的不一致信息
Args:
inconsistency_type: 不一致类型
context: 上下文信息(数据源、表名等)
details: 详细信息
"""
timestamp = datetime.now()
date_key = timestamp.strftime('%Y-%m-%d')
# 更新统计
self.daily_stats[date_key][inconsistency_type] += 1
self.daily_stats[date_key]['total'] += 1
# 记录日志
log_entry = {
'timestamp': timestamp.isoformat(),
'type': inconsistency_type,
'context': context,
'details': details,
'daily_count': self.daily_stats[date_key][inconsistency_type]
}
self.logger.info(json.dumps(log_entry, ensure_ascii=False))
# 检查是否需要告警
if self.daily_stats[date_key]['total'] >= self.alert_threshold:
self._send_alert(date_key)
def _send_alert(self, date_key):
"""发送告警"""
alert_msg = (
f"[ALERT] 数据不一致数量已达到 {self.alert_threshold}!"
f" 日期: {date_key}, "
f"总不一致: {self.daily_stats[date_key]['total']}"
)
self.logger.warning(alert_msg)
print(f"⚠️ {alert_msg}")
def generate_report(self, days=7):
"""生成统计报告"""
report = []
end_date = datetime.now()
start_date = end_date - timedelta(days=days)
current = start_date
while current <= end_date:
date_key = current.strftime('%Y-%m-%d')
stats = self.daily_stats.get(date_key, {'total': 0})
report.append({
'date': date_key,
'total_inconsistencies': stats['total'],
'types': {k: v for k, v in stats.items() if k != 'total'}
})
current += timedelta(days=1)
# 保存报告
report_file = f'inconsistency_report_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
with open(report_file, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
self.logger.info(f"报告已保存到: {report_file}")
return report
# 使用示例
tracker = AdvancedInconsistencyTracker()
# 模拟不同场景的不一致
inconsistencies = [
('FIELD_MISMATCH', {'source': 'order_db', 'table': 'orders'},
{'field': 'amount', 'expected': 100, 'actual': 99}),
('MISSING_RECORD', {'source': 'user_db', 'table': 'users'},
{'id': '12345', 'expected': 'present', 'actual': 'missing'}),
('RECORD_DUPLICATE', {'source': 'product_db', 'table': 'products'},
{'id': 'P001', 'duplicate_count': 2})
]
for inc_type, context, details in inconsistencies:
tracker.log_with_context(inc_type, context, details)
# 生成报告
report = tracker.generate_report(days=7)
最佳实践建议
- 分级日志:使用不同级别记录不同严重程度的不一致
- 结构化数据:使用JSON格式便于后续分析
- 自动统计:记录不一致的数量和类型统计
- 告警机制:当不一致超过阈值时自动告警
- 定期清理:定期归档和清理旧日志
选择哪种方式取决于你的具体需求,建议在项目初期就建立完善的数据不一致日志系统。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播