Skip to content

Add a log rotation function to the file handler #56

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 18, 2024
Merged
Changes from 7 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
84 changes: 80 additions & 4 deletions adafruit_logging.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2019 Dave Astels for Adafruit Industries
# SPDX-FileCopyrightText: 2024 Pat Satyshur
#
# SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -58,9 +59,11 @@

import time
import sys
import os
from collections import namedtuple

try:
# pylint: disable=deprecated-class
from typing import Optional, Hashable
from typing_extensions import Protocol

Expand Down Expand Up @@ -205,16 +208,88 @@ def emit(self, record: LogRecord) -> None:


class FileHandler(StreamHandler):
"""File handler for working with log files off of the microcontroller (like
an SD card)
"""File handler for working with log files off of the microcontroller (like an SD card).
This handler implements a very simple log rotating system. If LogFileSizeLimit is set, the
handler will check to see if the log file is larger than the given limit. If the log file is
larger than the limit, it is renamed and a new file is started for log entries. The old log
file is renamed with the OldFilePostfix variable appended to the name. If another file exsists
with this old file name, it will be deleted. Because there are two log files. The max size of
the log files is two times the limit set by LogFileSizeLimit.

:param str filename: The filename of the log file
:param str mode: Whether to write ('w') or append ('a'); default is to append
:param int LogFileSizeLimit: The max allowable size of the log file in bytes.
:param str OldFilePostfix: What to append to the filename for the old log file. Defaults
to '_old'.
"""

def __init__(self, filename: str, mode: str = "a") -> None:
def __init__(
self,
filename: str,
mode: str = "a",
LogFileSizeLimit: int = None,
OldFilePostfix: str = "_old",
) -> None:
if mode == "r":
raise ValueError("Can't write to a read only file")

self._LogFileName = filename
self._WriteMode = mode
self._OldFilePostfix = OldFilePostfix
self._LogFileSizeLimit = LogFileSizeLimit

# Here we are assuming that if there is a period in the filename, the stuff after the period
# is the extension of the file. It is possible that is not the case, but probably unlikely.
if "." in filename:
[basefilename, extension] = filename.rsplit(".", 1)
self._OldLogFileName = basefilename + self._OldFilePostfix + "." + extension
else:
basefilename = filename
self._OldLogFileName = basefilename + self._OldFilePostfix

# pylint: disable=consider-using-with
super().__init__(open(self._LogFileName, mode=self._WriteMode))
self.CheckLogSize()

def CheckLogSize(self) -> None:
"""Check the size of the log file and rotate if needed."""
if self._LogFileSizeLimit is None:
# No log limit set
return

# Close the log file. Probably needed if we want to delete/rename files.
self.close()

# Get the size of the log file.
try:
LogFileSize = os.stat(self._LogFileName)[6]
except OSError as e:
if e.args[0] == 2:
# Log file does not exsist. This is okay.
LogFileSize = None
else:
raise e

# Get the size of the old log file.
try:
OldLogFileSize = os.stat(self._OldLogFileName)[6]
except OSError as e:
if e.args[0] == 2:
# Log file does not exsist. This is okay.
OldLogFileSize = None
else:
raise e

# This checks if the log file is too big. If so, it deletes the old log file and renames the
# log file to the old log filename.
if LogFileSize > self._LogFileSizeLimit:
if OldLogFileSize is not None:
os.remove(self._OldLogFileName)
os.rename(self._LogFileName, self._OldLogFileName)

# Reopen the file.
# pylint: disable=consider-using-with
super().__init__(open(filename, mode=mode))
self.stream = open(self._LogFileName, mode=self._WriteMode)

def close(self) -> None:
"""Closes the file"""
Expand All @@ -233,6 +308,7 @@ def emit(self, record: LogRecord) -> None:

:param record: The record (message object) to be logged
"""
self.CheckLogSize()
self.stream.write(self.format(record))


Expand Down
Loading