Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
----------
Expand Down
33 changes: 31 additions & 2 deletions pgcli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import sys
import traceback
import logging
import logging.handlers
import threading
import shutil
import functools
Expand Down Expand Up @@ -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"]

Expand All @@ -546,7 +558,24 @@ 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)

# 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(
base_log_file,
when='midnight',
interval=1,
backupCount=30,
encoding='utf-8'
)
# Format: pgcli.YYYY-MM-DD.log
handler.suffix = ".%Y-%m-%d.log"

level_map = {
"CRITICAL": logging.CRITICAL,
Expand Down
9 changes: 6 additions & 3 deletions pgcli/pgclirc
Original file line number Diff line number Diff line change
Expand Up @@ -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.YYYY-MM-DD.log
# You can specify a custom path here if needed
log_file = default

# keyword casing preference. Possible values: "lower", "upper", "auto"
Expand Down
Loading