Add proper logging

This commit is contained in:
Fabian 2022-09-29 17:56:51 +02:00
parent 4761d4b82f
commit 832e768a4e
10 changed files with 62 additions and 38 deletions

View File

@ -8,6 +8,7 @@ pyside6 = "~=6.3"
requests = "~=2.28" requests = "~=2.28"
requests-futures = "~=1.0" requests-futures = "~=1.0"
packaging = "~=21.3" packaging = "~=21.3"
loguru = "~=0.6"
[dev-packages] [dev-packages]
pyinstaller = "~=5.1" pyinstaller = "~=5.1"

10
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "633aca7e0b43a295ce3429d3e12976a4c16dcf422cee53e37d266f8000ef5feb" "sha256": "b04dfa0eda7fd2df5b6d27d5a1f7d71168c984c74ce363c45a888430bca7b7d8"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -40,6 +40,14 @@
"markers": "python_version >= '3.5'", "markers": "python_version >= '3.5'",
"version": "==3.4" "version": "==3.4"
}, },
"loguru": {
"hashes": [
"sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c",
"sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"
],
"index": "pypi",
"version": "==0.6.0"
},
"packaging": { "packaging": {
"hashes": [ "hashes": [
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",

View File

@ -22,6 +22,7 @@ install_requires =
requests requests
requests-futures requests-futures
packaging packaging
loguru
[options.packages.find] [options.packages.find]
where = src where = src

View File

@ -1,22 +1,22 @@
import sys import sys
import traceback
from copy import copy from copy import copy
from pathlib import Path
from textwrap import dedent from textwrap import dedent
from threading import Lock from threading import Lock
from typing import Optional from typing import Optional
from loguru import logger
from packaging.version import Version from packaging.version import Version
from requests_futures.sessions import FuturesSession from requests_futures.sessions import FuturesSession
from fime.progressindicator import ProgressIndicator
from fime.util import get_icon
try: try:
from PySide6 import QtCore, QtGui, QtWidgets from PySide6 import QtCore, QtGui, QtWidgets
except ImportError: except ImportError:
from PySide2 import QtCore, QtGui, QtWidgets from PySide2 import QtCore, QtGui, QtWidgets
import fime import fime
from fime.progressindicator import ProgressIndicator
from fime.util import get_icon
URL = "https://gitlab.com/faerbit/fime/-/releases/permalink/latest" URL = "https://gitlab.com/faerbit/fime/-/releases/permalink/latest"
@ -51,8 +51,7 @@ class UpdateChecker:
else: else:
self.result = f"Newer fime version available: {latest_version}" self.result = f"Newer fime version available: {latest_version}"
except Exception: except Exception:
print("Could not get update info:\n", file=sys.stderr) logger.exception("Could not get update info")
print(traceback.format_exc(), file=sys.stderr)
finally: finally:
with self.lock: with self.lock:
self._done = True self._done = True
@ -63,6 +62,8 @@ class About(QtWidgets.QDialog):
super().__init__(parent, *args, **kwargs) super().__init__(parent, *args, **kwargs)
self.setWindowTitle("About") self.setWindowTitle("About")
log_dir_path = Path(QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppDataLocation)) / "logs"
text = dedent(f"""\ text = dedent(f"""\
fime fime
Copyright (c) 2020 - 2022 Faerbit Copyright (c) 2020 - 2022 Faerbit
@ -71,6 +72,8 @@ class About(QtWidgets.QDialog):
fime Version {fime.__version__} fime Version {fime.__version__}
Qt Version {QtCore.__version__} Qt Version {QtCore.__version__}
Python Version {sys.version} Python Version {sys.version}
Log directory: {log_dir_path}
""") """)
text = text.replace("\n", "<br/>") text = text.replace("\n", "<br/>")
version_label = QtWidgets.QLabel(self) version_label = QtWidgets.QLabel(self)

View File

@ -1,6 +1,8 @@
from configparser import ConfigParser from configparser import ConfigParser
from pathlib import Path from pathlib import Path
from loguru import logger
try: try:
from PySide6 import QtCore from PySide6 import QtCore
except ImportError: except ImportError:
@ -22,7 +24,7 @@ class Config:
config_dir_path = Path(QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppConfigLocation)) config_dir_path = Path(QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppConfigLocation))
config_path = config_dir_path / "fime.conf" config_path = config_dir_path / "fime.conf"
if config_path.exists(): if config_path.exists():
print(f'Reading config file "{config_path}"') logger.info(f'Reading config file "{config_path}"')
with open(config_path) as f: with open(config_path) as f:
config_text = f.read() config_text = f.read()
config_text = "[DEFAULT]\n" + config_text config_text = "[DEFAULT]\n" + config_text

View File

@ -8,6 +8,8 @@ from datetime import datetime, date, timedelta, time
from threading import Thread, Event from threading import Thread, Event
from typing import List, Tuple, Dict, Union from typing import List, Tuple, Dict, Union
from loguru import logger
try: try:
from PySide6 import QtCore from PySide6 import QtCore
except ImportError: except ImportError:
@ -63,7 +65,7 @@ class Data(MutableMapping):
def _save(self): def _save(self):
for key in self._hot_keys: for key in self._hot_keys:
print(f"... saving dict {key} ...") logger.info(f"saving dict {key}")
to_write = self._cache[key] # apparently thread-safe to_write = self._cache[key] # apparently thread-safe
with open(self.data_path.format(key), "w+") as f: with open(self.data_path.format(key), "w+") as f:
f.write(json.dumps(to_write)) f.write(json.dumps(to_write))

View File

@ -1,11 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os
import signal import signal
import sys import sys
from functools import partial from functools import partial
from pathlib import Path
from fime.about import About from loguru import logger
from fime.config import Config
from fime.worklog import WorklogDialog
try: try:
from PySide6 import QtCore, QtWidgets from PySide6 import QtCore, QtWidgets
@ -14,6 +14,9 @@ except ImportError:
from PySide2 import QtCore, QtWidgets from PySide2 import QtCore, QtWidgets
PYSIDE_6 = False PYSIDE_6 = False
from fime.about import About
from fime.config import Config
from fime.worklog import WorklogDialog
from fime.data import Tasks, Log, Data, LogCommentsData, Worklog, Report from fime.data import Tasks, Log, Data, LogCommentsData, Worklog, Report
from fime.exceptions import FimeException from fime.exceptions import FimeException
from fime.import_task import ImportTask from fime.import_task import ImportTask
@ -143,7 +146,7 @@ class App:
action.triggered.connect(item[1]) action.triggered.connect(item[1])
def sigterm_handler(self, signo, _frame): def sigterm_handler(self, signo, _frame):
print(f'handling signal "{signal.strsignal(signo)}"') logger.debug(f'handling signal "{signal.strsignal(signo)}"')
self.app.quit() self.app.quit()
def run(self): def run(self):
@ -164,19 +167,27 @@ class App:
self.taskEdit.show() self.taskEdit.show()
def excepthook(original, e_type, e_value, tb_obj): def init_logging():
log_dir_path = Path(QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppDataLocation)) / "logs"
logger.add(log_dir_path / "fime_{time:YYYY-MM-DD}.log", rotation="1d", retention="30d", compression="zip", level="INFO")
def excepthook(e_type, e_value, tb_obj):
if e_type is FimeException: if e_type is FimeException:
QtWidgets.QMessageBox.critical(None, "Error", str(e_value), QtWidgets.QMessageBox.Ok) QtWidgets.QMessageBox.critical(None, "Error", str(e_value), QtWidgets.QMessageBox.Ok)
elif issubclass(e_type, KeyboardInterrupt):
sys.__excepthook__(e_type, e_value, tb_obj)
else: else:
original(e_type, e_value, tb_obj) logger.critical("Unhandled exception", exc_info=(e_type, e_value, tb_obj))
sys.__excepthook__(e_type, e_value, tb_obj)
def main(): def main():
# important for QStandardPath to be correct # important for QStandardPath to be correct
QtCore.QCoreApplication.setApplicationName("fime") QtCore.QCoreApplication.setApplicationName("fime")
init_logging()
# also catches exceptions in other threads # also catches exceptions in other threads
original_excepthook = sys.excepthook sys.excepthook = excepthook
sys.excepthook = partial(excepthook, original_excepthook)
app = App() app = App()
app.run() app.run()

View File

@ -1,11 +1,11 @@
import os import os
import sys
import threading import threading
import traceback
from enum import Enum, auto from enum import Enum, auto
from functools import reduce, partial from functools import reduce, partial
from queue import Queue, Empty from queue import Queue, Empty
from loguru import logger
try: try:
from PySide6 import QtCore, QtWidgets from PySide6 import QtCore, QtWidgets
except ImportError: except ImportError:
@ -105,12 +105,11 @@ class TaskCompleter(QtWidgets.QCompleter):
}) })
else: else:
if not self.escalate: if not self.escalate:
print("No picker results. Escalating") logger.debug("No picker results. Escalating")
self.escalate = True self.escalate = True
self.update_search() self.update_search()
except Exception: except Exception:
print("Ignoring exception, as it only breaks autocompletion:", file=sys.stderr) logger.exception("Ignoring exception, as it only breaks autocompletion")
print(traceback.format_exc(), file=sys.stderr)
return return
def update_search(self): def update_search(self):
@ -144,6 +143,5 @@ class TaskCompleter(QtWidgets.QCompleter):
"result": extracted, "result": extracted,
}) })
except Exception: except Exception:
print("Ignoring exception, as it only breaks autocompletion:", file=sys.stderr) logger.exception("Ignoring exception, as it only breaks autocompletion")
print(traceback.format_exc(), file=sys.stderr)
return return

View File

@ -1,5 +1,7 @@
import enum import enum
from loguru import logger
try: try:
from PySide6 import QtCore, QtGui, QtWidgets from PySide6 import QtCore, QtGui, QtWidgets
except ImportError: except ImportError:
@ -13,7 +15,7 @@ def get_screen_height(qobject):
if hasattr(qobject, "screen"): if hasattr(qobject, "screen"):
return qobject.screen().size().height() return qobject.screen().size().height()
else: else:
print("unable to detect screen height falling back to default value of 1080") logger.info("unable to detect screen height falling back to default value of 1080")
return 1080 return 1080
@ -21,7 +23,7 @@ def get_screen_width(qobject):
if hasattr(qobject, "screen"): if hasattr(qobject, "screen"):
return qobject.screen().size().width() return qobject.screen().size().width()
else: else:
print("unable to detect screen width falling back to default value of 1920") logger.info("unable to detect screen width falling back to default value of 1920")
return 1920 return 1920

View File

@ -1,6 +1,4 @@
import os import os
import sys
import traceback
from concurrent.futures import Future from concurrent.futures import Future
from datetime import date, datetime, timedelta, time from datetime import date, datetime, timedelta, time
from functools import partial from functools import partial
@ -9,6 +7,7 @@ from threading import Lock
from typing import List, Dict, Tuple, Optional from typing import List, Dict, Tuple, Optional
import requests import requests
from loguru import logger
from requests_futures.sessions import FuturesSession from requests_futures.sessions import FuturesSession
from fime.config import Config from fime.config import Config
@ -42,12 +41,9 @@ class WorklogRest:
future.add_done_callback(self._resp_user) future.add_done_callback(self._resp_user)
return future return future
@logger.catch(message="Could not get user key")
def _resp_user(self, future): def _resp_user(self, future):
try:
self._user = future.result().json()["key"] self._user = future.result().json()["key"]
except Exception:
print("Could not get user key:\n", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr)
def get_issues_state(self, issue_keys: List[str], pdate: date) -> List[Tuple[Status, str, Optional[str]]]: def get_issues_state(self, issue_keys: List[str], pdate: date) -> List[Tuple[Status, str, Optional[str]]]:
ret = [] ret = []
@ -114,9 +110,9 @@ class WorklogRest:
worklog_found = True worklog_found = True
break break
if worklog_found: if worklog_found:
print(f"Found existing worklog for issue {issue_key}") logger.debug(f"Found existing worklog for issue {issue_key}")
else: else:
print(f"Did not find existing worklog for issue {issue_key}") logger.debug(f"Did not find existing worklog for issue {issue_key}")
self._issue_state[issue_key] = (Status.OK, issue_title) self._issue_state[issue_key] = (Status.OK, issue_title)
def _upload_sanity_check(self, issue_keys: List[str]): def _upload_sanity_check(self, issue_keys: List[str]):
@ -183,7 +179,7 @@ class WorklogRest:
with self._issues_lock: with self._issues_lock:
if resp.status_code in (200, 201): if resp.status_code in (200, 201):
self._issue_state[issue_key] = (Status.OK, "Successfully uploaded") self._issue_state[issue_key] = (Status.OK, "Successfully uploaded")
print(f"Successfully uploaded issue {issue_key}") logger.info(f"Successfully uploaded issue {issue_key}")
else: else:
msg = dedent(f"""\ msg = dedent(f"""\
Worklog upload failed: Worklog upload failed:
@ -193,4 +189,4 @@ class WorklogRest:
Response: {resp.text} Response: {resp.text}
""") """)
self._issue_state[issue_key] = (Status.ERROR, msg) self._issue_state[issue_key] = (Status.ERROR, msg)
print(msg, file=sys.stderr) logger.error(msg)