Coverage for faststream / _internal / cli / utils / logs.py: 46%
60 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-08 01:48 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-08 01:48 +0000
1import json
2import logging
3import logging.config
4from collections import defaultdict
5from enum import Enum
6from pathlib import Path
7from typing import TYPE_CHECKING, Any
9import typer
11from faststream.exceptions import INSTALL_TOML, INSTALL_YAML
13if TYPE_CHECKING:
14 from faststream._internal.application import Application
17class LogLevels(str, Enum):
18 """A class to represent log levels.
20 Attributes:
21 critical : critical log level
22 error : error log level
23 warning : warning log level
24 info : info log level
25 debug : debug log level
26 """
28 critical = "critical"
29 fatal = "fatal"
30 error = "error"
31 warning = "warning"
32 warn = "warn"
33 info = "info"
34 debug = "debug"
35 notset = "notset"
38LOG_LEVELS: defaultdict[str, int] = defaultdict(
39 lambda: logging.INFO,
40 critical=logging.CRITICAL,
41 fatal=logging.FATAL,
42 error=logging.ERROR,
43 warning=logging.WARNING,
44 warn=logging.WARNING,
45 info=logging.INFO,
46 debug=logging.DEBUG,
47 notset=logging.NOTSET,
48)
51class LogFiles(str, Enum):
52 """The class to represent supported log configuration files."""
54 json = ".json"
55 yaml = ".yaml"
56 yml = ".yml"
57 toml = ".toml"
60def get_log_level(level: LogLevels | str | int) -> int:
61 """Get the log level.
63 Args:
64 level: The log level to get. Can be an integer, a LogLevels enum value, or a string.
66 Returns:
67 The log level as an integer.
69 """
70 if isinstance(level, int): 70 ↛ 71line 70 didn't jump to line 71 because the condition on line 70 was never true
71 return level
73 if isinstance(level, LogLevels):
74 return LOG_LEVELS[level.value]
76 if isinstance(level, str): # pragma: no branch
77 return LOG_LEVELS[level.lower()]
79 return None
82def set_log_level(level: int, app: "Application") -> None:
83 """Sets the log level for an application."""
84 if app.logger and getattr(app.logger, "setLevel", None):
85 app.logger.setLevel(level) # type: ignore[attr-defined]
87 for broker in app.brokers:
88 broker.config.logger.set_level(level)
91def _get_json_config(file: Path) -> dict[str, Any] | Any:
92 """Parse json config file to dict."""
93 with file.open("r", encoding="utf-8") as config_file:
94 return json.load(config_file)
97def _get_yaml_config(file: Path) -> dict[str, Any] | Any:
98 """Parse yaml config file to dict."""
99 try:
100 import yaml
101 except ImportError as e:
102 typer.echo(INSTALL_YAML, err=True)
103 raise typer.Exit(1) from e
105 with file.open("r", encoding="utf-8") as config_file:
106 return yaml.safe_load(config_file)
109def _get_toml_config(file: Path) -> dict[str, Any] | Any:
110 """Parse toml config file to dict."""
111 try:
112 import tomllib
113 except ImportError:
114 try:
115 import tomli as tomllib
116 except ImportError as e:
117 typer.echo(INSTALL_TOML, err=True)
118 raise typer.Exit(1) from e
120 with file.open("rb") as config_file:
121 return tomllib.load(config_file)
124def _get_log_config(file: Path) -> dict[str, Any] | Any:
125 """Read dict config from file."""
126 if not file.exists():
127 msg = f"File {file} specified to --log-config not found"
128 raise ValueError(msg)
130 file_format = file.suffix
132 if file_format == LogFiles.json:
133 logging_config = _get_json_config(file)
134 elif file_format in {LogFiles.yaml, LogFiles.yml}:
135 logging_config = _get_yaml_config(file)
136 elif file_format == LogFiles.toml:
137 logging_config = _get_toml_config(file)
138 else:
139 msg = f"Format {file_format} specified to --log-config file is not supported"
140 raise ValueError(msg)
142 return logging_config
145def set_log_config(file: Path) -> None:
146 """Set the logging config from file."""
147 configuration = _get_log_config(file)
148 logging.config.dictConfig(configuration)