From 71698f3a1698587961f2ec1cb445428279ddeaa5 Mon Sep 17 00:00:00 2001 From: DiegoDAF Date: Thu, 4 Dec 2025 10:16:40 -0300 Subject: [PATCH 1/2] feat: implement daily log rotation and /var/log/pgcli location (v4.3.7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improvements to logging system: - Implement automatic daily log rotation at midnight - Keep 30 days of log history - Change default log location to /var/log/pgcli/pgcli.log - Automatic fallback to ~/.config/pgcli/log if no system permissions - Use TimedRotatingFileHandler for better log management - Log files formatted as: pgcli.log.YYYY-MM-DD Changes: - pgcli/main.py: Added logging.handlers import and updated initialize_logging() - pgcli/pgclirc: Updated documentation for new log behavior - pgcli/__init__.py: Bumped version to 4.3.7 - changelog.rst: Added Internal section entry 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- changelog.rst | 2 ++ pgcli/main.py | 27 +++++++++++++++++++++++++-- pgcli/pgclirc | 9 ++++++--- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/changelog.rst b/changelog.rst index 96eefd74..7b1c3a13 100644 --- a/changelog.rst +++ b/changelog.rst @@ -18,6 +18,8 @@ Internal: * Use github trusted publisher for pypi release * Update dev requirements and replace requirements-dev.txt with pyproject.toml * Use ruff instead of black +* Implement daily log rotation at midnight with 30-day retention using TimedRotatingFileHandler +* Change default log location to /var/log/pgcli/pgcli.log (with automatic fallback to ~/.config/pgcli/log if no permissions) Bug fixes: ---------- diff --git a/pgcli/main.py b/pgcli/main.py index 0b4b64f5..2415ac3d 100644 --- a/pgcli/main.py +++ b/pgcli/main.py @@ -9,6 +9,7 @@ import sys import traceback import logging +import logging.handlers import threading import shutil import functools @@ -537,7 +538,18 @@ def write_to_file(self, pattern, **_): def initialize_logging(self): log_file = self.config["main"]["log_file"] if log_file == "default": - log_file = config_location() + "log" + # Try /var/log/pgcli first, fallback to user config dir if no permissions + default_system_log = "/var/log/pgcli/pgcli.log" + try: + os.makedirs(os.path.dirname(default_system_log), exist_ok=True) + # Test if we can write to /var/log/pgcli + with open(default_system_log, "a"): + pass + log_file = default_system_log + except (OSError, PermissionError): + # Fallback to user's config directory if no system permissions + log_file = config_location() + "log" + ensure_dir_exists(log_file) log_level = self.config["main"]["log_level"] @@ -546,7 +558,18 @@ def initialize_logging(self): if log_level.upper() == "NONE": handler = logging.NullHandler() else: - handler = logging.FileHandler(os.path.expanduser(log_file)) + # Use TimedRotatingFileHandler for daily log rotation + # Rotates at midnight, keeps 30 days of logs + expanded_log_file = os.path.expanduser(log_file) + handler = logging.handlers.TimedRotatingFileHandler( + expanded_log_file, + when='midnight', + interval=1, + backupCount=30, + encoding='utf-8' + ) + # Format: pgcli.log.2025-12-02 + handler.suffix = "%Y-%m-%d" level_map = { "CRITICAL": logging.CRITICAL, diff --git a/pgcli/pgclirc b/pgcli/pgclirc index 63ccdaf3..9ef28453 100644 --- a/pgcli/pgclirc +++ b/pgcli/pgclirc @@ -66,9 +66,12 @@ generate_aliases = False alias_map_file = # log_file location. -# In Unix/Linux: ~/.config/pgcli/log -# In Windows: %USERPROFILE%\AppData\Local\dbcli\pgcli\log -# %USERPROFILE% is typically C:\Users\{username} +# Default behavior: +# - Tries /var/log/pgcli/pgcli.log first (if running with permissions) +# - Falls back to ~/.config/pgcli/log (Unix/Linux) or %USERPROFILE%\AppData\Local\dbcli\pgcli\log (Windows) +# Log rotation: Daily at midnight, keeps 30 days of logs +# Format: pgcli.log.YYYY-MM-DD +# You can specify a custom path here if needed log_file = default # keyword casing preference. Possible values: "lower", "upper", "auto" From 19a51cc19df7fb4ea284edd0658b221d17da962f Mon Sep 17 00:00:00 2001 From: DiegoDAF Date: Thu, 4 Dec 2025 13:42:26 -0300 Subject: [PATCH 2/2] fix: change log rotation format to pgcli.YYYY-MM-DD.log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed the log file rotation format from pgcli.log.YYYY-MM-DD to pgcli.YYYY-MM-DD.log for better file naming conventions. Changes: - pgcli/main.py: Updated suffix format and added logic to strip .log extension - pgcli/pgclirc: Updated documentation to reflect new format 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- pgcli/main.py | 12 +++++++++--- pgcli/pgclirc | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pgcli/main.py b/pgcli/main.py index 2415ac3d..3e62497b 100644 --- a/pgcli/main.py +++ b/pgcli/main.py @@ -561,15 +561,21 @@ def initialize_logging(self): # Use TimedRotatingFileHandler for daily log rotation # Rotates at midnight, keeps 30 days of logs expanded_log_file = os.path.expanduser(log_file) + + # Remove .log extension if present to avoid pgcli.log.log pattern + base_log_file = expanded_log_file + if base_log_file.endswith('.log'): + base_log_file = base_log_file[:-4] + handler = logging.handlers.TimedRotatingFileHandler( - expanded_log_file, + base_log_file, when='midnight', interval=1, backupCount=30, encoding='utf-8' ) - # Format: pgcli.log.2025-12-02 - handler.suffix = "%Y-%m-%d" + # Format: pgcli.YYYY-MM-DD.log + handler.suffix = ".%Y-%m-%d.log" level_map = { "CRITICAL": logging.CRITICAL, diff --git a/pgcli/pgclirc b/pgcli/pgclirc index 9ef28453..a789c0f1 100644 --- a/pgcli/pgclirc +++ b/pgcli/pgclirc @@ -70,7 +70,7 @@ alias_map_file = # - Tries /var/log/pgcli/pgcli.log first (if running with permissions) # - Falls back to ~/.config/pgcli/log (Unix/Linux) or %USERPROFILE%\AppData\Local\dbcli\pgcli\log (Windows) # Log rotation: Daily at midnight, keeps 30 days of logs -# Format: pgcli.log.YYYY-MM-DD +# Format: pgcli.YYYY-MM-DD.log # You can specify a custom path here if needed log_file = default