codekingpro/portable-devtools
115k
1from __future__ import annotations2 3import logging4import os5import typing6import warnings7from dataclasses import dataclass8 9from mitmproxy import hooks10from mitmproxy.contrib import click as miniclick11from mitmproxy.utils import human12 13if typing.TYPE_CHECKING:14 from mitmproxy import master15 16ALERT = logging.INFO + 117"""18The ALERT logging level has the same urgency as info, but19signals to interactive tools that the user's attention should be20drawn to the output even if they're not currently looking at the21event log.22"""23logging.addLevelName(ALERT, "ALERT")24 25LogLevels = [26 "error",27 "warn",28 "info",29 "alert",30 "debug",31]32 33LOG_COLORS = {logging.ERROR: "red", logging.WARNING: "yellow", ALERT: "magenta"}34 35 36class MitmFormatter(logging.Formatter):37 def __init__(self, colorize: bool):38 super().__init__()39 self.colorize = colorize40 time = "[%s]"41 client = "[%s]"42 if colorize:43 time = miniclick.style(time, fg="cyan", dim=True)44 client = miniclick.style(client, fg="yellow", dim=True)45 46 self.with_client = f"{time}{client} %s"47 self.without_client = f"{time} %s"48 49 default_time_format = "%H:%M:%S"50 default_msec_format = "%s.%03d"51 52 def format(self, record: logging.LogRecord) -> str:53 time = self.formatTime(record)54 message = record.getMessage()55 if record.exc_info:56 message = f"{message}\n{self.formatException(record.exc_info)}"57 if self.colorize:58 message = miniclick.style(59 message,60 fg=LOG_COLORS.get(record.levelno),61 # dim=(record.levelno <= logging.DEBUG)62 )63 if client := getattr(record, "client", None):64 client = human.format_address(client)65 return self.with_client % (time, client, message)66 else:67 return self.without_client % (time, message)68 69 70class MitmLogHandler(logging.Handler):71 def __init__(self, *args, **kwargs):72 super().__init__(*args, **kwargs)73 self._initiated_in_test = os.environ.get("PYTEST_CURRENT_TEST")74 75 def filter(self, record: logging.LogRecord) -> bool:76 # We can't remove stale handlers here because that would modify .handlers during iteration!77 return bool(78 super().filter(record)79 and (80 not self._initiated_in_test81 or self._initiated_in_test == os.environ.get("PYTEST_CURRENT_TEST")82 )83 )84 85 def install(self) -> None:86 if self._initiated_in_test:87 for h in list(logging.getLogger().handlers):88 if (89 isinstance(h, MitmLogHandler)90 and h._initiated_in_test != self._initiated_in_test91 ):92 h.uninstall()93 94 logging.getLogger().addHandler(self)95 96 def uninstall(self) -> None:97 logging.getLogger().removeHandler(self)98 99 100# everything below is deprecated!101 102 103class LogEntry:104 def __init__(self, msg, level):105 # it's important that we serialize to string here already so that we don't pick up changes106 # happening after this log statement.107 self.msg = str(msg)108 self.level = level109 110 def __eq__(self, other):111 if isinstance(other, LogEntry):112 return self.__dict__ == other.__dict__113 return False114 115 def __repr__(self):116 return f"LogEntry({self.msg}, {self.level})"117 118 119class Log:120 """121 The central logger, exposed to scripts as mitmproxy.ctx.log.122 123 Deprecated: Please use the standard Python logging module instead.124 """125 126 def __init__(self, master):127 self.master = master128 129 def debug(self, txt):130 """131 Log with level debug.132 """133 warnings.warn(134 "mitmproxy's ctx.log.debug() is deprecated. Please use the standard Python logging module instead.",135 DeprecationWarning,136 stacklevel=2,137 )138 logging.getLogger().debug(txt)139 140 def info(self, txt):141 """142 Log with level info.143 """144 warnings.warn(145 "mitmproxy's ctx.log.info() is deprecated. Please use the standard Python logging module instead.",146 DeprecationWarning,147 stacklevel=2,148 )149 logging.getLogger().info(txt)150 151 def alert(self, txt):152 """153 Log with level alert. Alerts have the same urgency as info, but154 signals to interactive tools that the user's attention should be155 drawn to the output even if they're not currently looking at the156 event log.157 """158 warnings.warn(159 "mitmproxy's ctx.log.alert() is deprecated. Please use the standard Python logging module instead.",160 DeprecationWarning,161 stacklevel=2,162 )163 logging.getLogger().log(ALERT, txt)164 165 def warn(self, txt):166 """167 Log with level warn.168 """169 warnings.warn(170 "mitmproxy's ctx.log.warn() is deprecated. Please use the standard Python logging module instead.",171 DeprecationWarning,172 stacklevel=2,173 )174 logging.getLogger().warning(txt)175 176 def error(self, txt):177 """178 Log with level error.179 """180 warnings.warn(181 "mitmproxy's ctx.log.error() is deprecated. Please use the standard Python logging module instead.",182 DeprecationWarning,183 stacklevel=2,184 )185 logging.getLogger().error(txt)186 187 def __call__(self, text, level="info"):188 warnings.warn(189 "mitmproxy's ctx.log() is deprecated. Please use the standard Python logging module instead.",190 DeprecationWarning,191 stacklevel=2,192 )193 logging.getLogger().log(level=logging.getLevelName(level.upper()), msg=text)194 195 196LOGGING_LEVELS_TO_LOGENTRY = {197 logging.ERROR: "error",198 logging.WARNING: "warn",199 logging.INFO: "info",200 ALERT: "alert",201 logging.DEBUG: "debug",202}203 204 205class LegacyLogEvents(MitmLogHandler):206 """Emit deprecated `add_log` events from stdlib logging."""207 208 def __init__(209 self,210 master: master.Master,211 ):212 super().__init__()213 self.master = master214 self.formatter = MitmFormatter(colorize=False)215 216 def emit(self, record: logging.LogRecord) -> None:217 entry = LogEntry(218 msg=self.format(record),219 level=LOGGING_LEVELS_TO_LOGENTRY.get(record.levelno, "error"),220 )221 self.master.event_loop.call_soon_threadsafe(222 self.master.addons.trigger,223 AddLogHook(entry),224 )225 226 227@dataclass228class AddLogHook(hooks.Hook):229 """230 **Deprecated:** Starting with mitmproxy 9, users should use the standard Python logging module instead, for example231 by calling `logging.getLogger().addHandler()`.232 233 Called whenever a new log entry is created through the mitmproxy234 context. Be careful not to log from this event, which will cause an235 infinite loop!236 """237 238 entry: LogEntry239 240 241def log_tier(level):242 """243 Comparison method for "old" LogEntry log tiers.244 Ideally you should use the standard Python logging module instead.245 """246 return dict(error=0, warn=1, info=2, alert=2, debug=3).get(level)247 