""" Java java.util.logging standard library APIs implemented in Python. java.util.logging package - Logger, Level, Handler, Formatter, etc. """ from __future__ import annotations from .._core import Stub from .lang import RuntimeException, Exception as _JavaException from .security import BasicPermission import typing as _typing import sys as _sys import datetime as _datetime import io as _io import threading as _threading import traceback as _traceback import os as _os __all__ = [ "Logger", "Level", "Handler", "ConsoleHandler", "FileHandler", "StreamHandler", "SocketHandler", "MemoryHandler", "Formatter", "SimpleFormatter", "XMLFormatter", "LogRecord", "LogManager", "Filter", "ErrorManager", "LoggingMXBean", "LoggingPermission", "OFF", "SEVERE", "WARNING", "INFO", "CONFIG", "FINE", "FINER", "FINEST", "ALL", ] import sys as _sys import threading as _threading # ============================================================ # Level Constants # ============================================================ class _Integer: """Wrapper to emulate Integer.MIN_VALUE / MAX_VALUE""" MIN_VALUE = -2147483648 MAX_VALUE = 2147483647 OFF = _Integer.MAX_VALUE SEVERE = 1000 WARNING = 900 INFO = 800 CONFIG = 700 FINE = 500 FINER = 400 FINEST = 300 ALL = _Integer.MIN_VALUE # ============================================================ # Level # ============================================================ class Level(Stub): """java.util.logging.Level""" def __init__(self, name, value, resource_bundle_name=None): super().__init__() self._name = name self._value = value self._resource_bundle_name = resource_bundle_name def getName(self): return self._name def intValue(self): return self._value def getResourceBundleName(self): return self._resource_bundle_name def toString(self): return self._name def hashCode(self): return self._value def equals(self, other): if not isinstance(other, Level): return False return self._value == other._value def parse(self, name): name_upper = name.upper() level_map = { "OFF": Level.OFF, "SEVERE": Level.SEVERE, "WARNING": Level.WARNING, "INFO": Level.INFO, "CONFIG": Level.CONFIG, "FINE": Level.FINE, "FINER": Level.FINER, "FINEST": Level.FINEST, "ALL": Level.ALL, } if name_upper in level_map: return level_map[name_upper] try: return Level(name, int(name)) except ValueError: raise IllegalArgumentException(f"Bad level name: {name}") def __lt__(self, other): if isinstance(other, Level): return self._value < other._value return NotImplemented def __le__(self, other): if isinstance(other, Level): return self._value <= other._value return NotImplemented def __gt__(self, other): if isinstance(other, Level): return self._value > other._value return NotImplemented def __ge__(self, other): if isinstance(other, Level): return self._value >= other._value return NotImplemented # Static level instances OFF = None SEVERE = None WARNING = None INFO = None CONFIG = None FINE = None FINER = None FINEST = None ALL = None # ============================================================ # Initialize static Level instances # ============================================================ Level.OFF = Level("OFF", _Integer.MAX_VALUE) Level.SEVERE = Level("SEVERE", 1000) Level.WARNING = Level("WARNING", 900) Level.INFO = Level("INFO", 800) Level.CONFIG = Level("CONFIG", 700) Level.FINE = Level("FINE", 500) Level.FINER = Level("FINER", 400) Level.FINEST = Level("FINEST", 300) Level.ALL = Level("ALL", _Integer.MIN_VALUE) # ============================================================ # LogRecord # ============================================================ class LogRecord(Stub): """java.util.logging.LogRecord""" def __init__(self, level, msg): super().__init__() self._level = level if isinstance(level, Level) else Level.INFO self._logger_name = "" self._message = msg self._millis = _datetime.datetime.now().timestamp() * 1000 self._thread_id = _threading.get_ident() self._source_class_name = None self._source_method_name = None self._thrown = None self._parameters = None self._resource_bundle = None self._resource_bundle_name = None self._sequence_number = id(self) self._long_thread_id = _threading.get_ident() def getLevel(self): return self._level def setLevel(self, level): self._level = level def getLoggerName(self): return self._logger_name def setLoggerName(self, name): self._logger_name = name def getMessage(self): return self._message def setMessage(self, msg): self._message = msg def getMillis(self): return self._millis def setMillis(self, millis): self._millis = millis def getThreadID(self): return self._thread_id def setThreadID(self, thread_id): self._thread_id = thread_id def getSourceClassName(self): return self._source_class_name def setSourceClassName(self, name): self._source_class_name = name def getSourceMethodName(self): return self._source_method_name def setSourceMethodName(self, name): self._source_method_name = name def getThrown(self): return self._thrown def setThrown(self, thrown): self._thrown = thrown def getParameters(self): return self._parameters def setParameters(self, params): self._parameters = params def getResourceBundle(self): return self._resource_bundle def setResourceBundle(self, bundle): self._resource_bundle = bundle def getResourceBundleName(self): return self._resource_bundle_name def setResourceBundleName(self, name): self._resource_bundle_name = name def getSequenceNumber(self): return self._sequence_number def setSequenceNumber(self, seq): self._sequence_number = seq def getLongThreadID(self): return self._long_thread_id def setLongThreadID(self, thread_id): self._long_thread_id = thread_id # ============================================================ # Filter # ============================================================ class Filter(Stub): """java.util.logging.Filter""" def isLoggable(self, record): return True # ============================================================ # Formatter # ============================================================ class Formatter(Stub): """java.util.logging.Formatter""" def format(self, record): raise NotImplementedError def getHead(self, handler): return "" def getTail(self, handler): return "" def formatMessage(self, record): msg = record.getMessage() params = record.getParameters() if params: try: return msg % params except Exception: pass return msg class SimpleFormatter(Formatter): """java.util.logging.SimpleFormatter""" def format(self, record): import datetime as dt ts = dt.datetime.fromtimestamp(record.getMillis() / 1000.0) ts_str = ts.strftime("%Y-%m-%d %H:%M:%S") msg = self.formatMessage(record) thrown = record.getThrown() result = f"{ts_str} {record.getLevel().getName()} {record.getLoggerName()}: {msg}" if thrown is not None: result += "\n" + "".join(_traceback.format_exception( type(thrown), thrown, getattr(thrown, '__traceback__', None) )) return result class XMLFormatter(Formatter): """java.util.logging.XMLFormatter""" def __init__(self): super().__init__() from xml.sax.saxutils import escape def format(self, record): ts = _datetime.datetime.fromtimestamp(record.getMillis() / 1000.0) ts_str = ts.strftime("%Y-%m-%dT%H:%M:%S") from xml.sax.saxutils import escape msg = escape(self.formatMessage(record)) logger_name = escape(record.getLoggerName() or "") level_name = record.getLevel().getName() parts = [ "", f" {ts_str}", f" {int(record.getMillis())}", f" {record.getSequenceNumber()}", f" {logger_name}", f" {level_name}", f" {escape(record.getSourceClassName() or '')}", f" {escape(record.getSourceMethodName() or '')}", f" {record.getThreadID()}", f" {msg}", ] thrown = record.getThrown() if thrown is not None: import traceback tb_str = "".join(traceback.format_exception( type(thrown), thrown, getattr(thrown, '__traceback__', None) )) parts.append(f" {escape(tb_str)}") parts.append("") return "\n".join(parts) def getHead(self, handler): return '\n\n\n' def getTail(self, handler): return "\n" # ============================================================ # ErrorManager # ============================================================ class ErrorManager(Stub): """java.util.logging.ErrorManager""" def __init__(self): super().__init__() def error(self, message, exception, error_code): _sys.stderr.write(f"ErrorManager: {message}\n") if exception: _traceback.print_exception(type(exception), exception, getattr(exception, '__traceback__', None)) GENERIC_FAILURE = 0 WRITE_FAILURE = 1 FLUSH_FAILURE = 2 CLOSE_FAILURE = 3 OPEN_FAILURE = 4 FORMAT_FAILURE = 5 # ============================================================ # Handler # ============================================================ class Handler(Stub): """java.util.logging.Handler""" def __init__(self): super().__init__() self._level = Level.ALL self._filter = None self._formatter = None self._encoding = None self._error_manager = ErrorManager() self._closed = False def publish(self, record): raise NotImplementedError def flush(self): pass def close(self): self._closed = True def isLoggable(self, record): if self._closed: return False if record.getLevel().intValue() < self._level.intValue(): return False if self._filter is not None: return self._filter.isLoggable(record) return True def setLevel(self, level): self._level = level def getLevel(self): return self._level def setFilter(self, filter_): self._filter = filter_ def getFilter(self): return self._filter def setFormatter(self, formatter): self._formatter = formatter def getFormatter(self): return self._formatter def setEncoding(self, encoding): self._encoding = encoding def getEncoding(self): return self._encoding def setErrorManager(self, em): self._error_manager = em def getErrorManager(self): return self._error_manager def reportError(self, msg, ex, code): if self._error_manager: self._error_manager.error(msg, ex, code) class StreamHandler(Handler): """java.util.logging.StreamHandler""" def __init__(self, out=None, formatter=None): super().__init__() if formatter is None: formatter = SimpleFormatter() self.setFormatter(formatter) self._output = out or _sys.stderr self._writer = None if self._output is not None: if isinstance(self._output, _io.TextIOBase): self._writer = self._output elif isinstance(self._output, _io.BufferedWriter): self._writer = _io.TextIOWrapper(self._output) else: self._writer = _io.TextIOWrapper(_io.BufferedWriter(self._output)) def publish(self, record): if not self.isLoggable(record): return try: msg = self.getFormatter().format(record) if self._writer is not None: self._writer.write(msg + "\n") else: print(msg, file=_sys.stderr) self.flush() except Exception as e: self.reportError("Error publishing record", e, ErrorManager.WRITE_FAILURE) def flush(self): if self._writer is not None: try: self._writer.flush() except Exception: pass def close(self): self.flush() if self._writer is not None: try: self._writer.close() except Exception: pass super().close() def setOutputStream(self, out): self._output = out if isinstance(out, _io.TextIOBase): self._writer = out elif isinstance(out, _io.BufferedWriter): self._writer = _io.TextIOWrapper(out) else: self._writer = _io.TextIOWrapper(_io.BufferedWriter(out)) class ConsoleHandler(StreamHandler): """java.util.logging.ConsoleHandler""" def __init__(self): super().__init__(_sys.stderr, SimpleFormatter()) self.setLevel(Level.INFO) class FileHandler(Handler): """java.util.logging.FileHandler""" def __init__(self, pattern=None, append=None, limit=0, count=1): super().__init__() self._pattern = pattern or "logging.log" self._limit = limit self._count = count self._append = append or False self._files = [] if "%u" in self._pattern or "%g" in self._pattern: self._current_filename = self._pattern.replace("%u", "0").replace("%g", "0") else: self._current_filename = self._pattern try: mode = "a" if self._append else "w" self._file = open(self._current_filename, mode, encoding="utf-8") self._writer = self._file except Exception as e: self._file = None self._writer = None self.reportError("Cannot open file", e, ErrorManager.OPEN_FAILURE) self._formatter = SimpleFormatter() def publish(self, record): if not self.isLoggable(record): return if self._writer is None: return try: msg = self.getFormatter().format(record) self._writer.write(msg + "\n") self.flush() except Exception as e: self.reportError("Error publishing record", e, ErrorManager.WRITE_FAILURE) def flush(self): if self._writer is not None: try: self._writer.flush() except Exception: pass def close(self): self.flush() if self._writer is not None: try: self._writer.close() except Exception: pass super().close() class SocketHandler(Handler): """java.util.logging.SocketHandler""" def __init__(self, host, port): super().__init__() self._host = host self._port = port self._sock = None self._output_stream = None def connect(self): import socket self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._sock.connect((self._host, self._port)) self._output_stream = self._sock.makefile("wb") def publish(self, record): if not self.isLoggable(record): return if self._output_stream is None: try: self.connect() except Exception as e: self.reportError("Cannot connect", e, ErrorManager.OPEN_FAILURE) return try: msg = self.getFormatter().format(record) self._output_stream.write(msg.encode() + b"\n") self.flush() except Exception as e: self.reportError("Error sending record", e, ErrorManager.WRITE_FAILURE) def flush(self): if self._output_stream is not None: try: self._output_stream.flush() except Exception: pass def close(self): self.flush() if self._output_stream is not None: try: self._output_stream.close() except Exception: pass if self._sock is not None: try: self._sock.close() except Exception: pass super().close() class MemoryHandler(Handler): """java.util.logging.MemoryHandler""" def __init__(self, target=None, size=1000, push_level=Level.SEVERE): super().__init__() self._target = target self._size = size self._push_level = push_level self._buffer = [] def publish(self, record): if not self.isLoggable(record): return self._buffer.append(record) if len(self._buffer) >= self._size: self.push() elif record.getLevel().intValue() >= self._push_level.intValue(): self.push() def push(self): if self._target is not None: for rec in self._buffer: self._target.publish(rec) self._buffer.clear() self._target.flush() def flush(self): self.push() def close(self): self.push() if self._target is not None: self._target.close() super().close() def setPushLevel(self, level): self._push_level = level def getPushLevel(self): return self._push_level def setTarget(self, target): self._target = target def getTarget(self): return self._target # ============================================================ # Logger # ============================================================ class Logger(Stub): """java.util.logging.Logger""" _loggers = {} _root = None _global_logger = None def __init__(self, name, resource_bundle_name=None): super().__init__() self._name = name self._resource_bundle_name = resource_bundle_name self._level = None self._parent = None self._handlers = [] self._filter = None self._use_parent_handlers = True @staticmethod def getGlobal(): if Logger._global_logger is None: Logger._global_logger = Logger("global") return Logger._global_logger @staticmethod def getLogger(name, resource_bundle_name=None): if not name: if Logger._root is None: Logger._root = Logger("", resource_bundle_name) return Logger._root if name not in Logger._loggers: logger = Logger(name, resource_bundle_name) Logger._loggers[name] = logger logger._parent = Logger.getLogger("", None) return Logger._loggers[name] def getName(self): return self._name def getParent(self): return self._parent def setParent(self, parent): self._parent = parent def setLevel(self, level): self._level = level def getLevel(self): return self._level def isLoggable(self, level): if self._level is not None: return level.intValue() >= self._level.intValue() if self._parent is not None: return self._parent.isLoggable(level) return level.intValue() >= Level.INFO.intValue() def addHandler(self, handler): if handler not in self._handlers: self._handlers.append(handler) def removeHandler(self, handler): if handler in self._handlers: self._handlers.remove(handler) def getHandlers(self): return self._handlers def setUseParentHandlers(self, use): self._use_parent_handlers = use def getUseParentHandlers(self): return self._use_parent_handlers def setFilter(self, filter_): self._filter = filter_ def getFilter(self): return self._filter def getResourceBundle(self): return None def getResourceBundleName(self): return self._resource_bundle_name def log(self, level, msg_or_supplier, *args): if isinstance(level, Level): if self.isLoggable(level): if callable(msg_or_supplier) and not args: msg = msg_or_supplier() else: msg = msg_or_supplier record = LogRecord(level, msg) record.setLoggerName(self._name) record.setParameters(args or None) self._log(record) else: # log(LogRecord) self._log(level) def _log(self, record): if self._filter is not None and not self._filter.isLoggable(record): return for handler in self._handlers: if handler.isLoggable(record): handler.publish(record) if self._use_parent_handlers and self._parent is not None: self._parent._log(record) def severe(self, msg): self.log(Level.SEVERE, msg) def warning(self, msg): self.log(Level.WARNING, msg) def info(self, msg): self.log(Level.INFO, msg) def config(self, msg): self.log(Level.CONFIG, msg) def fine(self, msg): self.log(Level.FINE, msg) def finer(self, msg): self.log(Level.FINER, msg) def finest(self, msg): self.log(Level.FINEST, msg) def entering(self, source_class, source_method, param=None): msg = f"ENTRY {source_class}.{source_method}" if param is not None: msg += f" {param}" self.log(Level.FINER, msg) def exiting(self, source_class, source_method, result=None): msg = f"RETURN {source_class}.{source_method}" if result is not None: msg += f" {result}" self.log(Level.FINER, msg) def throwing(self, source_class, source_method, thrown): record = LogRecord(Level.FINER, "THROW") record.setLoggerName(self._name) record.setSourceClassName(source_class) record.setSourceMethodName(source_method) record.setThrown(thrown) self._log(record) def logp(self, level, source_class, source_method, msg, *params): if self.isLoggable(level): record = LogRecord(level, msg) record.setLoggerName(self._name) record.setSourceClassName(source_class) record.setSourceMethodName(source_method) record.setParameters(params or None) self._log(record) def logrb(self, level, source_class, source_method, bundle_name, msg, *params): if self.isLoggable(level): record = LogRecord(level, msg) record.setLoggerName(self._name) record.setSourceClassName(source_class) record.setSourceMethodName(source_method) record.setResourceBundleName(bundle_name) record.setParameters(params or None) self._log(record) @staticmethod def getAnonymousLogger(resource_bundle_name=None): logger = Logger("", resource_bundle_name) return logger # ============================================================ # LogManager # ============================================================ class LogManager(Stub): """java.util.logging.LogManager""" _singleton = None def __init__(self): super().__init__() self._loggers = {} self._properties = {} @staticmethod def getLogManager(): if LogManager._singleton is None: LogManager._singleton = LogManager() return LogManager._singleton def addLogger(self, logger): name = logger.getName() if name in self._loggers: return False self._loggers[name] = logger return True def getLogger(self, name): return self._loggers.get(name) def getLoggers(self): return list(self._loggers.values()) def readConfiguration(self, input_stream=None): pass def readConfiguration(self, ins): pass def reset(self): for logger in self._loggers.values(): for handler in logger.getHandlers()[:]: handler.close() logger.removeHandler(handler) logger.setLevel(None) def checkAccess(self): pass def addPropertyChangeListener(self, listener): pass def removePropertyChangeListener(self, listener): pass LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging" def getProperty(self, name): return self._properties.get(name) # ============================================================ # LoggingMXBean # ============================================================ class LoggingMXBean(Stub): """java.util.logging.LoggingMXBean""" def getLoggerLevel(self, logger_name): logger = LogManager.getLogManager().getLogger(logger_name) if logger and logger.getLevel(): return logger.getLevel().getName() return "" def setLoggerLevel(self, logger_name, level_name): logger = LogManager.getLogManager().getLogger(logger_name) if logger: level = Level() logger.setLevel(level.parse(level_name)) def getParentLogger(self, logger_name): logger = LogManager.getLogManager().getLogger(logger_name) if logger and logger.getParent(): return logger.getParent().getName() return "" def getLoggerNames(self): return list(LogManager.getLogManager()._loggers.keys()) # ============================================================ # LoggingPermission # ============================================================ class LoggingPermission(BasicPermission): """java.util.logging.LoggingPermission""" def __init__(self, name, actions=None): super().__init__(name, actions) class IllegalArgumentException(RuntimeException): """java.lang.IllegalArgumentException""" pass