From e8123a6ab1d4eab03153ee04515219eb9dd4bfe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 10 Oct 2016 19:27:18 +0200 Subject: [PATCH 1/4] Custom logging handler that cleans files on startup --- src/octoprint/logging/__init__.py | 4 ++++ src/octoprint/logging/handlers.py | 16 ++++++++++++++++ src/octoprint/server/__init__.py | 4 ++-- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 src/octoprint/logging/__init__.py create mode 100644 src/octoprint/logging/handlers.py diff --git a/src/octoprint/logging/__init__.py b/src/octoprint/logging/__init__.py new file mode 100644 index 00000000..6e016b16 --- /dev/null +++ b/src/octoprint/logging/__init__.py @@ -0,0 +1,4 @@ +# coding=utf-8 +from __future__ import absolute_import + +from . import handlers diff --git a/src/octoprint/logging/handlers.py b/src/octoprint/logging/handlers.py new file mode 100644 index 00000000..ad4fcadb --- /dev/null +++ b/src/octoprint/logging/handlers.py @@ -0,0 +1,16 @@ +# coding=utf-8 +from __future__ import absolute_import + +import logging.handlers +import os + +class CleaningTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler): + + def __init__(self, *args, **kwargs): + logging.handlers.TimedRotatingFileHandler.__init__(self, *args, **kwargs) + + # clean up old files on handler start + if self.backupCount > 0: + for s in self.getFilesToDelete(): + os.remove(s) + diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 902930df..e74c19a8 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -574,11 +574,11 @@ class Server(object): "stream": "ext://sys.stdout" }, "file": { - "class": "logging.handlers.TimedRotatingFileHandler", + "class": "octoprint.logging.handlers.CleaningTimedRotatingFileHandler", "level": "DEBUG", "formatter": "simple", "when": "D", - "backupCount": "1", + "backupCount": 7, "filename": os.path.join(settings().getBaseFolder("logs"), "octoprint.log") }, "serialFile": { From 139b2277d115f22031bf1dddb4d00530b9ce59bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 10 Oct 2016 19:45:34 +0200 Subject: [PATCH 2/4] Fix missing absolute import declarations --- src/octoprint/__init__.py | 2 ++ src/octoprint/timelapse.py | 1 + src/octoprint/util/__init__.py | 2 ++ 3 files changed, 5 insertions(+) diff --git a/src/octoprint/__init__.py b/src/octoprint/__init__.py index df198672..d45166b5 100644 --- a/src/octoprint/__init__.py +++ b/src/octoprint/__init__.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +from __future__ import absolute_import + import sys from octoprint.daemon import Daemon from octoprint.server import Server diff --git a/src/octoprint/timelapse.py b/src/octoprint/timelapse.py index 026a897d..3e404fcc 100644 --- a/src/octoprint/timelapse.py +++ b/src/octoprint/timelapse.py @@ -1,4 +1,5 @@ # coding=utf-8 +from __future__ import absolute_import __author__ = "Gina Häußge " __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' diff --git a/src/octoprint/util/__init__.py b/src/octoprint/util/__init__.py index 97aa8a45..afd3ff41 100644 --- a/src/octoprint/util/__init__.py +++ b/src/octoprint/util/__init__.py @@ -1,4 +1,6 @@ # coding=utf-8 +from __future__ import absolute_import + """ This module bundles commonly used utility methods or helper classes that are used in multiple places withing OctoPrint's source code. From 343631b3c6ce63776be3b58c3b557fa2baa3a214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 11 Oct 2016 10:53:37 +0200 Subject: [PATCH 3/4] Custom SerialLoggingHandler Does roll overs on next connections to the printer --- src/octoprint/logging/handlers.py | 67 +++++++++++++++++++++++++++++++ src/octoprint/printer/standard.py | 4 ++ src/octoprint/server/__init__.py | 12 +++--- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/octoprint/logging/handlers.py b/src/octoprint/logging/handlers.py index ad4fcadb..fb613429 100644 --- a/src/octoprint/logging/handlers.py +++ b/src/octoprint/logging/handlers.py @@ -3,6 +3,8 @@ from __future__ import absolute_import import logging.handlers import os +import re +import time class CleaningTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler): @@ -14,3 +16,68 @@ class CleaningTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler for s in self.getFilesToDelete(): os.remove(s) + +class SerialLogHandler(logging.handlers.RotatingFileHandler): + + _do_rollover = False + _suffix_template = "%Y-%m-%d_%H-%M-%S" + _file_pattern = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$") + + @classmethod + def on_open_connection(cls): + cls._do_rollover = True + + def __init__(self, *args, **kwargs): + logging.handlers.RotatingFileHandler.__init__(self, *args, **kwargs) + self.cleanupFiles() + + def emit(self, record): + logging.handlers.RotatingFileHandler.emit(self, record) + + def shouldRollover(self, record): + return self.__class__._do_rollover + + def getFilesToDelete(self): + """ + Determine the files to delete when rolling over. + """ + dirName, baseName = os.path.split(self.baseFilename) + fileNames = os.listdir(dirName) + result = [] + prefix = baseName + "." + plen = len(prefix) + for fileName in fileNames: + if fileName[:plen] == prefix: + suffix = fileName[plen:] + if self.__class__._file_pattern.match(suffix): + result.append(os.path.join(dirName, fileName)) + result.sort() + if len(result) < self.backupCount: + result = [] + else: + result = result[:len(result) - self.backupCount] + return result + + def cleanupFiles(self): + if self.backupCount > 0: + for path in self.getFilesToDelete(): + os.remove(path) + + def doRollover(self): + self.__class__._do_rollover = False + + if self.stream: + self.stream.close() + self.stream = None + + if os.path.exists(self.baseFilename): + # figure out creation date/time to use for file suffix + t = time.localtime(os.stat(self.baseFilename).st_mtime) + dfn = self.baseFilename + "." + time.strftime(self.__class__._suffix_template, t) + if os.path.exists(dfn): + os.remove(dfn) + os.rename(self.baseFilename, dfn) + + self.cleanupFiles() + if not self.delay: + self.stream = self._open() diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 83b81a9f..8281aad1 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -194,6 +194,10 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): if self._comm is not None: self._comm.close() self._printerProfileManager.select(profile) + + from octoprint.logging.handlers import SerialLogHandler + SerialLogHandler.on_open_connection() + self._comm = comm.MachineCom(port, baudrate, callbackObject=self, printerProfileManager=self._printerProfileManager) def disconnect(self): diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index e74c19a8..b076670b 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -564,6 +564,9 @@ class Server(object): "formatters": { "simple": { "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + }, + "serial": { + "format": "%(asctime)s - %(message)s" } }, "handlers": { @@ -578,14 +581,14 @@ class Server(object): "level": "DEBUG", "formatter": "simple", "when": "D", - "backupCount": 7, + "backupCount": 6, "filename": os.path.join(settings().getBaseFolder("logs"), "octoprint.log") }, "serialFile": { - "class": "logging.handlers.RotatingFileHandler", + "class": "octoprint.logging.handlers.SerialLogHandler", "level": "DEBUG", - "formatter": "simple", - "maxBytes": 2 * 1024 * 1024, # let's limit the serial log to 2MB in size + "formatter": "serial", + "backupCount": 3, "filename": os.path.join(settings().getBaseFolder("logs"), "serial.log") } }, @@ -633,7 +636,6 @@ class Server(object): if settings().getBoolean(["serial", "log"]): # enable debug logging to serial.log logging.getLogger("SERIAL").setLevel(logging.DEBUG) - logging.getLogger("SERIAL").debug("Enabling serial logging") def _setup_app(self, app): from octoprint.server.util.flask import ReverseProxiedEnvironment, OctoPrintFlaskRequest, OctoPrintFlaskResponse From 688a998dd249f77a827215b562cc976f55688fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 11 Oct 2016 10:54:16 +0200 Subject: [PATCH 4/4] Use CleaningTimedRotatingFileHandler in bundled plugins --- src/octoprint/plugins/cura/__init__.py | 3 ++- src/octoprint/plugins/pluginmanager/__init__.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/octoprint/plugins/cura/__init__.py b/src/octoprint/plugins/cura/__init__.py index bbce724f..773f62f2 100644 --- a/src/octoprint/plugins/cura/__init__.py +++ b/src/octoprint/plugins/cura/__init__.py @@ -46,7 +46,8 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin, def on_startup(self, host, port): # setup our custom logger - cura_logging_handler = logging.handlers.RotatingFileHandler(self._settings.get_plugin_logfile_path(postfix="engine"), maxBytes=2*1024*1024) + from octoprint.logging.handlers import CleaningTimedRotatingFileHandler + cura_logging_handler = CleaningTimedRotatingFileHandler(self._settings.get_plugin_logfile_path(postfix="engine"), when="D", backupCount=3) cura_logging_handler.setFormatter(logging.Formatter("%(asctime)s %(message)s")) cura_logging_handler.setLevel(logging.DEBUG) diff --git a/src/octoprint/plugins/pluginmanager/__init__.py b/src/octoprint/plugins/pluginmanager/__init__.py index eb990d68..c76fbb39 100644 --- a/src/octoprint/plugins/pluginmanager/__init__.py +++ b/src/octoprint/plugins/pluginmanager/__init__.py @@ -65,7 +65,8 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, ##~~ StartupPlugin def on_startup(self, host, port): - console_logging_handler = logging.handlers.RotatingFileHandler(self._settings.get_plugin_logfile_path(postfix="console"), maxBytes=2*1024*1024) + from octoprint.logging.handlers import CleaningTimedRotatingFileHandler + console_logging_handler = CleaningTimedRotatingFileHandler(self._settings.get_plugin_logfile_path(postfix="console"), when="D", backupCount=3) console_logging_handler.setFormatter(logging.Formatter("%(asctime)s %(message)s")) console_logging_handler.setLevel(logging.DEBUG)