db_log.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import logging
  2. import os
  3. import sqlite3 as db
  4. import sys
  5. from math import inf
  6. from shutil import copyfile
  7. import time
  8. from typing import Optional
  9. import git
  10. DBName = str
  11. connected_dbs = [DBName]
  12. # get current commit at start time of program
  13. repo = git.Repo(search_parent_directories=True)
  14. CURRENT_SHA = repo.head.object.hexsha
  15. class DBLog:
  16. def __init__(self, db_name='log.db', create_if_not_exists=True):
  17. if db_name in connected_dbs:
  18. raise ValueError(f'There is already a connection to {db_name}.'
  19. 'If you want to re-use the same connection you can get it from `db_log.connected_dbs`.'
  20. 'If you want to disconnect you can call log.disconnect().')
  21. self.connection: Optional[db.Connection] = None
  22. self.cursor: Optional[db.Cursor] = None
  23. self.db_name: Optional[DBName] = None
  24. db_name = db_name.lower()
  25. if not os.path.isfile(db_name) and not create_if_not_exists:
  26. raise FileNotFoundError('There is no database with this name.')
  27. creating_new_db = not os.path.isfile(db_name)
  28. try:
  29. db_connection = db.connect(db_name, check_same_thread=False)
  30. # db_setup.create_functions(db_connection)
  31. # db_setup.set_pragmas(db_connection.cursor())
  32. # connection.text_factory = lambda x: x.encode('latin-1')
  33. except db.Error as e:
  34. print("Database error %s:" % e.args[0])
  35. raise
  36. self.connection = db_connection
  37. self.cursor = self.connection.cursor()
  38. self.db_name = db_name
  39. if creating_new_db:
  40. try:
  41. if os.path.isfile('/test-db/' + db_name):
  42. print('Using test logs')
  43. copyfile('/test-db/' + db_name, db_name)
  44. else:
  45. self.setup()
  46. except Exception:
  47. if self.connection is not None:
  48. self.connection.rollback()
  49. os.remove(db_name)
  50. raise
  51. self.connected = True
  52. self.min_level = -inf
  53. def disconnect(self, rollback=True):
  54. if rollback:
  55. self.connection.rollback()
  56. else:
  57. self.connection.commit()
  58. self.connection.close()
  59. self.connected = False
  60. def setup(self):
  61. self.cursor.execute('''
  62. CREATE TABLE IF NOT EXISTS entries(
  63. rowid INTEGER PRIMARY KEY,
  64. dt_created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  65. message TEXT NOT NULL,
  66. data BLOB, -- can be null
  67. pid INTEGER NOT NULl,
  68. message_type VARCHAR (25) NOT NULL, -- a plain text message title
  69. level INTEGER NOT NULL, -- relates to logging.ERROR and similar ones
  70. head_hex_sha VARCHAR-- SHA of currently checked out commit
  71. )
  72. ''')
  73. def log(self,
  74. message,
  75. level,
  76. message_type='generic',
  77. data=None,
  78. dt_created=None,
  79. current_pid=None,
  80. current_head_hex_sha=CURRENT_SHA,
  81. data_serialization_method=lambda x: x):
  82. if level < self.min_level:
  83. return
  84. if dt_created is None:
  85. dt_created = round(time.time())
  86. if current_pid is None:
  87. current_pid = os.getpid()
  88. data: str = data_serialization_method(data)
  89. self.cursor.execute('''
  90. INSERT INTO entries(message, data, dt_created, pid, head_hex_sha, message_type, level)
  91. VALUES (?, ?, ?, ?, ?, ?, ?)
  92. ''', (message, data, dt_created, current_pid, current_head_hex_sha, message_type, level))
  93. def debug(self, message, *args, **kwargs):
  94. self.log(message, logging.DEBUG, *args, **kwargs)
  95. def info(self, message, *args, **kwargs):
  96. self.log(message, logging.INFO, *args, **kwargs)
  97. def warning(self, message, *args, **kwargs):
  98. self.log(message, logging.WARNING, *args, **kwargs)
  99. warn = warning
  100. def error(self, message, *args, **kwargs):
  101. self.log(message, logging.ERROR, *args, **kwargs)
  102. def critical(self, message, *args, **kwargs):
  103. self.log(message, logging.CRITICAL, *args, **kwargs)
  104. fatal = critical
  105. def exception(self, msg, *args, data=None, **kwargs):
  106. if data is None:
  107. data = sys.exc_info()
  108. self.error(msg, *args, data=data, **kwargs)
  109. def commit(self):
  110. c = time.clock()
  111. self.connection.commit()
  112. delta = time.clock() - c
  113. print(f'Committing log files took {delta} seconds')
  114. return delta