mirror of
https://github.com/linuxserver/docker-ci.git
synced 2026-02-05 19:20:42 +08:00
132 lines
5.1 KiB
Python
132 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import os
|
|
import sys
|
|
import logging
|
|
from logging import Logger
|
|
from logging.handlers import TimedRotatingFileHandler
|
|
from logging import LogRecord
|
|
import re
|
|
import platform
|
|
|
|
image: str | None = os.environ.get("IMAGE")
|
|
meta_tag: str | None = os.environ.get("META_TAG")
|
|
if image and meta_tag:
|
|
dir: str = os.path.join(os.path.dirname(os.path.realpath(__file__)),"output",image,meta_tag)
|
|
os.makedirs(dir, exist_ok=True)
|
|
log_dir: str = os.path.join(dir,'ci.log')
|
|
else:
|
|
log_dir = os.path.join(os.getcwd(),'ci.log')
|
|
|
|
logger: Logger = logging.getLogger()
|
|
|
|
# Get the major and minor version of Python
|
|
major_version = sys.version_info.major
|
|
minor_version = sys.version_info.minor
|
|
|
|
# https://stackoverflow.com/a/78515926/15290341
|
|
# The stack level is different for Python 3.9 and above
|
|
# We need to set the correct stack level so that the log (%(module)s.%(funcName)s|line:%(lineno)d) output is correct
|
|
stack_level_per_py_version = 2 if (major_version, minor_version) >= (3, 9) else 1
|
|
|
|
|
|
# Add custom log level for success messages
|
|
logging.SUCCESS = 25
|
|
logging.addLevelName(logging.SUCCESS, "SUCCESS")
|
|
|
|
def success(self:'Logger', message:str, *args, **kwargs):
|
|
"""Log 'message % args' with severity 'SUCCESS'.
|
|
|
|
To pass exception information, use the keyword argument exc_info with
|
|
a true value, e.g.
|
|
|
|
logger.success("Houston, Tranquility Base Here. The Eagle has Landed.", exc_info=1)
|
|
"""
|
|
if self.isEnabledFor(logging.SUCCESS):
|
|
self._log(logging.SUCCESS, message, args, stacklevel = stack_level_per_py_version, **kwargs)
|
|
|
|
logging.Logger.success = success
|
|
|
|
class ColorPercentStyle(logging.PercentStyle):
|
|
"""Custom log formatter that add color to specific log levels."""
|
|
grey: str = "38"
|
|
yellow: str = "33"
|
|
red: str = "31"
|
|
cyan: str = "36"
|
|
green: str = "32"
|
|
|
|
def _get_color_fmt(self, color_code, bold=False) -> str:
|
|
if bold:
|
|
return "\x1b[" + color_code + ";1m" + self._fmt + "\x1b[0m"
|
|
return "\x1b[" + color_code + ";20m" + self._fmt + "\x1b[0m"
|
|
|
|
def _get_fmt(self, levelno) -> str:
|
|
colors: dict[int, str] = {
|
|
logging.DEBUG: self._get_color_fmt(self.grey),
|
|
logging.INFO: self._get_color_fmt(self.cyan),
|
|
logging.WARNING: self._get_color_fmt(self.yellow),
|
|
logging.ERROR: self._get_color_fmt(self.red),
|
|
logging.CRITICAL: self._get_color_fmt(self.red),
|
|
logging.SUCCESS: self._get_color_fmt(self.green)
|
|
}
|
|
|
|
return colors.get(levelno, self._get_color_fmt(self.grey))
|
|
|
|
def _format(self, record:LogRecord) -> str:
|
|
return self._get_fmt(record.levelno) % record.__dict__
|
|
|
|
class CustomLogFormatter(logging.Formatter):
|
|
"""Formatter that removes creds from logs."""
|
|
ACCESS_KEY: str = os.environ.get("ACCESS_KEY","super_secret_key") or "super_secret_key" # If env is an empty string, use default value
|
|
SECRET_KEY: str = os.environ.get("SECRET_KEY","super_secret_key") or "super_secret_key" # If env is an empty string, use default value
|
|
|
|
def formatException(self, exc_info) -> str:
|
|
"""Format an exception so that it prints on a single line."""
|
|
result: str = super(CustomLogFormatter, self).formatException(exc_info)
|
|
return repr(result) # or format into one line however you want to
|
|
|
|
def format_credential_key(self, s) -> str:
|
|
return re.sub(self.ACCESS_KEY, '(removed)', s)
|
|
|
|
def format_secret_key(self, s) -> str:
|
|
return re.sub(self.SECRET_KEY, '(removed)', s)
|
|
|
|
def format(self, record) -> str:
|
|
s: str = super(CustomLogFormatter, self).format(record)
|
|
if record.exc_text:
|
|
s = s.replace('\n', '') + '|'
|
|
s = self.format_credential_key(s)
|
|
s = self.format_secret_key(s)
|
|
|
|
return s
|
|
|
|
def formatMessage(self, record) -> str:
|
|
return ColorPercentStyle(self._fmt).format(record)
|
|
|
|
def configure_logging(log_level:str) -> None:
|
|
"""Setup console and file logging"""
|
|
|
|
log_level = log_level.upper()
|
|
logger.handlers = []
|
|
logger.setLevel(log_level)
|
|
|
|
# Console logging
|
|
ch = logging.StreamHandler()
|
|
cf = CustomLogFormatter('%(asctime)-15s | %(threadName)-17s | %(name)-10s | %(levelname)-8s | (%(module)s.%(funcName)s|line:%(lineno)d) | %(message)s |', '%d/%m/%Y %H:%M:%S')
|
|
ch.setFormatter(cf)
|
|
ch.setLevel(log_level)
|
|
logger.addHandler(ch)
|
|
|
|
# File logging
|
|
fh = TimedRotatingFileHandler(log_dir, when="midnight", interval=1, backupCount=7, delay=True, encoding='utf-8')
|
|
f = CustomLogFormatter('%(asctime)-15s | %(threadName)-17s | %(name)-10s | %(levelname)-8s | (%(module)s.%(funcName)s|line:%(lineno)d) | %(message)s |', '%d/%m/%Y %H:%M:%S')
|
|
fh.setFormatter(f)
|
|
fh.setLevel(log_level)
|
|
logger.addHandler(fh)
|
|
|
|
logging.info('Operating system: %s', platform.platform())
|
|
logging.info('Python version: %s', platform.python_version())
|
|
if log_level.upper() == "DEBUG":
|
|
logging.getLogger("botocore").setLevel(logging.WARNING) # Mute boto3 logging output
|
|
logging.getLogger("urllib3.connectionpool").setLevel(logging.WARNING) # Mute urllib3.connectionpool logging output
|