celestialflow 3.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- celestialflow/__init__.py +39 -0
- celestialflow/task_graph.py +665 -0
- celestialflow/task_logging.py +154 -0
- celestialflow/task_manage.py +1070 -0
- celestialflow/task_nodes.py +160 -0
- celestialflow/task_progress.py +57 -0
- celestialflow/task_report.py +162 -0
- celestialflow/task_structure.py +151 -0
- celestialflow/task_tools.py +501 -0
- celestialflow/task_types.py +61 -0
- celestialflow/task_web.py +170 -0
- celestialflow-3.0.1.dist-info/METADATA +301 -0
- celestialflow-3.0.1.dist-info/RECORD +15 -0
- celestialflow-3.0.1.dist-info/WHEEL +5 -0
- celestialflow-3.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
from multiprocessing import Queue as MPQueue
|
|
2
|
+
from queue import Empty
|
|
3
|
+
from threading import Thread
|
|
4
|
+
from time import localtime, strftime
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
from loguru import logger as loguru_logger
|
|
8
|
+
|
|
9
|
+
from .task_types import TerminationSignal, TERMINATION_SIGNAL
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LogListener:
|
|
13
|
+
"""
|
|
14
|
+
日志监听进程,用于将日志写入文件
|
|
15
|
+
"""
|
|
16
|
+
def __init__(self, level="INFO"):
|
|
17
|
+
now = strftime("%Y-%m-%d", localtime())
|
|
18
|
+
self.log_path = f"logs/task_logger({now}).log"
|
|
19
|
+
self.level = level
|
|
20
|
+
self.log_queue = MPQueue()
|
|
21
|
+
self._thread = Thread(target=self._listen, daemon=True)
|
|
22
|
+
|
|
23
|
+
def start(self):
|
|
24
|
+
# 配置 loguru 的两个 handler,stdout + file
|
|
25
|
+
loguru_logger.remove()
|
|
26
|
+
loguru_logger.add(
|
|
27
|
+
self.log_path,
|
|
28
|
+
level=self.level,
|
|
29
|
+
format="{time:YYYY-MM-DD HH:mm:ss} {level} {message}",
|
|
30
|
+
enqueue=True,
|
|
31
|
+
)
|
|
32
|
+
self._thread.start()
|
|
33
|
+
loguru_logger.debug("LogListener started.")
|
|
34
|
+
|
|
35
|
+
def _listen(self):
|
|
36
|
+
while True:
|
|
37
|
+
try:
|
|
38
|
+
record = self.log_queue.get(timeout=0.5)
|
|
39
|
+
if isinstance(record, TerminationSignal):
|
|
40
|
+
break
|
|
41
|
+
loguru_logger.log(record["level"], record["message"])
|
|
42
|
+
except Empty:
|
|
43
|
+
continue
|
|
44
|
+
# except Exception as e:
|
|
45
|
+
# loguru_logger.error(f"LogListener thread error: {type(e).__name__}({e})")
|
|
46
|
+
|
|
47
|
+
def get_queue(self):
|
|
48
|
+
return self.log_queue
|
|
49
|
+
|
|
50
|
+
def stop(self):
|
|
51
|
+
self.log_queue.put(TERMINATION_SIGNAL)
|
|
52
|
+
self._thread.join()
|
|
53
|
+
loguru_logger.debug("LogListener stopped.")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TaskLogger:
|
|
57
|
+
"""
|
|
58
|
+
多进程安全日志包装类,所有日志通过队列发送到监听进程写入
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, log_queue=None):
|
|
62
|
+
self.log_queue: MPQueue = log_queue
|
|
63
|
+
|
|
64
|
+
def _log(self, level: str, message: str):
|
|
65
|
+
self.log_queue.put({"level": level.upper(), "message": message})
|
|
66
|
+
|
|
67
|
+
# ==== manager ====
|
|
68
|
+
def start_manager(self, func_name, task_num, execution_mode, worker_limit):
|
|
69
|
+
text = f"'{func_name}' start {task_num} tasks by {execution_mode}"
|
|
70
|
+
text += f"({worker_limit} workers)." if execution_mode != "serial" else "."
|
|
71
|
+
self._log("INFO", text)
|
|
72
|
+
|
|
73
|
+
def end_manager(
|
|
74
|
+
self,
|
|
75
|
+
func_name,
|
|
76
|
+
execution_mode,
|
|
77
|
+
use_time,
|
|
78
|
+
success_num,
|
|
79
|
+
failed_num,
|
|
80
|
+
duplicated_num,
|
|
81
|
+
):
|
|
82
|
+
self._log(
|
|
83
|
+
"INFO",
|
|
84
|
+
f"'{func_name}' end tasks by {execution_mode}. Use {use_time:.2f} second. "
|
|
85
|
+
f"{success_num} tasks successed, {failed_num} tasks failed, {duplicated_num} tasks duplicated.",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# ==== stage ====
|
|
89
|
+
def start_stage(self, stage_name, func_name, execution_mode, worker_limit):
|
|
90
|
+
text = f"The {stage_name} in '{func_name}' start tasks by {execution_mode}"
|
|
91
|
+
text += f"({worker_limit} workers)." if execution_mode != "serial" else "."
|
|
92
|
+
self._log("INFO", text)
|
|
93
|
+
|
|
94
|
+
def end_stage(
|
|
95
|
+
self,
|
|
96
|
+
stage_name,
|
|
97
|
+
func_name,
|
|
98
|
+
execution_mode,
|
|
99
|
+
use_time,
|
|
100
|
+
success_num,
|
|
101
|
+
failed_num,
|
|
102
|
+
duplicated_num,
|
|
103
|
+
):
|
|
104
|
+
self._log(
|
|
105
|
+
"INFO",
|
|
106
|
+
f"The {stage_name} in '{func_name}' end tasks by {execution_mode}. Use {use_time:.2f} second. "
|
|
107
|
+
f"{success_num} tasks successed, {failed_num} tasks failed, {duplicated_num} tasks duplicated.",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# ==== layer ====
|
|
111
|
+
def start_layer(self, layer: List[str], layer_level: int):
|
|
112
|
+
self._log("INFO", f"Layer {layer} start. Layer level: {layer_level}.")
|
|
113
|
+
|
|
114
|
+
def end_layer(self, layer: List[str], use_time: float):
|
|
115
|
+
self._log("INFO", f"Layer {layer} end. Use {use_time:.2f} second.")
|
|
116
|
+
|
|
117
|
+
# ==== graph ====
|
|
118
|
+
def start_graph(self, stage_structure):
|
|
119
|
+
self._log("INFO", f"Starting TaskGraph stages. Graph structure:")
|
|
120
|
+
for line in stage_structure:
|
|
121
|
+
self._log("INFO", line)
|
|
122
|
+
|
|
123
|
+
def end_graph(self, use_time):
|
|
124
|
+
self._log("INFO", f"TaskGraph end. Use {use_time:.2f} second.")
|
|
125
|
+
|
|
126
|
+
# ==== task ====
|
|
127
|
+
def task_success(self, func_name, task_info, execution_mode, result_info, use_time):
|
|
128
|
+
self._log(
|
|
129
|
+
"SUCCESS",
|
|
130
|
+
f"In '{func_name}', Task {task_info} completed by {execution_mode}. Result is {result_info}. Used {use_time:.2f} seconds.",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def task_retry(self, func_name, task_info, retry_times, exception):
|
|
134
|
+
self._log(
|
|
135
|
+
"WARNING",
|
|
136
|
+
f"In '{func_name}', Task {task_info} failed {retry_times} times and will retry: ({type(exception).__name__}).",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def task_error(self, func_name, task_info, exception):
|
|
140
|
+
exception_text = str(exception).replace("\n", " ")
|
|
141
|
+
self._log(
|
|
142
|
+
"ERROR",
|
|
143
|
+
f"In '{func_name}', Task {task_info} failed and can't retry: ({type(exception).__name__}){exception_text}.",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
def task_duplicate(self, func_name, task_info):
|
|
147
|
+
self._log("SUCCESS", f"In '{func_name}', Task {task_info} has been duplicated.")
|
|
148
|
+
|
|
149
|
+
# ==== splitter task ====
|
|
150
|
+
def splitter_success(self, func_name, task_info, split_count, use_time):
|
|
151
|
+
self._log(
|
|
152
|
+
"SUCCESS",
|
|
153
|
+
f"In '{func_name}', Task {task_info} has split into {split_count} parts. Used {use_time:.2f} seconds.",
|
|
154
|
+
)
|