chore: rename project coven → hack-house ⛧
Rebrand the Rust client crate (coven/ → hh/, package+binary "hack-house"), README, CLI strings, and branch (coven → hack-house). Gitea repo renamed cmd-chat → hack-house to match. Crypto/server logic unchanged; selftest + golden-vector test still green, binary is now `hack-house`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sanic.helpers import is_atty
|
||||
|
||||
|
||||
# Python 3.11 changed the way Enum formatting works for mixed-in types.
|
||||
if sys.version_info < (3, 11, 0):
|
||||
|
||||
class StrEnum(str, Enum):
|
||||
pass
|
||||
|
||||
else:
|
||||
if not TYPE_CHECKING:
|
||||
from enum import StrEnum
|
||||
|
||||
|
||||
COLORIZE = is_atty() and not os.environ.get("SANIC_NO_COLOR")
|
||||
|
||||
|
||||
class Colors(StrEnum): # no cov
|
||||
"""
|
||||
Colors for log messages. If the output is not a TTY, the colors will be
|
||||
disabled.
|
||||
|
||||
Can be used like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from sanic.log import logger, Colors
|
||||
|
||||
logger.info(f"{Colors.GREEN}This is a green message{Colors.END}")
|
||||
|
||||
|
||||
Attributes:
|
||||
END: Reset the color
|
||||
BOLD: Bold text
|
||||
BLUE: Blue text
|
||||
GREEN: Green text
|
||||
PURPLE: Purple text
|
||||
RED: Red text
|
||||
SANIC: Sanic pink
|
||||
YELLOW: Yellow text
|
||||
GREY: Grey text
|
||||
"""
|
||||
|
||||
END = "\033[0m" if COLORIZE else ""
|
||||
BOLD = "\033[1m" if COLORIZE else ""
|
||||
BLUE = "\033[34m" if COLORIZE else ""
|
||||
GREEN = "\033[32m" if COLORIZE else ""
|
||||
PURPLE = "\033[35m" if COLORIZE else ""
|
||||
CYAN = "\033[36m" if COLORIZE else ""
|
||||
RED = "\033[31m" if COLORIZE else ""
|
||||
YELLOW = "\033[33m" if COLORIZE else ""
|
||||
GREY = "\033[38;5;240m" if COLORIZE else ""
|
||||
SANIC = "\033[38;2;255;13;104m" if COLORIZE else ""
|
||||
|
||||
|
||||
LEVEL_COLORS = {
|
||||
logging.DEBUG: Colors.BLUE,
|
||||
logging.WARNING: Colors.YELLOW,
|
||||
logging.ERROR: Colors.RED,
|
||||
logging.CRITICAL: Colors.RED + Colors.BOLD,
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import sys
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
LOGGING_CONFIG_DEFAULTS: dict[str, Any] = dict( # no cov
|
||||
version=1,
|
||||
disable_existing_loggers=False,
|
||||
loggers={
|
||||
"sanic.root": {"level": "INFO", "handlers": ["console"]},
|
||||
"sanic.error": {
|
||||
"level": "INFO",
|
||||
"handlers": ["error_console"],
|
||||
"propagate": True,
|
||||
"qualname": "sanic.error",
|
||||
},
|
||||
"sanic.access": {
|
||||
"level": "INFO",
|
||||
"handlers": ["access_console"],
|
||||
"propagate": True,
|
||||
"qualname": "sanic.access",
|
||||
},
|
||||
"sanic.server": {
|
||||
"level": "INFO",
|
||||
"handlers": ["console"],
|
||||
"propagate": True,
|
||||
"qualname": "sanic.server",
|
||||
},
|
||||
"sanic.websockets": {
|
||||
"level": "INFO",
|
||||
"handlers": ["console"],
|
||||
"propagate": True,
|
||||
"qualname": "sanic.websockets",
|
||||
},
|
||||
},
|
||||
handlers={
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "generic",
|
||||
"stream": sys.stdout,
|
||||
},
|
||||
"error_console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "generic",
|
||||
"stream": sys.stderr,
|
||||
},
|
||||
"access_console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "access",
|
||||
"stream": sys.stdout,
|
||||
},
|
||||
},
|
||||
formatters={
|
||||
"generic": {"class": "sanic.logging.formatter.AutoFormatter"},
|
||||
"access": {"class": "sanic.logging.formatter.AutoAccessFormatter"},
|
||||
},
|
||||
)
|
||||
"""
|
||||
Default logging configuration
|
||||
"""
|
||||
@@ -0,0 +1,33 @@
|
||||
from warnings import warn
|
||||
|
||||
from sanic.helpers import is_atty
|
||||
from sanic.logging.color import Colors
|
||||
|
||||
|
||||
def deprecation(message: str, version: float): # no cov
|
||||
"""
|
||||
Add a deprecation notice
|
||||
|
||||
Example when a feature is being removed. In this case, version
|
||||
should be AT LEAST next version + 2
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
deprecation("Helpful message", 99.9)
|
||||
|
||||
Example when a feature is deprecated but not being removed:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
deprecation("Helpful message", 0)
|
||||
|
||||
Args:
|
||||
message (str): Deprecation message
|
||||
version (float): Version when the feature will be removed
|
||||
"""
|
||||
version_display = f" v{version}" if version else ""
|
||||
version_info = f"[DEPRECATION{version_display}] "
|
||||
if is_atty():
|
||||
version_info = f"{Colors.RED}{version_info}"
|
||||
message = f"{Colors.YELLOW}{message}{Colors.END}"
|
||||
warn(version_info + message, DeprecationWarning)
|
||||
@@ -0,0 +1,13 @@
|
||||
import logging
|
||||
|
||||
|
||||
class VerbosityFilter(logging.Filter):
|
||||
"""
|
||||
Filter log records based on verbosity level.
|
||||
"""
|
||||
|
||||
verbosity: int = 0
|
||||
|
||||
def filter(self, record: logging.LogRecord) -> bool:
|
||||
verbosity = getattr(record, "verbosity", 0)
|
||||
return verbosity <= self.verbosity
|
||||
@@ -0,0 +1,387 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from sanic.helpers import is_atty, json_dumps
|
||||
from sanic.logging.color import LEVEL_COLORS
|
||||
from sanic.logging.color import Colors as c
|
||||
|
||||
|
||||
CONTROL_RE = re.compile(r"\033\[[0-9;]*\w")
|
||||
CONTROL_LIMIT_IDENT = "\033[1000D\033[{limit}C"
|
||||
CONTROL_LIMIT_START = "\033[1000D\033[{start}C\033[K"
|
||||
CONTROL_LIMIT_END = "\033[1000C\033[{right}D\033[K"
|
||||
EXCEPTION_LINE_RE = re.compile(r"^(?P<exc>.*?): (?P<message>.*)$")
|
||||
FILE_LINE_RE = re.compile(
|
||||
r"File \"(?P<path>.*?)\", line (?P<line_num>\d+), in (?P<location>.*)"
|
||||
)
|
||||
DEFAULT_FIELDS = set(
|
||||
logging.LogRecord("", 0, "", 0, "", (), None).__dict__.keys()
|
||||
) | {
|
||||
"ident",
|
||||
"message",
|
||||
"asctime",
|
||||
"right",
|
||||
}
|
||||
|
||||
|
||||
class AutoFormatter(logging.Formatter):
|
||||
"""
|
||||
Automatically sets up the formatter based on the environment.
|
||||
|
||||
It will switch between the Debug and Production formatters based upon
|
||||
how the environment is set up. Additionally, it will automatically
|
||||
detect if the output is a TTY and colorize the output accordingly.
|
||||
"""
|
||||
|
||||
SETUP = False
|
||||
ATTY = is_atty()
|
||||
NO_COLOR = os.environ.get("SANIC_NO_COLOR", "false").lower() == "true"
|
||||
LOG_EXTRA = os.environ.get("SANIC_LOG_EXTRA", "true").lower() == "true"
|
||||
IDENT = os.environ.get("SANIC_WORKER_IDENTIFIER", "Main ") or "Main "
|
||||
DATE_FORMAT = "%Y-%m-%d %H:%M:%S %z"
|
||||
IDENT_LIMIT = 5
|
||||
MESSAGE_START = 42
|
||||
PREFIX_FORMAT = (
|
||||
f"{c.GREY}%(ident)s{{limit}} %(asctime)s {c.END}"
|
||||
"%(levelname)s: {start}"
|
||||
)
|
||||
MESSAGE_FORMAT = "%(message)s"
|
||||
|
||||
def __init__(self, *args) -> None:
|
||||
args_list = list(args)
|
||||
if not args:
|
||||
args_list.append(self._make_format())
|
||||
elif args and not args[0]:
|
||||
args_list[0] = self._make_format()
|
||||
if len(args_list) < 2:
|
||||
args_list.append(self.DATE_FORMAT)
|
||||
elif not args[1]:
|
||||
args_list[1] = self.DATE_FORMAT
|
||||
|
||||
super().__init__(*args_list)
|
||||
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
record.ident = self.IDENT
|
||||
self._set_levelname(record)
|
||||
output = super().format(record)
|
||||
if self.LOG_EXTRA:
|
||||
output += self._log_extra(record)
|
||||
return output
|
||||
|
||||
def _set_levelname(self, record: logging.LogRecord) -> None:
|
||||
if (
|
||||
self.ATTY
|
||||
and not self.NO_COLOR
|
||||
and (color := LEVEL_COLORS.get(record.levelno))
|
||||
):
|
||||
record.levelname = f"{color}{record.levelname}{c.END}"
|
||||
|
||||
def _make_format(self) -> str:
|
||||
limit = CONTROL_LIMIT_IDENT.format(limit=self.IDENT_LIMIT)
|
||||
start = CONTROL_LIMIT_START.format(start=self.MESSAGE_START)
|
||||
base_format = self.PREFIX_FORMAT + self.MESSAGE_FORMAT
|
||||
fmt = base_format.format(limit=limit, start=start)
|
||||
if not self.ATTY or self.NO_COLOR:
|
||||
return CONTROL_RE.sub("", fmt)
|
||||
return fmt
|
||||
|
||||
def _log_extra(self, record: logging.LogRecord, indent: int = 0) -> str:
|
||||
extra_lines = [""]
|
||||
|
||||
for key, value in record.__dict__.items():
|
||||
if key not in DEFAULT_FIELDS:
|
||||
extra_lines.append(self._format_key_value(key, value, indent))
|
||||
|
||||
return "\n".join(extra_lines)
|
||||
|
||||
def _format_key_value(self, key, value, indent):
|
||||
indentation = " " * indent
|
||||
template = (
|
||||
f"{indentation} {{c.YELLOW}}{{key}}{{c.END}}={{value}}"
|
||||
if self.ATTY and not self.NO_COLOR
|
||||
else f"{indentation}{{key}}={{value}}"
|
||||
)
|
||||
if isinstance(value, dict):
|
||||
nested_lines = [template.format(c=c, key=key, value="")]
|
||||
for nested_key, nested_value in value.items():
|
||||
nested_lines.append(
|
||||
self._format_key_value(
|
||||
nested_key, nested_value, indent + 2
|
||||
)
|
||||
)
|
||||
return "\n".join(nested_lines)
|
||||
else:
|
||||
return template.format(c=c, key=key, value=value)
|
||||
|
||||
|
||||
class DebugFormatter(AutoFormatter):
|
||||
"""
|
||||
The DebugFormatter is used for development and debugging purposes.
|
||||
|
||||
It can be used directly, or it will be automatically selected if the
|
||||
environment is set up for development and is using the AutoFormatter.
|
||||
"""
|
||||
|
||||
IDENT_LIMIT = 5
|
||||
MESSAGE_START = 23
|
||||
DATE_FORMAT = "%H:%M:%S"
|
||||
|
||||
def _set_levelname(self, record: logging.LogRecord) -> None:
|
||||
if len(record.levelname) > 5:
|
||||
record.levelname = record.levelname[:4]
|
||||
super()._set_levelname(record)
|
||||
|
||||
def formatException(self, ei): # no cov
|
||||
orig = super().formatException(ei)
|
||||
if not self.ATTY or self.NO_COLOR:
|
||||
return orig
|
||||
colored_traceback = []
|
||||
lines = orig.splitlines()
|
||||
for idx, line in enumerate(lines):
|
||||
if line.startswith(" File"):
|
||||
line = self._color_file_line(line)
|
||||
elif line.startswith(" "):
|
||||
line = self._color_code_line(line)
|
||||
elif (
|
||||
"Error" in line or "Exception" in line or len(lines) - 1 == idx
|
||||
):
|
||||
line = self._color_exception_line(line)
|
||||
colored_traceback.append(line)
|
||||
return "\n".join(colored_traceback)
|
||||
|
||||
def _color_exception_line(self, line: str) -> str: # no cov
|
||||
match = EXCEPTION_LINE_RE.match(line)
|
||||
if not match:
|
||||
return line
|
||||
exc = match.group("exc")
|
||||
message = match.group("message")
|
||||
return f"{c.SANIC}{c.BOLD}{exc}{c.END}: {c.BOLD}{message}{c.END}"
|
||||
|
||||
def _color_file_line(self, line: str) -> str: # no cov
|
||||
match = FILE_LINE_RE.search(line)
|
||||
if not match:
|
||||
return line
|
||||
path = match.group("path")
|
||||
line_num = match.group("line_num")
|
||||
location = match.group("location")
|
||||
return (
|
||||
f' File "{path}", line {c.CYAN}{c.BOLD}{line_num}{c.END}, '
|
||||
f"in {c.BLUE}{c.BOLD}{location}{c.END}"
|
||||
)
|
||||
|
||||
def _color_code_line(self, line: str) -> str: # no cov
|
||||
return f"{c.YELLOW}{line}{c.END}"
|
||||
|
||||
|
||||
class ProdFormatter(AutoFormatter):
|
||||
"""
|
||||
The ProdFormatter is used for production environments.
|
||||
|
||||
It can be used directly, or it will be automatically selected if the
|
||||
environment is set up for production and is using the AutoFormatter.
|
||||
"""
|
||||
|
||||
|
||||
class LegacyFormatter(AutoFormatter):
|
||||
"""
|
||||
The LegacyFormatter is used if you want to use the old style of logging.
|
||||
|
||||
You can use it as follows, typically in conjunction with the
|
||||
LegacyAccessFormatter:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from sanic.log import LOGGING_CONFIG_DEFAULTS
|
||||
|
||||
LOGGING_CONFIG_DEFAULTS["formatters"] = {
|
||||
"generic": {
|
||||
"class": "sanic.logging.formatter.LegacyFormatter"
|
||||
},
|
||||
"access": {
|
||||
"class": "sanic.logging.formatter.LegacyAccessFormatter"
|
||||
},
|
||||
}
|
||||
"""
|
||||
|
||||
PREFIX_FORMAT = "%(asctime)s [%(process)s] [%(levelname)s] "
|
||||
DATE_FORMAT = "[%Y-%m-%d %H:%M:%S %z]"
|
||||
|
||||
|
||||
class AutoAccessFormatter(AutoFormatter):
|
||||
MESSAGE_FORMAT = (
|
||||
f"{c.PURPLE}%(host)s "
|
||||
f"{c.BLUE + c.BOLD}%(request)s{c.END} "
|
||||
f"%(right)s%(status)s %(byte)s {c.GREY}%(duration)s{c.END}"
|
||||
)
|
||||
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
status = len(str(getattr(record, "status", "")))
|
||||
byte = len(str(getattr(record, "byte", "")))
|
||||
duration = len(str(getattr(record, "duration", "")))
|
||||
record.right = (
|
||||
CONTROL_LIMIT_END.format(right=status + byte + duration + 1)
|
||||
if self.ATTY
|
||||
else ""
|
||||
)
|
||||
return super().format(record)
|
||||
|
||||
def _set_levelname(self, record: logging.LogRecord) -> None:
|
||||
if self.ATTY and record.levelno == logging.INFO:
|
||||
record.levelname = f"{c.SANIC}ACCESS{c.END}"
|
||||
|
||||
|
||||
class LegacyAccessFormatter(AutoAccessFormatter):
|
||||
"""
|
||||
The LegacyFormatter is used if you want to use the old style of logging.
|
||||
|
||||
You can use it as follows, typically in conjunction with the
|
||||
LegacyFormatter:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from sanic.log import LOGGING_CONFIG_DEFAULTS
|
||||
|
||||
LOGGING_CONFIG_DEFAULTS["formatters"] = {
|
||||
"generic": {
|
||||
"class": "sanic.logging.formatter.LegacyFormatter"
|
||||
},
|
||||
"access": {
|
||||
"class": "sanic.logging.formatter.LegacyAccessFormatter"
|
||||
},
|
||||
}
|
||||
"""
|
||||
|
||||
PREFIX_FORMAT = "%(asctime)s - (%(name)s)[%(levelname)s][%(host)s]: "
|
||||
MESSAGE_FORMAT = "%(request)s %(message)s %(status)s %(byte)s"
|
||||
|
||||
|
||||
class DebugAccessFormatter(AutoAccessFormatter):
|
||||
IDENT_LIMIT = 5
|
||||
MESSAGE_START = 23
|
||||
DATE_FORMAT = "%H:%M:%S"
|
||||
LOG_EXTRA = False
|
||||
|
||||
|
||||
class ProdAccessFormatter(AutoAccessFormatter):
|
||||
IDENT_LIMIT = 5
|
||||
MESSAGE_START = 42
|
||||
PREFIX_FORMAT = (
|
||||
f"{c.GREY}%(ident)s{{limit}}|%(asctime)s{c.END} "
|
||||
f"%(levelname)s: {{start}}"
|
||||
)
|
||||
MESSAGE_FORMAT = (
|
||||
f"{c.PURPLE}%(host)s {c.BLUE + c.BOLD}"
|
||||
f"%(request)s{c.END} "
|
||||
f"%(right)s%(status)s %(byte)s {c.GREY}%(duration)s{c.END}"
|
||||
)
|
||||
LOG_EXTRA = False
|
||||
|
||||
|
||||
class JSONFormatter(AutoFormatter):
|
||||
"""
|
||||
The JSONFormatter is used to output logs in JSON format.
|
||||
|
||||
This is useful for logging to a file or to a log aggregator that
|
||||
understands JSON. It will output all the fields from the LogRecord
|
||||
as well as the extra fields that are passed in.
|
||||
|
||||
You can use it as follows:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from sanic.log import LOGGING_CONFIG_DEFAULTS
|
||||
|
||||
LOGGING_CONFIG_DEFAULTS["formatters"] = {
|
||||
"generic": {
|
||||
"class": "sanic.logging.formatter.JSONFormatter"
|
||||
},
|
||||
"access": {
|
||||
"class": "sanic.logging.formatter.JSONFormatter"
|
||||
},
|
||||
}
|
||||
"""
|
||||
|
||||
ATTY = False
|
||||
NO_COLOR = True
|
||||
FIELDS = [
|
||||
"name",
|
||||
"levelno",
|
||||
"pathname",
|
||||
"module",
|
||||
"filename",
|
||||
"lineno",
|
||||
]
|
||||
|
||||
dumps = json_dumps
|
||||
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
return self.format_dict(self.to_dict(record))
|
||||
|
||||
def to_dict(self, record: logging.LogRecord) -> dict:
|
||||
base = {field: getattr(record, field, None) for field in self.FIELDS}
|
||||
extra = {
|
||||
key: value
|
||||
for key, value in record.__dict__.items()
|
||||
if key not in DEFAULT_FIELDS
|
||||
}
|
||||
info = {}
|
||||
if record.exc_info:
|
||||
info["exc_info"] = self.formatException(record.exc_info)
|
||||
if record.stack_info:
|
||||
info["stack_info"] = self.formatStack(record.stack_info)
|
||||
return {
|
||||
"timestamp": self.formatTime(record, self.datefmt),
|
||||
"level": record.levelname,
|
||||
"message": record.getMessage(),
|
||||
**base,
|
||||
**info,
|
||||
**extra,
|
||||
}
|
||||
|
||||
def format_dict(self, record: dict) -> str:
|
||||
return self.dumps(record)
|
||||
|
||||
|
||||
class JSONAccessFormatter(JSONFormatter):
|
||||
"""
|
||||
The JSONAccessFormatter is used to output access logs in JSON format.
|
||||
|
||||
This is useful for logging to a file or to a log aggregator that
|
||||
understands JSON. It will output all the fields from the LogRecord
|
||||
as well as the extra fields that are passed in.
|
||||
|
||||
You can use it as follows:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from sanic.log import LOGGING_CONFIG_DEFAULTS
|
||||
|
||||
LOGGING_CONFIG_DEFAULTS["formatters"] = {
|
||||
"generic": {
|
||||
"class": "sanic.logging.formatter.JSONFormatter"
|
||||
},
|
||||
"access": {
|
||||
"class": "sanic.logging.formatter.JSONAccessFormatter"
|
||||
},
|
||||
}
|
||||
"""
|
||||
|
||||
FIELDS = [
|
||||
"host",
|
||||
"request",
|
||||
"status",
|
||||
"byte",
|
||||
"duration",
|
||||
]
|
||||
|
||||
def to_dict(self, record: logging.LogRecord) -> dict:
|
||||
base = {field: getattr(record, field, None) for field in self.FIELDS}
|
||||
return {
|
||||
"timestamp": self.formatTime(record, self.datefmt),
|
||||
"level": record.levelname,
|
||||
"message": record.getMessage(),
|
||||
**base,
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import logging
|
||||
|
||||
from sanic.logging.filter import VerbosityFilter
|
||||
|
||||
|
||||
_verbosity_filter = VerbosityFilter()
|
||||
|
||||
logger = logging.getLogger("sanic.root") # no cov
|
||||
"""
|
||||
General Sanic logger
|
||||
"""
|
||||
logger.addFilter(_verbosity_filter)
|
||||
|
||||
error_logger = logging.getLogger("sanic.error") # no cov
|
||||
"""
|
||||
Logger used by Sanic for error logging
|
||||
"""
|
||||
error_logger.addFilter(_verbosity_filter)
|
||||
|
||||
access_logger = logging.getLogger("sanic.access") # no cov
|
||||
"""
|
||||
Logger used by Sanic for access logging
|
||||
"""
|
||||
access_logger.addFilter(_verbosity_filter)
|
||||
|
||||
server_logger = logging.getLogger("sanic.server") # no cov
|
||||
"""
|
||||
Logger used by Sanic for server related messages
|
||||
"""
|
||||
server_logger.addFilter(_verbosity_filter)
|
||||
|
||||
websockets_logger = logging.getLogger("sanic.websockets") # no cov
|
||||
"""
|
||||
Logger used by Sanic for websockets module and protocol related messages
|
||||
"""
|
||||
websockets_logger.addFilter(_verbosity_filter)
|
||||
websockets_logger.setLevel(logging.WARNING) # Too noisy on debug/info
|
||||
@@ -0,0 +1,61 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
from sanic.helpers import Default, _default
|
||||
from sanic.log import (
|
||||
access_logger,
|
||||
error_logger,
|
||||
logger,
|
||||
server_logger,
|
||||
websockets_logger,
|
||||
)
|
||||
from sanic.logging.formatter import (
|
||||
AutoAccessFormatter,
|
||||
AutoFormatter,
|
||||
DebugAccessFormatter,
|
||||
DebugFormatter,
|
||||
ProdAccessFormatter,
|
||||
ProdFormatter,
|
||||
)
|
||||
|
||||
|
||||
def setup_logging(
|
||||
debug: bool,
|
||||
no_color: bool = False,
|
||||
log_extra: bool | Default = _default,
|
||||
) -> None:
|
||||
if AutoFormatter.SETUP:
|
||||
return
|
||||
|
||||
if isinstance(log_extra, Default):
|
||||
log_extra = debug
|
||||
os.environ["SANIC_LOG_EXTRA"] = str(log_extra)
|
||||
AutoFormatter.LOG_EXTRA = log_extra
|
||||
|
||||
if no_color:
|
||||
os.environ["SANIC_NO_COLOR"] = str(no_color)
|
||||
AutoFormatter.NO_COLOR = no_color
|
||||
|
||||
AutoFormatter.SETUP = True
|
||||
|
||||
for lggr in (logger, server_logger, error_logger, websockets_logger):
|
||||
_auto_format(
|
||||
lggr,
|
||||
AutoFormatter,
|
||||
DebugFormatter if debug else ProdFormatter,
|
||||
)
|
||||
_auto_format(
|
||||
access_logger,
|
||||
AutoAccessFormatter,
|
||||
DebugAccessFormatter if debug else ProdAccessFormatter,
|
||||
)
|
||||
|
||||
|
||||
def _auto_format(
|
||||
logger: logging.Logger,
|
||||
auto_class: type[AutoFormatter],
|
||||
formatter_class: type[AutoFormatter],
|
||||
) -> None:
|
||||
for handler in logger.handlers:
|
||||
if type(handler.formatter) is auto_class:
|
||||
handler.setFormatter(formatter_class())
|
||||
Reference in New Issue
Block a user