algo-backend-framework 0.0.5__tar.gz → 0.0.6__tar.gz

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.
Files changed (59) hide show
  1. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/PKG-INFO +2 -3
  2. algo_backend_framework-0.0.6/algo_backend/config/__init__.py +3 -0
  3. algo_backend_framework-0.0.6/algo_backend/config/basic_config.py +32 -0
  4. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/handler/sse_handler.py +1 -1
  5. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/log/__init__.py +4 -1
  6. algo_backend_framework-0.0.6/algo_backend/log/common.py +28 -0
  7. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/metrics/prometheus_context.py +0 -13
  8. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/metrics/time_cost_metrics.py +3 -1
  9. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/schema/sse.py +2 -3
  10. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/starter/__init__.py +2 -1
  11. algo_backend_framework-0.0.6/algo_backend/starter/app_mounter.py +31 -0
  12. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/starter/default_app_generator.py +18 -26
  13. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/starter/default_service_starter.py +9 -13
  14. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend_framework.egg-info/PKG-INFO +2 -3
  15. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend_framework.egg-info/SOURCES.txt +1 -7
  16. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend_framework.egg-info/requires.txt +1 -2
  17. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/pyproject.toml +2 -3
  18. algo_backend_framework-0.0.5/algo_backend/config/__init__.py +0 -8
  19. algo_backend_framework-0.0.5/algo_backend/config/basic_config.py +0 -13
  20. algo_backend_framework-0.0.5/algo_backend/config/loguru_config.py +0 -19
  21. algo_backend_framework-0.0.5/algo_backend/log/common.py +0 -16
  22. algo_backend_framework-0.0.5/algo_backend/log/loguru/__init__.py +0 -5
  23. algo_backend_framework-0.0.5/algo_backend/log/loguru/log_clean.py +0 -140
  24. algo_backend_framework-0.0.5/algo_backend/log/loguru/log_setup.py +0 -89
  25. algo_backend_framework-0.0.5/algo_backend/log/loguru/log_starter.py +0 -65
  26. algo_backend_framework-0.0.5/algo_backend/log/loguru/patch_logging.py +0 -83
  27. algo_backend_framework-0.0.5/algo_backend/log/nblog/__init__.py +0 -0
  28. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/README.md +0 -0
  29. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/__init__.py +0 -0
  30. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/exception/__init__.py +0 -0
  31. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/exception/error_code_manage.py +0 -0
  32. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/exception/exception.py +0 -0
  33. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/exception/status_code.py +0 -0
  34. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/handler/__init__.py +0 -0
  35. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/handler/exception_to_vo.py +0 -0
  36. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/handler/simple_handler.py +0 -0
  37. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/intercept/__init__.py +0 -0
  38. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/intercept/common.py +0 -0
  39. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/intercept/http.py +0 -0
  40. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/intercept/validate.py +0 -0
  41. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/metrics/__init__.py +0 -0
  42. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/metrics/collector/__init__.py +0 -0
  43. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/metrics/collector/common.py +0 -0
  44. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/metrics/collector/gc_metrics.py +0 -0
  45. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/metrics/collector/schedule_monitor.py +0 -0
  46. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/metrics/collector/system_metrics.py +0 -0
  47. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/metrics/http_metrics.py +0 -0
  48. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/middleware/__init__.py +0 -0
  49. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/middleware/cors.py +0 -0
  50. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/middleware/metrics.py +0 -0
  51. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/schema/__init__.py +0 -0
  52. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/schema/vo.py +0 -0
  53. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/starter/event_list.py +0 -0
  54. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/utils/__init__.py +0 -0
  55. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/utils/meta_class.py +0 -0
  56. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend/utils/utils.py +0 -0
  57. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend_framework.egg-info/dependency_links.txt +0 -0
  58. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/algo_backend_framework.egg-info/top_level.txt +0 -0
  59. {algo_backend_framework-0.0.5 → algo_backend_framework-0.0.6}/setup.cfg +0 -0
@@ -1,15 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: algo-backend-framework
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: Ctcdn algorithm backend framework
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
7
7
  Requires-Dist: fastapi>=0.128.0
8
- Requires-Dist: loguru>=0.7.3
9
8
  Requires-Dist: prometheus-client>=0.24.1
10
9
  Requires-Dist: psutil>=7.2.1
11
10
  Requires-Dist: pydantic>=2.12.5
12
- Requires-Dist: python-dotenv>=1.2.1
11
+ Requires-Dist: pydantic-settings>=2.12.0
13
12
  Requires-Dist: uvicorn>=0.40.0
14
13
 
15
14
  # algo-backend-framework-python
@@ -0,0 +1,3 @@
1
+ from .basic_config import ErrorCodeConfig, ServiceConfig, CommonLogConfig
2
+
3
+ __all__ = ["ServiceConfig", "ErrorCodeConfig", "CommonLogConfig"]
@@ -0,0 +1,32 @@
1
+ import os
2
+
3
+ from pydantic_settings import BaseSettings
4
+
5
+
6
+ class BasicConfig(BaseSettings):
7
+ class Config:
8
+ env_file = ".env"
9
+ case_sensitive = False
10
+ extra = "allow"
11
+
12
+
13
+ class ErrorCodeConfig:
14
+ SERVICE_PREFIX: int = os.getenv("ERROR_CODE_SERVICE_PREFIX", "0")
15
+
16
+
17
+ class ServiceConfig(BasicConfig):
18
+ HTTP_PORT: int = 8100
19
+ TIMEOUT_KEEP_ALIVE: int = 1000
20
+ PROCESS_NUM: int = 1
21
+
22
+
23
+ class CommonLogConfig(BasicConfig):
24
+ """
25
+ LOGGER_PATH: 日志文件路径
26
+ LOG_RETENTION_DAY: 日志保留天数
27
+ SAVE_LOG: 是否保存日志
28
+ """
29
+
30
+ LOGGER_PATH: str = "/logger"
31
+ LOG_RETENTION_DAY: int = 60
32
+ SAVE_LOG: bool = True
@@ -25,7 +25,7 @@ def sse_timing_and_exception_handler(
25
25
  transfer_obj_cls=type(SseVoGenerator),
26
26
  *,
27
27
  api_id: BasicApiId = DefaultApiErrorCode.DEFAULT_ERROR,
28
- api_name: str = ""
28
+ api_name: str = "",
29
29
  ):
30
30
  def decorator(
31
31
  func: Callable[..., AsyncIterable[D]],
@@ -1 +1,4 @@
1
- from .common import BasicLogStarter
1
+ from .common import BasicLogStarter
2
+
3
+
4
+ __all__ = ["BasicLogStarter"]
@@ -0,0 +1,28 @@
1
+ class BasicLogStarter:
2
+ """
3
+ 日志启动器,初始化日志格式和落盘等
4
+ """
5
+
6
+ def __init__(self, service_name: str):
7
+ """
8
+ :param service_name: 服务名
9
+ """
10
+ self.service_name = service_name
11
+
12
+ def setup_log(self):
13
+ """
14
+ 执行一些日志设置
15
+ """
16
+ ...
17
+
18
+ def app_generator_hook(self, app_generate):
19
+ """
20
+ 钩子函数,传入对象类型DefaultAlgoAppGenerator
21
+ """
22
+ ...
23
+
24
+ def service_generator_hook(self, service_generate):
25
+ """
26
+ 钩子函数:传入对象类型DefaultAlgoServiceStarter
27
+ """
28
+ ...
@@ -2,8 +2,6 @@ import logging
2
2
  import os
3
3
  import shutil
4
4
 
5
- from fastapi import FastAPI
6
-
7
5
  logger = logging.getLogger(__name__)
8
6
 
9
7
 
@@ -42,14 +40,3 @@ class PrometheusContext:
42
40
 
43
41
  os.makedirs(prom_dir, exist_ok=True)
44
42
  logger.info(f"Created new Prometheus multiprocessing directory: {prom_dir}")
45
-
46
- @classmethod
47
- def mount_prometheus_endpoint(cls, app: FastAPI):
48
- """
49
- Mount the Prometheus metrics endpoint on the given application.
50
- """
51
- from prometheus_client import CollectorRegistry, make_asgi_app, multiprocess
52
-
53
- registry = CollectorRegistry()
54
- multiprocess.MultiProcessCollector(registry=registry)
55
- app.mount("/metrics", make_asgi_app(registry=registry))
@@ -174,7 +174,9 @@ class PrometheusTimeCostMetricSetting:
174
174
  elif inspect.iscoroutinefunction(func):
175
175
  return cls._create_async_func_wrapper(func, metrics_cls_name, key)
176
176
  else:
177
- raise ValueError(f"{func.__name__} is not a async function or async generator")
177
+ raise ValueError(
178
+ f"{func.__name__} is not a async function or async generator"
179
+ )
178
180
 
179
181
  return decorator
180
182
 
@@ -10,7 +10,7 @@ T = TypeVar("T", bound=BaseModel)
10
10
 
11
11
 
12
12
  class SseVoGenerator(ABC):
13
- def __init__(self, request_id: str=None):
13
+ def __init__(self, request_id: str = None):
14
14
  self.request_id = request_id
15
15
 
16
16
  @abstractmethod
@@ -36,5 +36,4 @@ class SseVoGenerator(ABC):
36
36
  ...
37
37
 
38
38
  @abstractmethod
39
- def error(self, exception: BasicException) -> T:
40
- ...
39
+ def error(self, exception: BasicException) -> T: ...
@@ -1,4 +1,5 @@
1
1
  from .default_app_generator import DefaultAlgoAppGenerator
2
2
  from .default_service_starter import DefaultAlgoServiceStarter
3
+ from .app_mounter import AbstractAppMounter
3
4
 
4
- __all__ = ["DefaultAlgoAppGenerator", "DefaultAlgoServiceStarter"]
5
+ __all__ = ["DefaultAlgoAppGenerator", "DefaultAlgoServiceStarter", "AbstractAppMounter"]
@@ -0,0 +1,31 @@
1
+ from abc import abstractmethod, ABC
2
+
3
+ from fastapi import FastAPI
4
+
5
+
6
+ class AbstractAppMounter(ABC):
7
+ """
8
+ App接口挂载点
9
+ """
10
+
11
+ def __init__(self, service_name: str):
12
+ self.service_name = service_name
13
+
14
+ @abstractmethod
15
+ def mount_app(self, app: FastAPI): ...
16
+
17
+
18
+ class PrometheusAppMounter(AbstractAppMounter):
19
+ """
20
+ Prometheus metrics endpoint 挂载点
21
+ """
22
+
23
+ def mount_app(self, app: FastAPI):
24
+ """
25
+ Mount the Prometheus metrics endpoint on the given application.
26
+ """
27
+ from prometheus_client import CollectorRegistry, make_asgi_app, multiprocess
28
+
29
+ registry = CollectorRegistry()
30
+ multiprocess.MultiProcessCollector(registry=registry)
31
+ app.mount("/metrics", make_asgi_app(registry=registry))
@@ -13,13 +13,11 @@ from algo_backend.intercept import (
13
13
  ValidateExceptionInterceptor,
14
14
  )
15
15
  from algo_backend.log import BasicLogStarter
16
- from algo_backend.metrics import (
17
- PrometheusContext,
18
- PrometheusTimeCostMetricSetting,
19
- )
16
+ from algo_backend.metrics import PrometheusTimeCostMetricSetting
20
17
  from algo_backend.metrics.collector import MetricsScheduleMonitor
21
18
  from algo_backend.middleware import default_cors_middleware
22
19
 
20
+ from .app_mounter import AbstractAppMounter, PrometheusAppMounter
23
21
  from .event_list import EventList
24
22
 
25
23
  logger = logging.getLogger(__name__)
@@ -37,7 +35,7 @@ class DefaultAlgoAppGenerator:
37
35
  app_error_code_prefix: Optional[int] = None,
38
36
  custom_start_event: List[Callable[[], Coroutine[Any, Any, None]]] = None,
39
37
  custom_end_event: List[Callable[[], Coroutine[Any, Any, None]]] = None,
40
- log_starter: Optional[BasicLogStarter] = None,
38
+ log_starter: Optional[type(BasicLogStarter)] = BasicLogStarter,
41
39
  intercept_dict: Optional[Dict[Exception, BasicExceptionInterceptor]] = None,
42
40
  middlewares: List[Middleware] = (default_cors_middleware,),
43
41
  api_time_cost_buckets: Optional[List[int]] = None,
@@ -63,7 +61,7 @@ class DefaultAlgoAppGenerator:
63
61
  self.start_event_list = EventList()
64
62
  self.end_event_list = EventList()
65
63
 
66
- self.log_starter = log_starter or BasicLogStarter(service_name=service_name)
64
+ self.log_starter: BasicLogStarter = log_starter(service_name=service_name)
67
65
 
68
66
  self.intercept_dict = {
69
67
  RequestValidationError: ValidateExceptionInterceptor(),
@@ -73,6 +71,10 @@ class DefaultAlgoAppGenerator:
73
71
 
74
72
  self.middleware_list = list(middlewares)
75
73
 
74
+ self.app_mounter_list: List[type(AbstractAppMounter)] = [
75
+ PrometheusAppMounter,
76
+ ]
77
+
76
78
  self.time_cost_metrics_initializer = (
77
79
  lambda: PrometheusTimeCostMetricSetting.initialize(
78
80
  api_metrics_buckets=api_time_cost_buckets,
@@ -117,26 +119,20 @@ class DefaultAlgoAppGenerator:
117
119
  self.middleware_list.append(middleware)
118
120
  return self
119
121
 
120
- def set_log_stater(self, log_starter: BasicLogStarter) -> "DefaultAlgoAppGenerator":
121
- self.log_starter = log_starter
122
- return self
123
-
124
- def use_log_loguru(self):
125
- """
126
- 使用loguru日志框架
127
- """
128
- from algo_backend.log.loguru import LoguruStarter
122
+ def append_mounter(self, mounter_cls: type(AbstractAppMounter)):
123
+ self.app_mounter_list.append(mounter_cls)
129
124
 
130
- return self.set_log_stater(LoguruStarter(service_name=self.service_name))
125
+ def add_mounter(self) -> "DefaultAlgoAppGenerator":
126
+ for mounter_cls in self.app_mounter_list:
127
+ mounter_cls(self.service_name).mount_app(self.__app)
128
+ return self
131
129
 
132
- def use_log_nblog(self):
130
+ def generate(self):
133
131
  """
134
- 使用nblog日志框架,待实现
132
+ 主方法
133
+ :return:
135
134
  """
136
- ...
137
-
138
- def generate(self):
139
- self.init_app().add_prometheus_endpoint().add_interceptor()
135
+ self.init_app().add_interceptor().add_mounter()
140
136
  return self.app
141
137
 
142
138
  @property
@@ -149,10 +145,6 @@ class DefaultAlgoAppGenerator:
149
145
  )
150
146
  return self
151
147
 
152
- def add_prometheus_endpoint(self):
153
- PrometheusContext.mount_prometheus_endpoint(self.__app)
154
- return self
155
-
156
148
  def add_interceptor(self):
157
149
  for exc, interceptor in self.intercept_dict.items():
158
150
  self.__app.add_exception_handler(exc, interceptor.intercept)
@@ -4,6 +4,7 @@ import uvicorn
4
4
 
5
5
  from algo_backend.config import ServiceConfig
6
6
  from algo_backend.metrics import PrometheusContext
7
+ from algo_backend.log import BasicLogStarter
7
8
 
8
9
 
9
10
  class DefaultAlgoServiceStarter:
@@ -32,16 +33,9 @@ class DefaultAlgoServiceStarter:
32
33
  self.__main_pid_event.insert(index, func)
33
34
  return self
34
35
 
35
- def use_loguru(self):
36
- """
37
- 使用loguru日志设置,对主进程的日志生效
38
- 注册loguru的定时清理器
39
- """
40
- from algo_backend.log.loguru import LoguruStarter
41
-
42
- loguru_stater = LoguruStarter(service_name=self.service_name)
43
- self.insert_event(loguru_stater.setup_log)
44
- self.append_event(loguru_stater.run_log_cleaner)
36
+ def use_log_stater(self, log_starter_class: type(BasicLogStarter)):
37
+ log_starter: BasicLogStarter = log_starter_class(service_name=self.service_name)
38
+ log_starter.service_generator_hook(self)
45
39
  return self
46
40
 
47
41
  def main_pid_setup(self) -> "DefaultAlgoServiceStarter":
@@ -59,12 +53,14 @@ class DefaultAlgoServiceStarter:
59
53
  host: str = "0.0.0.0",
60
54
  **kwargs,
61
55
  ):
56
+ service_config = ServiceConfig()
62
57
  # 启动服务
63
58
  uvicorn.run(
64
59
  app_str,
65
60
  host=host,
66
- port=ServiceConfig.HTTP_PORT,
67
- timeout_keep_alive=ServiceConfig.TIMEOUT_KEEP_ALIVE,
68
- workers=ServiceConfig.PROCESS_NUM,
61
+ port=service_config.HTTP_PORT,
62
+ timeout_keep_alive=service_config.TIMEOUT_KEEP_ALIVE,
63
+ workers=service_config.PROCESS_NUM,
64
+ log_config=None,
69
65
  **kwargs,
70
66
  )
@@ -1,15 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: algo-backend-framework
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: Ctcdn algorithm backend framework
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
7
7
  Requires-Dist: fastapi>=0.128.0
8
- Requires-Dist: loguru>=0.7.3
9
8
  Requires-Dist: prometheus-client>=0.24.1
10
9
  Requires-Dist: psutil>=7.2.1
11
10
  Requires-Dist: pydantic>=2.12.5
12
- Requires-Dist: python-dotenv>=1.2.1
11
+ Requires-Dist: pydantic-settings>=2.12.0
13
12
  Requires-Dist: uvicorn>=0.40.0
14
13
 
15
14
  # algo-backend-framework-python
@@ -3,7 +3,6 @@ pyproject.toml
3
3
  algo_backend/__init__.py
4
4
  algo_backend/config/__init__.py
5
5
  algo_backend/config/basic_config.py
6
- algo_backend/config/loguru_config.py
7
6
  algo_backend/exception/__init__.py
8
7
  algo_backend/exception/error_code_manage.py
9
8
  algo_backend/exception/exception.py
@@ -18,12 +17,6 @@ algo_backend/intercept/http.py
18
17
  algo_backend/intercept/validate.py
19
18
  algo_backend/log/__init__.py
20
19
  algo_backend/log/common.py
21
- algo_backend/log/loguru/__init__.py
22
- algo_backend/log/loguru/log_clean.py
23
- algo_backend/log/loguru/log_setup.py
24
- algo_backend/log/loguru/log_starter.py
25
- algo_backend/log/loguru/patch_logging.py
26
- algo_backend/log/nblog/__init__.py
27
20
  algo_backend/metrics/__init__.py
28
21
  algo_backend/metrics/http_metrics.py
29
22
  algo_backend/metrics/prometheus_context.py
@@ -40,6 +33,7 @@ algo_backend/schema/__init__.py
40
33
  algo_backend/schema/sse.py
41
34
  algo_backend/schema/vo.py
42
35
  algo_backend/starter/__init__.py
36
+ algo_backend/starter/app_mounter.py
43
37
  algo_backend/starter/default_app_generator.py
44
38
  algo_backend/starter/default_service_starter.py
45
39
  algo_backend/starter/event_list.py
@@ -1,7 +1,6 @@
1
1
  fastapi>=0.128.0
2
- loguru>=0.7.3
3
2
  prometheus-client>=0.24.1
4
3
  psutil>=7.2.1
5
4
  pydantic>=2.12.5
6
- python-dotenv>=1.2.1
5
+ pydantic-settings>=2.12.0
7
6
  uvicorn>=0.40.0
@@ -1,16 +1,15 @@
1
1
  [project]
2
2
  name = "algo-backend-framework"
3
- version = "0.0.5"
3
+ version = "0.0.6"
4
4
  description = "Ctcdn algorithm backend framework"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
7
7
  dependencies = [
8
8
  "fastapi>=0.128.0",
9
- "loguru>=0.7.3",
10
9
  "prometheus-client>=0.24.1",
11
10
  "psutil>=7.2.1",
12
11
  "pydantic>=2.12.5",
13
- "python-dotenv>=1.2.1",
12
+ "pydantic-settings>=2.12.0",
14
13
  "uvicorn>=0.40.0",
15
14
  ]
16
15
 
@@ -1,8 +0,0 @@
1
- from dotenv import load_dotenv
2
-
3
- load_dotenv(".env")
4
-
5
- from .basic_config import ErrorCodeConfig, ServiceConfig
6
- from .loguru_config import LoguruConfig
7
-
8
- __all__ = ["LoguruConfig", "ServiceConfig", "ErrorCodeConfig"]
@@ -1,13 +0,0 @@
1
- import os
2
-
3
- from algo_backend.utils import OsAttrMeta
4
-
5
-
6
- class ErrorCodeConfig:
7
- SERVICE_PREFIX: int = os.getenv("ERROR_CODE_SERVICE_PREFIX", "0")
8
-
9
-
10
- class ServiceConfig(metaclass=OsAttrMeta):
11
- HTTP_PORT: int = 8100
12
- TIMEOUT_KEEP_ALIVE: int = 1000
13
- PROCESS_NUM: int = 1
@@ -1,19 +0,0 @@
1
- from typing import List
2
-
3
- from algo_backend.utils import OsAttrMeta
4
-
5
-
6
- class LoguruConfig(metaclass=OsAttrMeta):
7
- LOGGER_PATH: str = "/logger"
8
- LOG_RETENTION_DAY: int = 60
9
- DISABLE_LOG_PKG: str = ""
10
- LOG_ADD_CONTAINED_ID: bool = False
11
- SAVE_INFO_LEVEL: bool = False
12
- SAVE_DEBUG_LOG: bool = True
13
-
14
- @classmethod
15
- def get_disable_log_pkg(cls) -> List[str]:
16
- if cls.DISABLE_LOG_PKG:
17
- return cls.DISABLE_LOG_PKG.split(",")
18
- else:
19
- return []
@@ -1,16 +0,0 @@
1
- class BasicLogStarter:
2
- """
3
- 日志启动器,初始化日志格式和落盘等
4
- """
5
-
6
- def __init__(self, service_name: str):
7
- """
8
- :param service_name: 服务名
9
- """
10
- self.service_name = service_name
11
-
12
- def setup_log(self):
13
- """
14
- 执行一些日志设置
15
- """
16
- pass
@@ -1,5 +0,0 @@
1
- from .log_clean import LoguruCleaner
2
- from .log_setup import LoguruSetup
3
- from .log_starter import LoguruStarter
4
-
5
- __all__ = ["LoguruCleaner", "LoguruSetup", "LoguruStarter"]
@@ -1,140 +0,0 @@
1
- import os
2
- import threading
3
- import time
4
- from datetime import datetime, timedelta
5
- from typing import List, Optional
6
-
7
- from loguru import logger
8
- from pydantic import BaseModel, Field
9
-
10
-
11
- class LogInfo(BaseModel):
12
- """
13
- 日志信息
14
- """
15
-
16
- file_path: str = Field(..., description="日志文件路径")
17
- file_size: Optional[int] = None # 日志文件大小
18
- create_time: Optional[datetime] = None # 日志文件创建时间
19
- modify_time: Optional[datetime] = None # 日志文件修改时间
20
- access_time: Optional[datetime] = None # 日志文件访问时间
21
-
22
- @classmethod
23
- def gen(cls, file_path: str):
24
- if not os.path.exists(file_path):
25
- return cls(file_path=file_path)
26
- return cls(
27
- file_path=file_path,
28
- file_size=os.path.getsize(file_path),
29
- create_time=datetime.fromtimestamp(os.path.getctime(file_path)),
30
- modify_time=datetime.fromtimestamp(os.path.getmtime(file_path)),
31
- access_time=datetime.fromtimestamp(os.path.getatime(file_path)),
32
- )
33
-
34
-
35
- class LoguruCleaner:
36
- """
37
- 清理时间较长的日志,默认保留60天的日志
38
- """
39
-
40
- def __init__(self, log_dir: str, retention_day: int = 60):
41
- """
42
- :param log_dir: 日志目录
43
- :param retention_day: 保留天数
44
- """
45
- self.retention_day = retention_day
46
- self.log_dir = log_dir
47
- self.current_time: datetime = datetime.now()
48
- self.threshold_time: datetime = self.current_time - timedelta(
49
- days=self.retention_day
50
- )
51
-
52
- def scan_log(self) -> List[LogInfo]:
53
- """
54
- 扫描日志目录,返回所有日志文件
55
- """
56
- log_files = []
57
- if os.path.exists(self.log_dir):
58
- log_files = [
59
- LogInfo.gen(os.path.join(self.log_dir, file))
60
- for file in os.listdir(self.log_dir)
61
- if file.endswith(".log")
62
- ]
63
- return log_files
64
-
65
- def judge_is_old_log(self, log_file: LogInfo) -> bool:
66
- """
67
- 判断日志文件是否过期
68
- 过期标准:
69
- """
70
- if log_file.modify_time and log_file.modify_time < self.threshold_time:
71
- return True
72
- return False
73
-
74
- def extract_old_log(self) -> List[LogInfo]:
75
- """
76
- 提取过期的日志文件
77
- """
78
- log_files = self.scan_log()
79
-
80
- if not log_files:
81
- logger.debug("No log files found")
82
- return []
83
-
84
- logger.debug(
85
- f"[ExtractOldLogFiles]: scan [{len(log_files)}] logs from [{self.log_dir}]"
86
- )
87
-
88
- old_log_files = [o for o in log_files if self.judge_is_old_log(o)]
89
-
90
- logger.debug(
91
- f"[ExtractOldLogFiles]: extract [{len(old_log_files)}] old logs, threshold day [{self.threshold_time}]"
92
- )
93
-
94
- return old_log_files
95
-
96
- def delete_log(self, log_file: List[LogInfo]):
97
- """
98
- 删除日志文件
99
- """
100
- if log_file:
101
- logger.debug(f"[DeleteLogFiles]: Start to delete [{len(log_file)}] logs")
102
- cnt = 0
103
- for log in log_file:
104
- try:
105
- os.remove(log.file_path)
106
- logger.debug(f"[DeleteLogFiles]: Delete log [{log.file_path}]")
107
- cnt += 1
108
- except FileNotFoundError as e:
109
- logger.warning(
110
- f"[DeleteLogFiles]: Failed to delete log [{log.file_path}], error [{e}]"
111
- )
112
-
113
- logger.debug(f"[DeleteLogFiles]: Delete [{cnt}]/[{len(log_file)}] logs")
114
-
115
- def delete_lod_log(self):
116
- """
117
- 删除过时的日志文件
118
- """
119
- old_logs = self.extract_old_log()
120
- self.delete_log(old_logs)
121
-
122
- @classmethod
123
- def schedule_run(cls, log_dir: str, retention_day: int = 60):
124
- """
125
- 启动线程,定时清理日志
126
- """
127
- # 创建线程
128
- interval = 60 * 60 * 24
129
-
130
- def worker():
131
- while True:
132
- log_cleaner = LoguruCleaner(log_dir, retention_day)
133
- logger.debug(
134
- f"pid={os.getpid()} | [ScheduleRun]: Start to run log cleaner, log dir [{log_dir}], retention day [{retention_day}]"
135
- )
136
- log_cleaner.delete_lod_log()
137
- time.sleep(interval) # 暂停一天
138
-
139
- thread = threading.Thread(target=worker, daemon=True)
140
- thread.start()
@@ -1,89 +0,0 @@
1
- import os
2
- import sys
3
- from typing import Optional
4
-
5
- from loguru import logger
6
-
7
- from .patch_logging import patch_logging_to_loguru
8
-
9
-
10
- class LoguruSetup:
11
- FORMAT = (
12
- "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
13
- "<level>{level: <8}</level> | "
14
- "<cyan>p-{process}</cyan> | "
15
- "<cyan>t-{thread}</cyan> | "
16
- "<cyan>{thread.name}</cyan> | "
17
- "<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
18
- "<level>{message}</level>"
19
- )
20
-
21
- __IS_SET_ROTATE = False
22
-
23
- @classmethod
24
- def rotate_daily(
25
- cls,
26
- *,
27
- log_dir: str,
28
- service_name: str,
29
- add_pid_suffix: bool = True,
30
- save_info: bool = True,
31
- save_debug: bool = True,
32
- stderr_colorize: bool = True,
33
- run_id_suffix: Optional[str] = None,
34
- ):
35
- """
36
- 日志输出终端和落盘
37
- : params: log_dir: 日志目录
38
- : params: service_name: 服务名
39
- : params: add_pid_suffix: 是否添加进程ID后缀
40
- : params: save_info: 是否保存INFO级别日志
41
- : params: save_debug: 是否保存DEBUG级别日志
42
- : params: stderr_colorize: 是否启用终端颜色显示
43
- : params: run_id_suffix: 运行ID后缀,用于避免多副本时的冲突
44
- """
45
- if cls.__IS_SET_ROTATE:
46
- return
47
-
48
- logger.remove() # 清空设置,防止重复
49
-
50
- os.makedirs(log_dir, exist_ok=True)
51
- pid_suffix = f"_{os.getpid()}" if add_pid_suffix else ""
52
- run_id_suffix = f"_r{run_id_suffix}" if run_id_suffix else ""
53
-
54
- # 添加终端处理器(控制台输出)
55
- logger.add(
56
- sink=sys.stderr, # 输出到标准错误流
57
- level="DEBUG", # 终端显示更详细的DEBUG日志
58
- format=cls.FORMAT,
59
- colorize=stderr_colorize, # 启用颜色显示
60
- backtrace=True, # 堆栈信息显示在终端
61
- )
62
-
63
- if save_info:
64
- # 配置 INFO 及以上级别日志
65
- logger.add(
66
- os.path.join(
67
- log_dir, f"{service_name}_info{pid_suffix}{run_id_suffix}.log"
68
- ),
69
- rotation="1 day", # 每日滚动
70
- filter=lambda record: record["level"].no >= 20,
71
- format=cls.FORMAT,
72
- enqueue=True,
73
- )
74
-
75
- if save_debug:
76
- # 配置 DEBUG 级别日志
77
- logger.add(
78
- os.path.join(
79
- log_dir, f"{service_name}_debug{pid_suffix}{run_id_suffix}.log"
80
- ),
81
- rotation="1 day", # 每日滚动
82
- level="DEBUG",
83
- filter=lambda record: record["level"].no >= 10,
84
- format=cls.FORMAT,
85
- enqueue=True,
86
- )
87
- patch_logging_to_loguru()
88
- logger.info("日志设置完成")
89
- cls.__IS_SET_ROTATE = True
@@ -1,65 +0,0 @@
1
- import os
2
- import socket
3
-
4
- from loguru import logger
5
-
6
- from algo_backend.config import LoguruConfig as LogConfig
7
-
8
- from ..common import BasicLogStarter
9
- from .log_clean import LoguruCleaner
10
- from .log_setup import LoguruSetup
11
-
12
-
13
- class LoguruStarter(BasicLogStarter):
14
- """
15
- 容器内日志目录默认是:/logger/服务名
16
- """
17
-
18
- def __init__(self, service_name: str):
19
- super().__init__(service_name)
20
-
21
- @property
22
- def service_log_dir(self):
23
- return os.path.join(LogConfig.LOGGER_PATH, self.service_name)
24
-
25
- def setup_log(self):
26
- """
27
- 日志设置
28
- """
29
-
30
- LoguruSetup.rotate_daily(
31
- log_dir=self.service_log_dir,
32
- service_name=self.add_container_id(service_name=self.service_name),
33
- add_pid_suffix=True,
34
- save_info=LogConfig.SAVE_INFO_LEVEL,
35
- save_debug=LogConfig.SAVE_DEBUG_LOG,
36
- )
37
-
38
- for pkg in LogConfig.get_disable_log_pkg():
39
- # 忽略一些包的日志
40
- logger.debug(f"ignore log: {pkg}")
41
- logger.disable(pkg)
42
-
43
- def run_log_cleaner(self):
44
- """
45
- 启动定时任务清理日志
46
- """
47
- LoguruCleaner.schedule_run(
48
- log_dir=self.service_log_dir,
49
- retention_day=LogConfig.LOG_RETENTION_DAY,
50
- )
51
-
52
- @classmethod
53
- def add_container_id(cls, service_name: str):
54
- if not LogConfig.LOG_ADD_CONTAINED_ID:
55
- logger.info("日志名不增加containerId")
56
- return service_name
57
-
58
- try:
59
- socket_hostname = f"-{socket.gethostname()}"
60
- except:
61
- socket_hostname = ""
62
- if service_name in socket_hostname:
63
- return service_name
64
- else:
65
- return f"{service_name}{socket_hostname}"
@@ -1,83 +0,0 @@
1
- import logging
2
-
3
- from loguru import logger
4
-
5
- from algo_backend.config import LoguruConfig
6
-
7
- disable_log_pkg = LoguruConfig.get_disable_log_pkg()
8
-
9
-
10
- def patch_logging_to_loguru():
11
- """
12
- 将 Python 原生 logging 系统的所有日志重定向到 loguru
13
- """
14
-
15
- class LoguruHandler(logging.Handler):
16
- def emit(self, record):
17
- # 过滤特定模块的日志
18
- if any(excluded in record.name for excluded in disable_log_pkg):
19
- return
20
-
21
- try:
22
- level = logger.level(record.levelname).name
23
- except ValueError:
24
- level = record.levelno
25
-
26
- # 从当前帧开始,找到真正调用 logging 的位置
27
- frame = logging.currentframe()
28
- depth = 0 # 从0开始计算,动态确定深度
29
-
30
- # 遍历调用栈,跳过所有 logging 模块的内部调用
31
- while frame:
32
- filename = frame.f_code.co_filename
33
- func_name = frame.f_code.co_name
34
-
35
- # 检查是否为 logging 模块的内部调用
36
- is_logging_internal = (
37
- # 标准库 logging 模块路径
38
- "logging" in filename
39
- and (
40
- filename.endswith("logging/__init__.py")
41
- or "/logging/" in filename
42
- or "\\logging\\" in filename
43
- )
44
- ) or (
45
- # logging 内部函数名
46
- func_name
47
- in (
48
- "callHandlers",
49
- "handle",
50
- "emit",
51
- "handleError",
52
- "_log",
53
- "makeRecord",
54
- "getLogger",
55
- "debug",
56
- "info",
57
- "warning",
58
- "error",
59
- "exception",
60
- "critical",
61
- )
62
- )
63
-
64
- if is_logging_internal:
65
- frame = frame.f_back
66
- depth += 1
67
- else:
68
- # 找到真实的调用者,跳出循环
69
- break
70
-
71
- # 如果找不到合适的帧,回退到默认行为
72
- if not frame:
73
- depth = 2
74
-
75
- logger.opt(depth=depth, exception=record.exc_info).log(
76
- level, record.getMessage()
77
- )
78
-
79
- # 设置根记录器级别为最低级别,确保所有日志都被处理
80
- logging.root.setLevel(logging.DEBUG)
81
-
82
- logging.root.handlers = []
83
- logging.root.addHandler(LoguruHandler())