重构DBN模型
This commit is contained in:
@@ -30,7 +30,7 @@ def _build_prediction_items(results: List[Dict[str, Any]]) -> List[PredictionIte
|
||||
|
||||
max_hazard = max(probs, key=probs.get)
|
||||
items.append(PredictionItem(
|
||||
id=r["point_id"],
|
||||
id=r["source_id"], # 使用 source_id(隐患点/风险点ID)而非 xian_risk_factors.id
|
||||
type=SOURCE_TYPE_MAP.get(r.get("source_type"), "未知"),
|
||||
probability=round(probs[max_hazard], 4),
|
||||
level=LEVEL_MAP.get(levels.get(max_hazard, "none"), "无"),
|
||||
@@ -70,7 +70,7 @@ def _predict_sync(point_ids: Optional[List[int]], region_code: Optional[str],
|
||||
|
||||
save_results = [
|
||||
{
|
||||
"point_id": r.get("point_id"),
|
||||
"point_id": r.get("source_id"), # 使用 source_id(隐患点/风险点ID)而非 xian_risk_factors.id
|
||||
"source_type": r.get("source_type"),
|
||||
"lon": r.get("lon"),
|
||||
"lat": r.get("lat"),
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ def _build_prediction_items(results: List[Dict[str, Any]]) -> List[PredictionIte
|
||||
|
||||
max_hazard = max(probs, key=probs.get)
|
||||
items.append(PredictionItem(
|
||||
id=r["point_id"],
|
||||
id=r["source_id"], # 使用 source_id(隐患点/风险点ID)而非 xian_risk_factors.id
|
||||
type=SOURCE_TYPE_MAP.get(r.get("source_type"), "未知"),
|
||||
probability=round(probs[max_hazard], 4),
|
||||
level=LEVEL_MAP.get(levels.get(max_hazard, "none"), "无"),
|
||||
@@ -73,7 +73,7 @@ def _predict_sync(point_ids: Optional[List[int]], region_code: Optional[str],
|
||||
}
|
||||
save_results = [
|
||||
{
|
||||
"point_id": r.get("point_id"),
|
||||
"point_id": r.get("source_id"), # 使用 source_id(隐患点/风险点ID)而非 xian_risk_factors.id
|
||||
"source_type": r.get("source_type"),
|
||||
"lon": r.get("lon"),
|
||||
"lat": r.get("lat"),
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
# 定义所有连续因子的分箱规则
|
||||
# 包含暴雨灾害链和地震灾害链的全部因子
|
||||
#
|
||||
# 2026-06-06: 基于1201个样本的实际数据分布,采用分位数分箱(等频分箱)
|
||||
# 替代原有等宽分箱,使每个区间样本量更均匀
|
||||
# 2026-06-11: 基于1365个样本(796隐患点+569风险点)的实际数据分布
|
||||
# 连续因子采用分位数分箱(等频分箱),分类因子基于实际编码映射
|
||||
|
||||
# ============================================
|
||||
# 暴雨触发层离散化规则(保持气象标准不变)
|
||||
@@ -62,17 +62,17 @@ seismic_intensity:
|
||||
elevation:
|
||||
description: "高程"
|
||||
unit: "m"
|
||||
# 数据: [356, 1934], 均值764.3±317.89, 偏度0.973
|
||||
# 分位数: [356, 470, 624, 792, 1016, 1934]
|
||||
bins: [356, 470, 624, 792, 1016, 1934]
|
||||
# 数据: [354, 1926], 均值789.36±325.64
|
||||
# 分位数: [354, 482, 637, 817, 1074, 1926]
|
||||
bins: [354, 482, 637, 817, 1074, 1926]
|
||||
labels: [very_low, low, medium, high, very_high]
|
||||
|
||||
slope:
|
||||
description: "坡度"
|
||||
unit: "度"
|
||||
# 数据: [0.11, 47.14], 均值9.42±8.57, 偏度1.433
|
||||
# 分位数: [0.11, 1.81, 5.43, 9.48, 15.13, 47.14]
|
||||
bins: [0.11, 1.81, 5.43, 9.48, 15.13, 47.14]
|
||||
# 数据: [0.0, 47.0], 均值10.46±9.12
|
||||
# 分位数: [0.0, 2.0, 6.0, 11.0, 17.0, 47.0]
|
||||
bins: [0.0, 2.0, 6.0, 11.0, 17.0, 47.0]
|
||||
labels: [very_low, low, medium, high, very_high]
|
||||
|
||||
aspect:
|
||||
@@ -85,13 +85,20 @@ aspect:
|
||||
|
||||
soil_type:
|
||||
description: "土壤分类(中国土壤分类系统)"
|
||||
unit: "分类代码"
|
||||
unit: "3位数编码"
|
||||
# 中国土壤分类系统25个亚类,本数据库出现8种
|
||||
# 编码规则:1xx淋溶土、2xx钙层/均腐土、4xx初育土
|
||||
mapping:
|
||||
0: ultisol # 老成土
|
||||
6: entisol # 初育土
|
||||
11: fluvo_aquic # 潮土
|
||||
18: yellow_brown # 黄棕壤
|
||||
default: entisol
|
||||
110: brown_soil # 棕壤(淋溶土,秦岭北麓山地)
|
||||
120: brown_soil # 暗棕壤(淋溶土,高海拔山地)
|
||||
130: yellow_brown # 黄棕壤(淋溶土,暖温带过渡区)
|
||||
150: yellow_brown # 黄褐土(淋溶土,黄土塬区)
|
||||
210: cinnamon # 褐土(钙层土,黄土塬区主要旱作土壤)
|
||||
240: black_lu # 黑垆土(均腐土,古土壤残余)
|
||||
410: alluvial # 新积土(初育土,渭河冲积平原)
|
||||
420: aeolian # 风沙土(初育土,风积沙质土壤)
|
||||
255: unknown # GIS背景值(1个样本)
|
||||
default: cinnamon # 褐土占比最大(38.8%),作为默认值
|
||||
|
||||
lithology:
|
||||
description: "岩性(中国地质分类)"
|
||||
@@ -105,18 +112,19 @@ lithology:
|
||||
11: mixed_clastic # 混合碎屑沉积岩(砂岩+泥岩互层)
|
||||
13: terrigenous # 陆源碎屑岩(砂岩、粉砂岩)
|
||||
14: unconsolidated # 松散堆积物(黄土、冲洪积)
|
||||
255: unknown # 无数据(GIS栅格背景值)
|
||||
default: unconsolidated
|
||||
|
||||
landuse:
|
||||
description: "土地利用类型"
|
||||
unit: "分类代码"
|
||||
mapping:
|
||||
10: forest # 林地
|
||||
30: farmland # 农田
|
||||
40: urban # 城市
|
||||
50: water # 水域
|
||||
60: barren # 裸地
|
||||
80: farmland # 耕地(合并入农田)
|
||||
1: forest # 林地(GIS栅格编码1)
|
||||
2: farmland # 农田(GIS栅格编码2)
|
||||
3: urban # 城市(GIS栅格编码3)
|
||||
4: water # 水域(GIS栅格编码4)
|
||||
5: barren # 裸地(GIS栅格编码5)
|
||||
8: farmland # 耕地(GIS栅格编码8,合并入农田)
|
||||
default: farmland
|
||||
|
||||
terrain:
|
||||
@@ -130,80 +138,82 @@ terrain:
|
||||
5: gentle_hill # 低缓丘陵(塬边过渡带)
|
||||
6: low_mountain # 低山(骊山等)
|
||||
7: flat_plain # 平缓平原(冲积平原)
|
||||
255: unknown # 无数据(GIS栅格背景值)
|
||||
default: hill
|
||||
|
||||
impervious:
|
||||
description: "不透水率"
|
||||
unit: "百分比"
|
||||
# 数据: [0.0, 97.2], 均值16.40±25.99, 偏度1.787
|
||||
# 26.9%为0.0(无硬化地表),非零值右偏分布
|
||||
# 分箱策略:0单独一类,其余4等分(分位数分箱)
|
||||
# 分位数(非零): [2.0, 9.95, 31.8, 97.2]
|
||||
bins: [0.0, 0.01, 2.0, 10.0, 32.0, 97.2]
|
||||
unit: "小数比例(0-1)"
|
||||
# 数据: [0.0, 1.0], 均值0.31±0.46
|
||||
# 68.9%为0.0(无硬化地表),非零值右偏分布
|
||||
# 分箱策略:0单独一类,其余4等分
|
||||
bins: [0.0, 0.01, 0.25, 0.50, 0.75, 1.0]
|
||||
labels: [none, very_low, low, medium, high]
|
||||
|
||||
ndvi:
|
||||
description: "植被指数"
|
||||
unit: "NDVI值"
|
||||
# 数据: [1.25, 38.68], 均值20.67±5.87, 偏度-0.106
|
||||
# 分位数: [1.25, 17.09, 20.3, 22.4, 25.2, 38.68]
|
||||
bins: [1.25, 17.09, 20.3, 22.4, 25.2, 38.68]
|
||||
unit: "NDVI值(×1000缩放)"
|
||||
# 数据: [-1.0, 5336.0], 均值2045.95±689.47
|
||||
# 分位数: [-1.0, 1616.2, 1891.0, 2172.0, 2496.0, 5336.0]
|
||||
bins: [-1.0, 1616.0, 1891.0, 2172.0, 2496.0, 5336.0]
|
||||
labels: [very_low, low, medium, high, very_high]
|
||||
|
||||
sand_content:
|
||||
description: "土壤含沙量"
|
||||
unit: "百分比"
|
||||
# 数据: [23.0, 52.0], 均值34.43±4.29, 偏度0.538
|
||||
# 分位数: [23.0, 31.0, 33.0, 35.0, 37.0, 52.0]
|
||||
bins: [23.0, 31.0, 33.0, 35.0, 37.0, 52.0]
|
||||
# 数据: [23.0, 255.0], 均值35.14±7.75
|
||||
# 255为异常值(缺失值编码),正常范围[23, 52]
|
||||
# 分位数(正常值): [23.0, 31.0, 34.0, 35.0, 38.0, 52.0]
|
||||
bins: [23.0, 31.0, 34.0, 35.0, 38.0, 255.0]
|
||||
labels: [very_low, low, medium, high, very_high]
|
||||
|
||||
ph:
|
||||
description: "土壤PH值"
|
||||
unit: "PH值"
|
||||
# 数据: [59.0, 81.0], 均值71.79±4.14, 偏度-0.398
|
||||
# 分位数: [59.0, 68.0, 72.0, 74.0, 76.0, 81.0]
|
||||
bins: [59.0, 68.0, 72.0, 74.0, 76.0, 81.0]
|
||||
unit: "PH值(×10缩放,如71=7.1)"
|
||||
# 数据: [60.0, 255.0], 均值71.82±6.91
|
||||
# 255为异常值(缺失值编码),正常范围[59, 81]
|
||||
# 分位数(正常值): [60.0, 67.0, 71.0, 74.0, 76.0, 81.0]
|
||||
bins: [60.0, 67.0, 71.0, 74.0, 76.0, 255.0]
|
||||
labels: [very_low, low, medium, high, very_high]
|
||||
|
||||
soil_moisture:
|
||||
description: "土壤湿度"
|
||||
unit: "百分比"
|
||||
# 数据: [0.0, 41.1], 均值32.02±14.92, 偏度-1.676
|
||||
# 约10%为0.0(缺失/极端干燥),其余集中在37-41
|
||||
# 分位数: [0.0, 37.7, 38.6, 38.9, 39.4, 41.1]
|
||||
bins: [0.0, 37.0, 38.5, 39.5, 41.1]
|
||||
labels: [very_low, low, medium, high]
|
||||
unit: "小数比例(0-1)"
|
||||
# 数据: [-1.0, 0.28], 均值0.15±0.08, 约10%为-1(缺失值)
|
||||
# 正常值范围: [0.0, 0.28]
|
||||
# 分位数(正常值): [0.0, 0.12, 0.14, 0.16, 0.19, 0.28]
|
||||
# 分箱策略:-1视为缺失/极端干燥,其余4等分
|
||||
bins: [-1.0, 0.0, 0.10, 0.14, 0.18, 0.28]
|
||||
labels: [very_low, low, medium, high, very_high]
|
||||
|
||||
organic_carbon:
|
||||
description: "有机碳"
|
||||
unit: "百分比"
|
||||
# 数据: [0.0, 73.0], 均值38.36±19.14, 偏度-1.187
|
||||
# 分位数: [0.0, 34.0, 41.0, 47.0, 53.0, 73.0]
|
||||
bins: [0.0, 34.0, 41.0, 47.0, 53.0, 73.0]
|
||||
# 数据: [0.0, 65.0], 均值39.03±19.13
|
||||
# 分位数: [0.0, 34.0, 42.0, 48.0, 53.0, 65.0]
|
||||
bins: [0.0, 34.0, 42.0, 48.0, 53.0, 65.0]
|
||||
labels: [very_low, low, medium, high, very_high]
|
||||
|
||||
dist_to_river:
|
||||
description: "距离河道距离"
|
||||
unit: "米"
|
||||
# 数据: [12.21, 29904.99], 均值11003.92±6582.23, 偏度0.271
|
||||
# 分位数: [12.21, 5165.0, 9003.0, 12424.97, 16431.82, 29904.99]
|
||||
bins: [12.21, 5165.0, 9003.0, 12424.97, 16431.82, 29904.99]
|
||||
# 数据: [12.21, 29968.26], 均值11378.07±6704.59
|
||||
# 分位数: [12.21, 5409.3, 9522.82, 12667.75, 16952.46, 29968.26]
|
||||
bins: [12.21, 5409.3, 9522.82, 12667.75, 16952.46, 29968.26]
|
||||
labels: [very_close, close, moderate, far, very_far]
|
||||
|
||||
dist_to_fault:
|
||||
description: "距离断裂带距离"
|
||||
unit: "米"
|
||||
# 数据: [1.74, 14542.53], 均值3448.52±3406.56, 偏度1.055
|
||||
# 分位数: [1.74, 476.69, 1433.62, 3334.87, 6502.28, 14542.53]
|
||||
bins: [1.74, 476.69, 1433.62, 3334.87, 6502.28, 14542.53]
|
||||
# 数据: [1.72, 14685.31], 均值3527.70±3400.55
|
||||
# 分位数: [1.72, 515.98, 1451.71, 3577.36, 6545.45, 14685.31]
|
||||
bins: [1.72, 515.98, 1451.71, 3577.36, 6545.45, 14685.31]
|
||||
labels: [very_close, close, moderate, far, very_far]
|
||||
|
||||
pipe_density:
|
||||
description: "供水管网密度"
|
||||
unit: "m/m²"
|
||||
# 数据: [0.0, 0.07], 约80%为0.0,90%分位数0.013,95%分位数0.023
|
||||
# 分箱策略:0单独一类,其余3等分(分位数分箱)
|
||||
# 分位数(非零): [0.013, 0.023, 0.065]
|
||||
bins: [0.0, 0.001, 0.013, 0.023, 0.065]
|
||||
# 数据: [0.0, 0.07], 约83.9%为0.0,非零值分位数[0.000438, 0.007136, 0.015399, 0.024523, 0.065431]
|
||||
# 分箱策略:0单独一类,其余3等分
|
||||
bins: [0.0, 0.001, 0.010, 0.025, 0.065]
|
||||
labels: [none, low, medium, high]
|
||||
|
||||
@@ -102,8 +102,8 @@ landslide:
|
||||
# 大地震+近场+丘陵地形:黄土塬边
|
||||
- condition: {magnitude: [major, great], epicenter_distance: [very_near, near], terrain: [hill, gentle_hill]}
|
||||
probability: 0.55
|
||||
# 强震+近场+初育土(黄土)
|
||||
- condition: {magnitude: [strong, major], epicenter_distance: [very_near, near], soil_type: [entisol]}
|
||||
# 强震+近场+初育土(新积土/风沙土,黄土)
|
||||
- condition: {magnitude: [strong, major], epicenter_distance: [very_near, near], soil_type: [alluvial, aeolian]}
|
||||
probability: 0.52
|
||||
|
||||
# === 中风险(0.20-0.40)===
|
||||
|
||||
@@ -147,18 +147,18 @@ node_states:
|
||||
seismic_intensity: [minor, light, moderate, severe, extreme]
|
||||
|
||||
# 环境层(与暴雨模型共享,状态名与 discretization.yaml 一致)
|
||||
# 基于1201个样本的实际数据分布,2026-06-06更新
|
||||
# 基于1365个样本的实际数据分布,2026-06-11更新
|
||||
elevation: [very_low, low, medium, high, very_high]
|
||||
slope: [very_low, low, medium, high, very_high]
|
||||
aspect: [flat, north, east, south, west]
|
||||
soil_type: [ultisol, entisol, fluvo_aquic, yellow_brown]
|
||||
lithology: [acid_rock, basic_rock, carbonate, metamorphic, mixed_clastic, terrigenous, unconsolidated]
|
||||
soil_type: [brown_soil, yellow_brown, cinnamon, black_lu, alluvial, aeolian, unknown]
|
||||
lithology: [acid_rock, basic_rock, carbonate, metamorphic, mixed_clastic, terrigenous, unconsolidated, unknown]
|
||||
landuse: [forest, farmland, urban, water, barren]
|
||||
terrain: [mountain, plain, deep_valley, hill, gentle_hill, low_mountain, flat_plain]
|
||||
terrain: [mountain, plain, deep_valley, hill, gentle_hill, low_mountain, flat_plain, unknown]
|
||||
ndvi: [very_low, low, medium, high, very_high]
|
||||
sand_content: [very_low, low, medium, high, very_high]
|
||||
ph: [very_low, low, medium, high, very_high]
|
||||
soil_moisture: [very_low, low, medium, high]
|
||||
soil_moisture: [very_low, low, medium, high, very_high]
|
||||
organic_carbon: [very_low, low, medium, high, very_high]
|
||||
dist_to_river: [very_close, close, moderate, far, very_far]
|
||||
dist_to_fault: [very_close, close, moderate, far, very_far]
|
||||
|
||||
@@ -72,8 +72,8 @@ landslide:
|
||||
# 城区开挖坡脚/弃土加载+暴雨:工程滑坡
|
||||
- condition: {landuse: [urban], slope: [medium, high, very_high], rain_intensity: [heavy, storm, downpour, extreme]}
|
||||
probability: 0.52
|
||||
# 大雨+陡坡+初育土(黄土):黄土塬边滑坡
|
||||
- condition: {rain_intensity: [heavy, storm, downpour], slope: [high, very_high], soil_type: [entisol]}
|
||||
# 大雨+陡坡+初育土(新积土/风沙土,黄土):黄土塬边滑坡
|
||||
- condition: {rain_intensity: [heavy, storm, downpour], slope: [high, very_high], soil_type: [alluvial, aeolian]}
|
||||
probability: 0.50
|
||||
|
||||
# === 中风险(0.20-0.40)===
|
||||
|
||||
@@ -143,19 +143,19 @@ node_states:
|
||||
accum_rain: [trace, light, moderate, heavy, extreme]
|
||||
|
||||
# 环境层(离散字段状态名与数据库编码一一对应,连续字段用分位数分箱)
|
||||
# 基于1201个样本的实际数据分布,2026-06-06更新
|
||||
# 基于1365个样本的实际数据分布,2026-06-11更新
|
||||
elevation: [very_low, low, medium, high, very_high]
|
||||
slope: [very_low, low, medium, high, very_high]
|
||||
aspect: [flat, north, east, south, west]
|
||||
soil_type: [ultisol, entisol, fluvo_aquic, yellow_brown]
|
||||
lithology: [acid_rock, basic_rock, carbonate, metamorphic, mixed_clastic, terrigenous, unconsolidated]
|
||||
soil_type: [brown_soil, yellow_brown, cinnamon, black_lu, alluvial, aeolian, unknown]
|
||||
lithology: [acid_rock, basic_rock, carbonate, metamorphic, mixed_clastic, terrigenous, unconsolidated, unknown]
|
||||
landuse: [forest, farmland, urban, water, barren]
|
||||
terrain: [mountain, plain, deep_valley, hill, gentle_hill, low_mountain, flat_plain]
|
||||
terrain: [mountain, plain, deep_valley, hill, gentle_hill, low_mountain, flat_plain, unknown]
|
||||
impervious: [none, very_low, low, medium, high]
|
||||
ndvi: [very_low, low, medium, high, very_high]
|
||||
sand_content: [very_low, low, medium, high, very_high]
|
||||
ph: [very_low, low, medium, high, very_high]
|
||||
soil_moisture: [very_low, low, medium, high]
|
||||
soil_moisture: [very_low, low, medium, high, very_high]
|
||||
organic_carbon: [very_low, low, medium, high, very_high]
|
||||
dist_to_river: [very_close, close, moderate, far, very_far]
|
||||
dist_to_fault: [very_close, close, moderate, far, very_far]
|
||||
|
||||
@@ -34,9 +34,6 @@ class AppLauncher:
|
||||
# 检查虚拟环境
|
||||
check_virtualenv(self.project_root)
|
||||
|
||||
# 检查安装依赖
|
||||
check_dependencies(self.project_root)
|
||||
|
||||
# 检查是否正在使用虚拟环境运行
|
||||
import platform
|
||||
import sys
|
||||
@@ -57,12 +54,16 @@ class AppLauncher:
|
||||
print("检测到未使用虚拟环境,正在切换到虚拟环境...")
|
||||
print("=" * 50)
|
||||
|
||||
# 使用虚拟环境的Python重新启动应用
|
||||
# 使用虚拟环境的Python重新启动应用(不传递参数避免重复检查)
|
||||
import subprocess
|
||||
cmd = [str(venv_python)] + sys.argv
|
||||
subprocess.run(cmd, check=True)
|
||||
return
|
||||
|
||||
# 以下代码仅在虚拟环境中执行
|
||||
# 检查安装依赖(只执行一次)
|
||||
check_dependencies(self.project_root)
|
||||
|
||||
# 启动应用
|
||||
print("\n" + "=" * 50)
|
||||
print("✓ 所有检查通过,准备启动应用...")
|
||||
|
||||
@@ -214,11 +214,12 @@ class EarthquakeDBN:
|
||||
预测结果
|
||||
"""
|
||||
point_id = point.get('id')
|
||||
source_id = point.get('source_id')
|
||||
lon = point.get('lon')
|
||||
lat = point.get('lat')
|
||||
source_type = point.get('source_type')
|
||||
|
||||
logger.debug(f"地震预测点 ID={point_id}, source_type={source_type}")
|
||||
logger.debug(f"地震预测点 ID={point_id}, source_id={source_id}, source_type={source_type}")
|
||||
|
||||
# 计算震中距(如果未直接提供)
|
||||
if epicenter_distance is None:
|
||||
@@ -270,6 +271,7 @@ class EarthquakeDBN:
|
||||
# 构造输出
|
||||
result = {
|
||||
'point_id': point_id,
|
||||
'source_id': source_id, # 隐患点/风险点的真实ID
|
||||
'source_type': source_type,
|
||||
'lon': lon,
|
||||
'lat': lat,
|
||||
@@ -376,6 +378,7 @@ class EarthquakeDBN:
|
||||
logger.error(f"预测点 {point.get('id')} 失败: {e}")
|
||||
results.append({
|
||||
'point_id': point.get('id'),
|
||||
'source_id': point.get('source_id'),
|
||||
'source_type': point.get('source_type'),
|
||||
'lon': point.get('lon'),
|
||||
'lat': point.get('lat'),
|
||||
@@ -423,6 +426,7 @@ class EarthquakeDBN:
|
||||
logger.error(f"预测点 {point.get('id')} 失败: {e}")
|
||||
results.append({
|
||||
'point_id': point.get('id'),
|
||||
'source_id': point.get('source_id'),
|
||||
'source_type': point.get('source_type'),
|
||||
'lon': point.get('lon'),
|
||||
'lat': point.get('lat'),
|
||||
|
||||
@@ -230,11 +230,12 @@ class RainfallDBN:
|
||||
预测结果
|
||||
"""
|
||||
point_id = point.get('id')
|
||||
source_id = point.get('source_id')
|
||||
lon = point.get('lon')
|
||||
lat = point.get('lat')
|
||||
source_type = point.get('source_type')
|
||||
|
||||
logger.debug(f"预测点 ID={point_id}, source_type={source_type}")
|
||||
logger.debug(f"预测点 ID={point_id}, source_id={source_id}, source_type={source_type}")
|
||||
|
||||
# 获取降雨数据
|
||||
if rainfall is not None and duration is not None:
|
||||
@@ -287,6 +288,7 @@ class RainfallDBN:
|
||||
# 构造输出
|
||||
result = {
|
||||
'point_id': point_id,
|
||||
'source_id': source_id, # 隐患点/风险点的真实ID
|
||||
'source_type': source_type,
|
||||
'lon': lon,
|
||||
'lat': lat,
|
||||
@@ -369,6 +371,7 @@ class RainfallDBN:
|
||||
logger.error(f"预测点 {point.get('id')} 失败: {e}")
|
||||
results.append({
|
||||
'point_id': point.get('id'),
|
||||
'source_id': point.get('source_id'),
|
||||
'source_type': point.get('source_type'),
|
||||
'lon': point.get('lon'),
|
||||
'lat': point.get('lat'),
|
||||
@@ -417,6 +420,7 @@ class RainfallDBN:
|
||||
logger.error(f"预测点 {point.get('id')} 失败: {e}")
|
||||
results.append({
|
||||
'point_id': point.get('id'),
|
||||
'source_id': point.get('source_id'),
|
||||
'source_type': point.get('source_type'),
|
||||
'lon': point.get('lon'),
|
||||
'lat': point.get('lat'),
|
||||
|
||||
+47
-50
@@ -1,81 +1,78 @@
|
||||
"""
|
||||
日志工具类
|
||||
支持按天分割、自动清理过期日志
|
||||
使用 loguru 提供增强的日志功能,支持按天分割、自动清理过期日志
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from datetime import datetime, timedelta
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class LoggerManager:
|
||||
"""日志管理器"""
|
||||
"""日志管理器 - 基于 loguru"""
|
||||
|
||||
_loggers = {}
|
||||
_configured = False
|
||||
|
||||
@classmethod
|
||||
def get_logger(cls, name: str = "algorithm", log_dir: str = "logs") -> logging.Logger:
|
||||
def get_logger(cls, name: str = "algorithm", log_dir: str = "logs"):
|
||||
"""
|
||||
获取日志记录器
|
||||
获取日志记录器(loguru 不需要传统意义上的 logger 实例)
|
||||
|
||||
Args:
|
||||
name: 日志名称(用于文件命名)
|
||||
log_dir: 日志目录
|
||||
|
||||
Returns:
|
||||
loguru.logger 实例
|
||||
"""
|
||||
if not cls._configured:
|
||||
cls._configure_logger(name, log_dir)
|
||||
return logger
|
||||
|
||||
@classmethod
|
||||
def _configure_logger(cls, name: str, log_dir: str):
|
||||
"""
|
||||
配置 loguru 日志处理器
|
||||
|
||||
Args:
|
||||
name: 日志名称
|
||||
log_dir: 日志目录
|
||||
|
||||
Returns:
|
||||
logging.Logger 实例
|
||||
"""
|
||||
if name in cls._loggers:
|
||||
return cls._loggers[name]
|
||||
# 移除默认的 stderr handler
|
||||
logger.remove()
|
||||
|
||||
# 创建日志目录
|
||||
log_path = Path(log_dir)
|
||||
log_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 创建 logger
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
# 避免重复添加 handler
|
||||
if logger.handlers:
|
||||
cls._loggers[name] = logger
|
||||
return logger
|
||||
|
||||
# 日志格式
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s [%(threadName)s] %(levelname)-5s %(name)s - %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
# 控制台 Handler - 彩色输出
|
||||
logger.add(
|
||||
sink=sys.stderr,
|
||||
level="INFO",
|
||||
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> [<cyan>{thread.name}</cyan>] <level>{level: <5}</level> <blue>{name}</blue> - <level>{message}</level>",
|
||||
colorize=True
|
||||
)
|
||||
|
||||
# 控制台 Handler
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.INFO)
|
||||
console_handler.setFormatter(formatter)
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
# 文件 Handler - 按天分割
|
||||
# 文件 Handler - 按大小分割,Windows 兼容
|
||||
log_file = log_path / f"{name}.log"
|
||||
file_handler = TimedRotatingFileHandler(
|
||||
filename=str(log_file),
|
||||
when='midnight',
|
||||
interval=1,
|
||||
backupCount=7,
|
||||
encoding='utf-8'
|
||||
logger.add(
|
||||
sink=str(log_file),
|
||||
level="DEBUG",
|
||||
format="{time:YYYY-MM-DD HH:mm:ss} [{thread.name}] {level: <5} {name} - {message}",
|
||||
rotation="50 MB", # 按大小轮转,避免Windows文件锁定问题
|
||||
retention="7 days", # 保留7天
|
||||
compression="zip", # 压缩旧日志文件
|
||||
encoding="utf-8",
|
||||
enqueue=True, # 异步写入,避免文件锁定
|
||||
delay=True, # 延迟打开文件,减少锁定时间
|
||||
backtrace=True, # 完整堆栈跟踪
|
||||
diagnose=True # 详细错误诊断
|
||||
)
|
||||
file_handler.setLevel(logging.DEBUG)
|
||||
file_handler.setFormatter(formatter)
|
||||
|
||||
# 设置日志文件命名格式
|
||||
file_handler.suffix = "%Y-%m-%d.log"
|
||||
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
cls._loggers[name] = logger
|
||||
return logger
|
||||
cls._configured = True
|
||||
|
||||
|
||||
# 便捷函数
|
||||
def get_logger(name: str = "algorithm", log_dir: str = "logs") -> logging.Logger:
|
||||
def get_logger(name: str = "algorithm", log_dir: str = "logs"):
|
||||
"""
|
||||
获取日志记录器的便捷函数
|
||||
|
||||
@@ -84,6 +81,6 @@ def get_logger(name: str = "algorithm", log_dir: str = "logs") -> logging.Logger
|
||||
log_dir: 日志目录
|
||||
|
||||
Returns:
|
||||
logging.Logger 实例
|
||||
loguru.logger 实例
|
||||
"""
|
||||
return LoggerManager.get_logger(name, log_dir)
|
||||
|
||||
+2
-1
@@ -7,4 +7,5 @@ matplotlib == 3.10.9
|
||||
Pillow == 12.2.0
|
||||
pyyaml == 6.0.3
|
||||
fastapi == 0.136.3
|
||||
uvicorn[standard] == 0.48.0
|
||||
uvicorn[standard] == 0.48.0
|
||||
loguru == 0.7.3
|
||||
Reference in New Issue
Block a user