From 1609b8fb1a9700bc44e3a33efa60904213b5192a Mon Sep 17 00:00:00 2001
From: anima
Date: Fri, 9 Aug 2024 16:37:30 +0200
Subject: [PATCH] initial version
---
LogHandlerSQLite.py | 152 ++++++++++++++++++++++++++++++++++++++++
__init__.py | 1 +
sql/insertDbVersion.sql | 4 ++
sql/insertLogs.sql | 5 ++
sql/selectDbVersion.sql | 1 +
sql/selectLogs.sql | 2 +
sql/selectLogsByPID.sql | 4 ++
sql/tables.sql | 15 ++++
8 files changed, 184 insertions(+)
create mode 100644 LogHandlerSQLite.py
create mode 100644 __init__.py
create mode 100644 sql/insertDbVersion.sql
create mode 100644 sql/insertLogs.sql
create mode 100644 sql/selectDbVersion.sql
create mode 100644 sql/selectLogs.sql
create mode 100644 sql/selectLogsByPID.sql
create mode 100644 sql/tables.sql
diff --git a/LogHandlerSQLite.py b/LogHandlerSQLite.py
new file mode 100644
index 0000000..c20aa33
--- /dev/null
+++ b/LogHandlerSQLite.py
@@ -0,0 +1,152 @@
+import logging
+
+import os
+import sqlite3 as sql
+import shutil
+
+class LogHandlerSQLite(logging.Handler):
+ """logging handler for sqlite3"""
+ __AUTHOR__ = 'anima'
+ __VERSION__ = '1.0.0'
+
+ def __init__(self, level: int | str = 0, dbfile: str = 'logs.db') -> None:
+ """logging handler for sqlite3
+
+ :params level: loglevel (see logging lib)
+ :type level: int | str
+ :params dbfile: filename for sqlite db
+ :type dbfile: str
+ """
+ self._dbfile : str = dbfile
+ self._sqlfiles : str = os.path.dirname(os.path.realpath(__file__)) + '/sql'
+ self.dbVersion : str = '1.0' # needed db version
+ self.__setup_db()
+ super().__init__(level)
+
+ ##> db general
+ def __db_con(self) -> sql.Connection:
+ """connection handler for with statements
+
+ :return: connection object
+ :rtype: sqlite3.Connection
+ """
+ return sql.connect(self._dbfile, detect_types=sql.PARSE_DECLTYPES | sql.PARSE_COLNAMES)
+
+ def __db_query(self, queryfile: str, payload: list = None, getId: bool = False) -> list | None:
+ """send a query to db set at `dbfile`
+
+ :param queryfile: filename for sql file (without .sql) multible querys per file possable (split with ;)
+ :type queryfile: str
+ :param payload: send payload to sql query
+ :type payload: list
+ :param getId: if True this will return the id of newest row inerst form this query
+ :type getId: bool
+
+ :return: db data is query was select or None if no data matches # known bug: works only if payload set (empty possable)
+ :rtype: list | None
+ """
+ with open(f'{self._sqlfiles}/{queryfile}.sql', 'r') as sqlfile:
+ sqlscript = sqlfile.read()
+
+ with self.__db_con() as con:
+ cur = con.cursor()
+ if isinstance(payload, list):
+ cur.execute(sqlscript, payload)
+ id = cur.lastrowid
+ else:
+ cur.executescript(sqlscript)
+ data = cur.fetchall()
+ if getId:
+ return id
+ elif len(data) > 0:
+ return data
+
+ def __setup_db(self) -> None:
+ """init a new db if need check version of db and start update until `dbVersion` match
+
+ :return: none
+ """
+ self.__db_query('tables')
+ if self.__get_db_version() is None:
+ self.__db_query('insertDbVersion')
+
+ while not self.dbVersion == self.__get_db_version():
+ self.__update_db(self.__get_db_version())
+
+ def __update_db(self, version) -> None:
+ """update db file; create a backup for every version
+
+ :param version: needed version; version structure conroled by sql files
+ :type version: str
+
+ :return: none
+ """
+ shutil.copyfile(f'{self._dbfile}', f'{self._dbfile}.v-{version}.bak')
+ self.__db_query(f'update/updateDb-{version}')
+
+ ##> select (get) querys
+ def __get_db_version(self) -> str | None:
+ """read dbVersion from db
+
+ :return: version from db
+ :rtype: str
+
+ """
+ data = self.__db_query('selectDbVersion', [])
+ if data is None:
+ return data
+ else:
+ return data[0][0]
+
+ def get_log(self) -> list[tuple] | None:
+ """get all logs from db
+
+ :return: all logs from db
+ :rtype: list[tuple]
+ """
+ return self.__db_query('selectLogs', [])
+
+ def get_log_by_pid(self, pid: int = os.getpid()) -> list[tuple] | None:
+ """get all logs from db match the pid
+
+ :param pid: process id (default is own from running instance)
+ :type pid: int
+
+ :return: all logs match the pid or none
+ :rtype: list[tuple] | None
+ """
+ if isinstance(pid, int):
+ return self.__db_query('selectLogsByPID', [pid])
+ else: return None
+
+ ##> logging methods
+ def emit(self, record: logging.LogRecord) -> None:
+ """save log to db
+
+ :param record: logging record called from logging lib
+ :type record: logging.LogRecord
+
+ """
+ data = [
+ record.created,
+ record.process,
+ record.name,
+ record.levelname,
+ record.levelno,
+ record.filename,
+ record.lineno,
+ record.msg,
+ ]
+ self.__db_query('insertLogs', data)
+
+if __name__ == "__main__":
+ log = logging.getLogger(__name__)
+ log.setLevel(10)
+
+ logDB = LogHandlerSQLite()
+ log.addHandler(logDB)
+
+ log.error('Here is a info')
+ print(logDB.get_log_by_pid())
+
+ t = LogHandlerSQLite()
\ No newline at end of file
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..2d67a92
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1 @@
+from .LogHandlerSQLite import LogHandlerSQLite
\ No newline at end of file
diff --git a/sql/insertDbVersion.sql b/sql/insertDbVersion.sql
new file mode 100644
index 0000000..38b2657
--- /dev/null
+++ b/sql/insertDbVersion.sql
@@ -0,0 +1,4 @@
+INSERT INTO dbVersion
+ (dbVersion)
+VALUES
+ ('1.0');
\ No newline at end of file
diff --git a/sql/insertLogs.sql b/sql/insertLogs.sql
new file mode 100644
index 0000000..23c881e
--- /dev/null
+++ b/sql/insertLogs.sql
@@ -0,0 +1,5 @@
+INSERT INTO logs
+ (logTime, process, logHandler, levelName, levelNo, fileName, fileLineNo, message)
+VALUES
+ (:1, :2, :3, :4, :5, :6, :7, :8)
+;
\ No newline at end of file
diff --git a/sql/selectDbVersion.sql b/sql/selectDbVersion.sql
new file mode 100644
index 0000000..e51d1d1
--- /dev/null
+++ b/sql/selectDbVersion.sql
@@ -0,0 +1 @@
+SELECT * FROM dbVersion;
\ No newline at end of file
diff --git a/sql/selectLogs.sql b/sql/selectLogs.sql
new file mode 100644
index 0000000..4d42219
--- /dev/null
+++ b/sql/selectLogs.sql
@@ -0,0 +1,2 @@
+SELECT *
+FROM logs
\ No newline at end of file
diff --git a/sql/selectLogsByPID.sql b/sql/selectLogsByPID.sql
new file mode 100644
index 0000000..a8d6c65
--- /dev/null
+++ b/sql/selectLogsByPID.sql
@@ -0,0 +1,4 @@
+SELECT *
+FROM logs
+WHERE process == ?
+;
\ No newline at end of file
diff --git a/sql/tables.sql b/sql/tables.sql
new file mode 100644
index 0000000..db6e5dc
--- /dev/null
+++ b/sql/tables.sql
@@ -0,0 +1,15 @@
+CREATE TABLE IF NOT EXISTS dbVersion (
+ dbVersion TEXT PRIMARY KEY NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS logs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ logTime datetime,
+ process INTEGER,
+ logHandler TEXT,
+ levelName TEXT,
+ levelNo INTEGER,
+ fileName TEXT,
+ fileLineNo INTEGER,
+ message TEXT NOT NULL
+);
\ No newline at end of file