mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-27 23:47:46 +00:00
c8f1c5364a
* change_log_object * lint code * delete comments * change_log_object * change_log_object * fix import test error * update code * update code * fix bugs * skip mypy error * skip mypy error * skip mypy error * Start the flask server before running the demo. * achieve front and back interaction * fix github-advanced-security comments * fix github-advanced-security comments * tmp ignore * fix CI * move some logic * change format * adjust logic * log2json changes * tmp * fix * fix bug * refine log2json between 5 scenarios * fix * refine codes * fix logic * use localhost * add loop & all_duration param for old scenario startup * merge control logic * add README for server ui api * update README * reuse code in logger * add loop_n and all_duration param * fix upload * ui server now use port in setting * fix port setting * fix port setting * fix mypy check * refine logger and log storage * fix ruff error * fix CI * refine logger, loop, storage * bind one FileStorage with one logger * not truncate log storage * refine LoopBase.load(), use `checkout` instead of `output_path` and `do_truncate` * clear session folder when loading loop to run * move component info init step to ExpGen Class * Update rdagent/utils/workflow.py * move truncate_session function to LoopBase class * add checkout param for other scenarios * fix bug * move WebStorage to UI * change web_storage name * add randomname to requirements * add typer * fix requirements --------- Co-authored-by: WinstonLiyte <1957922024@qq.com> Co-authored-by: Bowen Xian <xianbowen@outlook.com> Co-authored-by: you-n-g <you-n-g@users.noreply.github.com>
130 lines
4.1 KiB
Python
130 lines
4.1 KiB
Python
import os
|
|
import sys
|
|
from contextlib import contextmanager
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Generator
|
|
|
|
from loguru import logger
|
|
from psutil import Process
|
|
|
|
from rdagent.core.utils import SingletonBaseClass, import_class
|
|
|
|
from .base import Storage
|
|
from .conf import LOG_SETTINGS
|
|
from .storage import FileStorage
|
|
from .utils import get_caller_info
|
|
|
|
|
|
class RDAgentLog(SingletonBaseClass):
|
|
"""
|
|
The files are organized based on the tag & PID
|
|
Here is an example tag
|
|
|
|
.. code-block::
|
|
|
|
a
|
|
- b
|
|
- c
|
|
- 123
|
|
- common_logs.log
|
|
- 1322
|
|
- common_logs.log
|
|
- 1233
|
|
- <timestamp>.pkl
|
|
- d
|
|
- 1233-673 ...
|
|
- 1233-4563 ...
|
|
- 1233-365 ...
|
|
|
|
"""
|
|
|
|
# TODO: Simplify it to introduce less concepts ( We may merge RDAgentLog, Storage &)
|
|
# Solution: Storage => PipeLog, View => PipeLogView, RDAgentLog is an instance of PipeLogger
|
|
# PipeLogger.info(...) , PipeLogger.get_resp() to get feedback from frontend.
|
|
# def f():
|
|
# logger = PipeLog()
|
|
# logger.info("<code>")
|
|
# feedback = logger.get_reps()
|
|
_tag: str = ""
|
|
|
|
def __init__(self) -> None:
|
|
self.storage = FileStorage(LOG_SETTINGS.trace_path)
|
|
self.other_storages: list[Storage] = []
|
|
for storage, args in LOG_SETTINGS.storages.items():
|
|
storage_cls = import_class(storage)
|
|
self.other_storages.append(storage_cls(*args))
|
|
|
|
self.main_pid = os.getpid()
|
|
|
|
@contextmanager
|
|
def tag(self, tag: str) -> Generator[None, None, None]:
|
|
if tag.strip() == "":
|
|
raise ValueError("Tag cannot be empty.")
|
|
if self._tag != "":
|
|
tag = "." + tag
|
|
|
|
# TODO: It may result in error in mutithreading or co-routine
|
|
self._tag = self._tag + tag
|
|
try:
|
|
yield
|
|
finally:
|
|
self._tag = self._tag[: -len(tag)]
|
|
|
|
def set_storages_path(self, path: str | Path) -> None:
|
|
for storage in [self.storage] + self.other_storages:
|
|
if hasattr(storage, "path"):
|
|
storage.path = path
|
|
|
|
def truncate_storages(self, time: datetime) -> None:
|
|
for storage in [self.storage] + self.other_storages:
|
|
storage.truncate(time=time)
|
|
|
|
def get_pids(self) -> str:
|
|
"""
|
|
Returns a string of pids from the current process to the main process.
|
|
Split by '-'.
|
|
"""
|
|
pid = os.getpid()
|
|
process = Process(pid)
|
|
pid_chain = f"{pid}"
|
|
while process.pid != self.main_pid:
|
|
parent_pid = process.ppid()
|
|
parent_process = Process(parent_pid)
|
|
pid_chain = f"{parent_pid}-{pid_chain}"
|
|
process = parent_process
|
|
return pid_chain
|
|
|
|
def log_object(self, obj: object, *, tag: str = "") -> None:
|
|
# TODO: I think we can merge the log_object function with other normal log methods to make the interface simpler.
|
|
caller_info = get_caller_info()
|
|
tag = f"{self._tag}.{tag}.{self.get_pids()}".strip(".")
|
|
|
|
for storage in [self.storage] + self.other_storages:
|
|
logp = storage.log(obj, tag=tag)
|
|
logger.patch(lambda r: r.update(caller_info)).info(f"Log object to [{storage}], uri: {logp}")
|
|
|
|
def _log(self, level: str, msg: str, *, tag: str = "", raw: bool = False) -> None:
|
|
caller_info = get_caller_info()
|
|
tag = f"{self._tag}.{tag}.{self.get_pids()}".strip(".")
|
|
|
|
if raw:
|
|
logger.remove()
|
|
logger.add(sys.stderr, format=lambda r: "{message}")
|
|
|
|
log_func = getattr(logger.patch(lambda r: r.update(caller_info)), level)
|
|
log_func(msg)
|
|
|
|
if raw:
|
|
logger.remove()
|
|
logger.add(sys.stderr)
|
|
|
|
def info(self, msg: str, *, tag: str = "", raw: bool = False) -> None:
|
|
self._log("info", msg, tag=tag, raw=raw)
|
|
|
|
def warning(self, msg: str, *, tag: str = "", raw: bool = False) -> None:
|
|
self._log("warning", msg, tag=tag, raw=raw)
|
|
|
|
def error(self, msg: str, *, tag: str = "", raw: bool = False) -> None:
|
|
self._log("error", msg, tag=tag, raw=raw)
|