# SPDX-License-Identifier: GPL-2.0-only # This file is part of Scapy # See https://scapy.net/ for more information # Copyright (C) Philippe Biondi """ Logging subsystem and basic exception class. """ ############################# # Logging subsystem # ############################# import logging import traceback import time from scapy.consts import WINDOWS # Typing imports from logging import LogRecord from typing import ( Any, Dict, Tuple, ) class Scapy_Exception(Exception): pass class ScapyInvalidPlatformException(Scapy_Exception): pass class ScapyNoDstMacException(Scapy_Exception): pass class ScapyFreqFilter(logging.Filter): def __init__(self): # type: () -> None logging.Filter.__init__(self) self.warning_table = {} # type: Dict[int, Tuple[float, int]] # noqa: E501 def filter(self, record): # type: (LogRecord) -> bool from scapy.config import conf # Levels below INFO are not covered if record.levelno <= logging.INFO: return True wt = conf.warning_threshold if wt > 0: stk = traceback.extract_stack() caller = 0 # type: int for _, l, n, _ in stk: if n == 'warning': break caller = l tm, nb = self.warning_table.get(caller, (0, 0)) ltm = time.time() if ltm - tm > wt: tm = ltm nb = 0 else: if nb < 2: nb += 1 if nb == 2: record.msg = "more " + str(record.msg) else: return False self.warning_table[caller] = (tm, nb) return True class ScapyColoredFormatter(logging.Formatter): """A subclass of logging.Formatter that handles colors.""" levels_colored = { 'DEBUG': 'reset', 'INFO': 'reset', 'WARNING': 'bold+yellow', 'ERROR': 'bold+red', 'CRITICAL': 'bold+white+bg_red' } def format(self, record): # type: (LogRecord) -> str message = super(ScapyColoredFormatter, self).format(record) from scapy.config import conf message = conf.color_theme.format( message, self.levels_colored[record.levelname] ) return message if WINDOWS: # colorama is bundled within IPython, but # logging.StreamHandler will be overwritten when called, # so we can't wait for IPython to call it try: import colorama colorama.init() except ImportError: pass # get Scapy's master logger log_scapy = logging.getLogger("scapy") log_scapy.propagate = False # override the level if not already set if log_scapy.level == logging.NOTSET: log_scapy.setLevel(logging.WARNING) # add a custom handler controlled by Scapy's config _handler = logging.StreamHandler() _handler.setFormatter( ScapyColoredFormatter( "%(levelname)s: %(message)s", ) ) log_scapy.addHandler(_handler) # logs at runtime log_runtime = logging.getLogger("scapy.runtime") log_runtime.addFilter(ScapyFreqFilter()) # logs in interactive functions log_interactive = logging.getLogger("scapy.interactive") log_interactive.setLevel(logging.DEBUG) # logs when loading Scapy log_loading = logging.getLogger("scapy.loading") def warning(x, *args, **kargs): # type: (str, *Any, **Any) -> None """ Prints a warning during runtime. """ log_runtime.warning(x, *args, **kargs)