Log rotation.

This commit is contained in:
tecnovert
2025-03-12 10:40:32 +02:00
parent 7d5f7e0936
commit 826527fea9
10 changed files with 313 additions and 276 deletions

View File

@@ -5,17 +5,18 @@
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import os
import time
import shlex
import socks
import random
import socket
import urllib
import logging import logging
import threading import os
import traceback import random
import shlex
import socket
import socks
import subprocess import subprocess
import sys
import threading
import time
import traceback
import urllib
from sockshandler import SocksiPyHandler from sockshandler import SocksiPyHandler
@@ -42,9 +43,9 @@ def getaddrinfo_tor(*args):
class BaseApp(DBMethods): class BaseApp(DBMethods):
def __init__(self, fp, data_dir, settings, chain, log_name="BasicSwap"): def __init__(self, data_dir, settings, chain, log_name="BasicSwap"):
self.fp = None
self.log_name = log_name self.log_name = log_name
self.fp = fp
self.fail_code = 0 self.fail_code = 0
self.mock_time_offset = 0 self.mock_time_offset = 0
@@ -71,24 +72,33 @@ class BaseApp(DBMethods):
self.default_socket_timeout = socket.getdefaulttimeout() self.default_socket_timeout = socket.getdefaulttimeout()
self.default_socket_getaddrinfo = socket.getaddrinfo self.default_socket_getaddrinfo = socket.getaddrinfo
def __del__(self):
if self.fp:
self.fp.close()
def stopRunning(self, with_code=0): def stopRunning(self, with_code=0):
self.fail_code = with_code self.fail_code = with_code
with self.mxDB: with self.mxDB:
self.chainstate_delay_event.set() self.chainstate_delay_event.set()
self.delay_event.set() self.delay_event.set()
def openLogFile(self):
self.fp = open(os.path.join(self.data_dir, "basicswap.log"), "a")
def prepareLogging(self): def prepareLogging(self):
logging.setLoggerClass(BSXLogger) logging.setLoggerClass(BSXLogger)
self.log = logging.getLogger(self.log_name) self.log = logging.getLogger(self.log_name)
self.log.propagate = False self.log.propagate = False
self.openLogFile()
# Remove any existing handlers # Remove any existing handlers
self.log.handlers = [] self.log.handlers = []
formatter = logging.Formatter( formatter = logging.Formatter(
"%(asctime)s %(levelname)s : %(message)s", "%Y-%m-%d %H:%M:%S" "%(asctime)s %(levelname)s : %(message)s", "%Y-%m-%d %H:%M:%S"
) )
stream_stdout = logging.StreamHandler() stream_stdout = logging.StreamHandler(sys.stdout)
if self.log_name != "BasicSwap": if self.log_name != "BasicSwap":
stream_stdout.setFormatter( stream_stdout.setFormatter(
logging.Formatter( logging.Formatter(
@@ -98,6 +108,7 @@ class BaseApp(DBMethods):
) )
else: else:
stream_stdout.setFormatter(formatter) stream_stdout.setFormatter(formatter)
self.log_formatter = formatter
stream_fp = logging.StreamHandler(self.fp) stream_fp = logging.StreamHandler(self.fp)
stream_fp.setFormatter(formatter) stream_fp.setFormatter(formatter)

View File

@@ -11,6 +11,7 @@ import concurrent.futures
import copy import copy
import datetime as dt import datetime as dt
import json import json
import logging
import os import os
import random import random
import secrets import secrets
@@ -281,14 +282,13 @@ class BasicSwap(BaseApp):
def __init__( def __init__(
self, self,
fp,
data_dir, data_dir,
settings, settings,
chain, chain,
log_name="BasicSwap", log_name="BasicSwap",
transient_instance=False, transient_instance=False,
): ):
super().__init__(fp, data_dir, settings, chain, log_name) super().__init__(data_dir, settings, chain, log_name)
v = __version__.split(".") v = __version__.split(".")
self._version = struct.pack(">HHH", int(v[0]), int(v[1]), int(v[2])) self._version = struct.pack(">HHH", int(v[0]), int(v[1]), int(v[2]))
@@ -352,6 +352,13 @@ class BasicSwap(BaseApp):
self._expire_db_records_after = self.get_int_setting( self._expire_db_records_after = self.get_int_setting(
"expire_db_records_after", 7 * 86400, 0, 31 * 86400 "expire_db_records_after", 7 * 86400, 0, 31 * 86400
) # Seconds ) # Seconds
self._max_logfile_bytes = self.settings.get(
"max_logfile_size", 100
) # In MB 0 to disable truncation
if self._max_logfile_bytes > 0:
self._max_logfile_bytes *= 1024 * 1024
self._max_logfiles = self.get_int_setting("max_logfiles", 10, 1, 100)
self._notifications_cache = {} self._notifications_cache = {}
self._is_encrypted = None self._is_encrypted = None
self._is_locked = None self._is_locked = None
@@ -9724,6 +9731,51 @@ class BasicSwap(BaseApp):
self.checkAcceptedBids() self.checkAcceptedBids()
self._last_checked_expired = now self._last_checked_expired = now
if self._max_logfile_bytes > 0:
logfile_size: int = self.fp.tell()
self.log.debug(f"Log file bytes: {logfile_size}.")
if logfile_size > self._max_logfile_bytes:
for i, log_handler in enumerate(self.log.handlers):
stream_name = getattr(log_handler.stream, "name", "")
if stream_name.endswith(".log"):
del self.log.handlers[i]
break
self.fp.close()
log_path = os.path.join(self.data_dir, "basicswap.log")
if self._max_logfiles == 1:
os.remove(log_path)
else:
last_log = os.path.join(
self.data_dir,
f"basicswap_{self._max_logfiles - 1:0>2}.log",
)
if os.path.exists(last_log):
os.remove(last_log)
for i in range(self._max_logfiles - 2, 0, -1):
path_from = os.path.join(
self.data_dir, f"basicswap_{i:0>2}.log"
)
path_to = os.path.join(
self.data_dir, f"basicswap_{i + 1:0>2}.log"
)
if os.path.exists(path_from):
os.rename(path_from, path_to)
log_path = os.path.join(self.data_dir, "basicswap.log")
os.rename(
log_path,
os.path.join(self.data_dir, "basicswap_01.log"),
)
self.openLogFile()
stream_fp = logging.StreamHandler(self.fp)
stream_fp.setFormatter(self.log_formatter)
self.log.addHandler(stream_fp)
self.log.info("Log file rotated.")
if now - self._last_checked_actions >= self.check_actions_seconds: if now - self._last_checked_actions >= self.check_actions_seconds:
self.checkQueuedActions() self.checkQueuedActions()
self._last_checked_actions = now self._last_checked_actions = now

View File

@@ -1733,11 +1733,8 @@ def test_particl_encryption(data_dir, settings, chain, use_tor_proxy):
swap_client = None swap_client = None
daemons = [] daemons = []
daemon_args = ["-noconnect", "-nodnsseed", "-nofindpeers", "-nostaking"] daemon_args = ["-noconnect", "-nodnsseed", "-nofindpeers", "-nostaking"]
with open(os.path.join(data_dir, "basicswap.log"), "a") as fp:
try: try:
swap_client = BasicSwap( swap_client = BasicSwap(data_dir, settings, chain, transient_instance=True)
fp, data_dir, settings, chain, transient_instance=True
)
if not swap_client.use_tor_proxy: if not swap_client.use_tor_proxy:
# Cannot set -bind or -whitebind together with -listen=0 # Cannot set -bind or -whitebind together with -listen=0
daemon_args.append("-nolisten") daemon_args.append("-nolisten")
@@ -1793,11 +1790,8 @@ def initialise_wallets(
coins_failed_to_initialise = [] coins_failed_to_initialise = []
with open(os.path.join(data_dir, "basicswap.log"), "a") as fp:
try: try:
swap_client = BasicSwap( swap_client = BasicSwap(data_dir, settings, chain, transient_instance=True)
fp, data_dir, settings, chain, transient_instance=True
)
if not swap_client.use_tor_proxy: if not swap_client.use_tor_proxy:
# Cannot set -bind or -whitebind together with -listen=0 # Cannot set -bind or -whitebind together with -listen=0
daemon_args.append("-nolisten") daemon_args.append("-nolisten")
@@ -1822,9 +1816,7 @@ def initialise_wallets(
if c == Coins.XMR: if c == Coins.XMR:
if coin_settings["manage_wallet_daemon"]: if coin_settings["manage_wallet_daemon"]:
filename = ( filename = (
coin_name coin_name + "-wallet-rpc" + (".exe" if os.name == "nt" else "")
+ "-wallet-rpc"
+ (".exe" if os.name == "nt" else "")
) )
filename: str = getWalletBinName( filename: str = getWalletBinName(
c, coin_settings, coin_name + "-wallet-rpc" c, coin_settings, coin_name + "-wallet-rpc"
@@ -1852,9 +1844,7 @@ def initialise_wallets(
pass pass
else: else:
if coin_settings["manage_daemon"]: if coin_settings["manage_daemon"]:
filename: str = getCoreBinName( filename: str = getCoreBinName(c, coin_settings, coin_name + "d")
c, coin_settings, coin_name + "d"
)
coin_args = ( coin_args = (
["-nofindpeers", "-nostaking"] if c == Coins.PART else [] ["-nofindpeers", "-nostaking"] if c == Coins.PART else []
) )
@@ -1916,9 +1906,7 @@ def initialise_wallets(
if c in (Coins.BTC, Coins.LTC, Coins.DOGE, Coins.DASH): if c in (Coins.BTC, Coins.LTC, Coins.DOGE, Coins.DASH):
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors # wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
use_descriptors = coin_settings.get( use_descriptors = coin_settings.get("use_descriptors", False)
"use_descriptors", False
)
swap_client.callcoinrpc( swap_client.callcoinrpc(
c, c,
"createwallet", "createwallet",
@@ -1992,9 +1980,7 @@ def initialise_wallets(
coins_failed_to_initialise.append((c, e)) coins_failed_to_initialise.append((c, e))
if WALLET_ENCRYPTION_PWD != "" and c not in coins_to_create_wallets_for: if WALLET_ENCRYPTION_PWD != "" and c not in coins_to_create_wallets_for:
try: try:
swap_client.ci(c).changeWalletPassword( swap_client.ci(c).changeWalletPassword("", WALLET_ENCRYPTION_PWD)
"", WALLET_ENCRYPTION_PWD
)
except Exception as e: # noqa: F841 except Exception as e: # noqa: F841
logger.warning(f"changeWalletPassword failed for {coin_name}.") logger.warning(f"changeWalletPassword failed for {coin_name}.")

View File

@@ -272,8 +272,8 @@ def getCoreBinArgs(coin_id: int, coin_settings, prepare=False, use_tor_proxy=Fal
def runClient( def runClient(
fp, data_dir: str, chain: str, start_only_coins: bool, log_prefix: str = "BasicSwap" data_dir: str, chain: str, start_only_coins: bool, log_prefix: str = "BasicSwap"
): ) -> int:
global swap_client, logger global swap_client, logger
daemons = [] daemons = []
pids = [] pids = []
@@ -298,7 +298,7 @@ def runClient(
with open(settings_path) as fs: with open(settings_path) as fs:
settings = json.load(fs) settings = json.load(fs)
swap_client = BasicSwap(fp, data_dir, settings, chain, log_name=log_prefix) swap_client = BasicSwap(data_dir, settings, chain, log_name=log_prefix)
logger = swap_client.log logger = swap_client.log
if os.path.exists(pids_path): if os.path.exists(pids_path):
@@ -482,7 +482,6 @@ def runClient(
else cfg.DEFAULT_ALLOW_CORS else cfg.DEFAULT_ALLOW_CORS
) )
thread_http = HttpThread( thread_http = HttpThread(
fp,
settings["htmlhost"], settings["htmlhost"],
settings["htmlport"], settings["htmlport"],
allow_cors, allow_cors,
@@ -548,6 +547,9 @@ def runClient(
except Exception as e: except Exception as e:
swap_client.log.error(f"Error: {e}") swap_client.log.error(f"Error: {e}")
fail_code: int = swap_client.fail_code
del swap_client
if os.path.exists(pids_path): if os.path.exists(pids_path):
with open(pids_path) as fd: with open(pids_path) as fd:
lines = fd.read().split("\n") lines = fd.read().split("\n")
@@ -561,6 +563,8 @@ def runClient(
with open(pids_path, "w") as fd: with open(pids_path, "w") as fd:
fd.write(still_running) fd.write(still_running)
return fail_code
def printVersion(): def printVersion():
logger.info( logger.info(
@@ -642,14 +646,11 @@ def main():
if not os.path.exists(data_dir): if not os.path.exists(data_dir):
os.makedirs(data_dir) os.makedirs(data_dir)
with open(os.path.join(data_dir, "basicswap.log"), "a") as fp: logger.info(os.path.basename(sys.argv[0]) + ", version: " + __version__ + "\n\n")
logger.info( fail_code = runClient(data_dir, chain, start_only_coins, log_prefix)
os.path.basename(sys.argv[0]) + ", version: " + __version__ + "\n\n"
)
runClient(fp, data_dir, chain, start_only_coins, log_prefix)
print("Done.") print("Done.")
return swap_client.fail_code if swap_client is not None else 0 return fail_code
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -637,11 +637,10 @@ class HttpHandler(BaseHTTPRequestHandler):
class HttpThread(threading.Thread, HTTPServer): class HttpThread(threading.Thread, HTTPServer):
def __init__(self, fp, host_name, port_no, allow_cors, swap_client): def __init__(self, host_name, port_no, allow_cors, swap_client):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.stop_event = threading.Event() self.stop_event = threading.Event()
self.fp = fp
self.host_name = host_name self.host_name = host_name
self.port_no = port_no self.port_no = port_no
self.allow_cors = allow_cors self.allow_cors = allow_cors

View File

@@ -408,9 +408,8 @@ class Test(unittest.TestCase):
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
with open(settings_path) as fs: with open(settings_path) as fs:
settings = json.load(fs) settings = json.load(fs)
fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
sc = BasicSwap( sc = BasicSwap(
fp, basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i) basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i)
) )
cls.swap_clients.append(sc) cls.swap_clients.append(sc)
sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid) sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
@@ -423,7 +422,7 @@ class Test(unittest.TestCase):
sc.start() sc.start()
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc) t = HttpThread(TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
cls.http_threads.append(t) cls.http_threads.append(t)
t.start() t.start()
@@ -484,7 +483,6 @@ class Test(unittest.TestCase):
t.join() t.join()
for c in cls.swap_clients: for c in cls.swap_clients:
c.finalise() c.finalise()
c.fp.close()
stopDaemons(cls.daemons) stopDaemons(cls.daemons)

View File

@@ -325,9 +325,7 @@ class Test(unittest.TestCase):
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
with open(settings_path) as fs: with open(settings_path) as fs:
settings = json.load(fs) settings = json.load(fs)
fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
sc = BasicSwap( sc = BasicSwap(
fp,
basicswap_dir, basicswap_dir,
settings, settings,
"regtest", "regtest",
@@ -338,7 +336,7 @@ class Test(unittest.TestCase):
sc.setDaemonPID(Coins.PART, cls.part_daemons[i].handle.pid) sc.setDaemonPID(Coins.PART, cls.part_daemons[i].handle.pid)
sc.start() sc.start()
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc) t = HttpThread(TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
cls.http_threads.append(t) cls.http_threads.append(t)
t.start() t.start()
@@ -397,7 +395,6 @@ class Test(unittest.TestCase):
t.join() t.join()
for c in cls.swap_clients: for c in cls.swap_clients:
c.finalise() c.finalise()
c.fp.close()
stopDaemons(cls.part_daemons) stopDaemons(cls.part_daemons)
stopDaemons(cls.btc_daemons) stopDaemons(cls.btc_daemons)

View File

@@ -365,9 +365,8 @@ class Test(unittest.TestCase):
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
with open(settings_path) as fs: with open(settings_path) as fs:
settings = json.load(fs) settings = json.load(fs)
fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
sc = BasicSwap( sc = BasicSwap(
fp, basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i) basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i)
) )
cls.swap_clients.append(sc) cls.swap_clients.append(sc)
@@ -376,7 +375,7 @@ class Test(unittest.TestCase):
sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid) sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
sc.start() sc.start()
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc) t = HttpThread(TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
cls.http_threads.append(t) cls.http_threads.append(t)
t.start() t.start()
@@ -437,7 +436,6 @@ class Test(unittest.TestCase):
t.join() t.join()
for c in cls.swap_clients: for c in cls.swap_clients:
c.finalise() c.finalise()
c.fp.close()
stopDaemons(cls.daemons) stopDaemons(cls.daemons)
cls.http_threads.clear() cls.http_threads.clear()

View File

@@ -414,9 +414,8 @@ class Test(unittest.TestCase):
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
with open(settings_path) as fs: with open(settings_path) as fs:
settings = json.load(fs) settings = json.load(fs)
fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
sc = BasicSwap( sc = BasicSwap(
fp, basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i) basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i)
) )
cls.swap_clients.append(sc) cls.swap_clients.append(sc)
sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid) sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
@@ -424,7 +423,7 @@ class Test(unittest.TestCase):
sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid) sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
sc.start() sc.start()
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc) t = HttpThread(TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
cls.http_threads.append(t) cls.http_threads.append(t)
t.start() t.start()
@@ -482,7 +481,6 @@ class Test(unittest.TestCase):
t.join() t.join()
for c in cls.swap_clients: for c in cls.swap_clients:
c.finalise() c.finalise()
c.fp.close()
stopDaemons(cls.daemons) stopDaemons(cls.daemons)
cls.http_threads.clear() cls.http_threads.clear()

View File

@@ -654,9 +654,7 @@ class BaseTest(unittest.TestCase):
if cls.restore_instance and i == 1: if cls.restore_instance and i == 1:
cls.network_key = settings["network_key"] cls.network_key = settings["network_key"]
cls.network_pubkey = settings["network_pubkey"] cls.network_pubkey = settings["network_pubkey"]
fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
sc = BasicSwap( sc = BasicSwap(
fp,
basicswap_dir, basicswap_dir,
settings, settings,
"regtest", "regtest",
@@ -684,7 +682,7 @@ class BaseTest(unittest.TestCase):
# Import a random seed to keep the existing test behaviour. BTC core rescans even with timestamp: now. # Import a random seed to keep the existing test behaviour. BTC core rescans even with timestamp: now.
sc.ci(Coins.BTC).initialiseWallet(random.randbytes(32)) sc.ci(Coins.BTC).initialiseWallet(random.randbytes(32))
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc) t = HttpThread(TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
cls.http_threads.append(t) cls.http_threads.append(t)
t.start() t.start()
# Set future block rewards to nowhere (a random address), so wallet amounts stay constant # Set future block rewards to nowhere (a random address), so wallet amounts stay constant
@@ -952,7 +950,6 @@ class BaseTest(unittest.TestCase):
logging.info("Stopping swap clients") logging.info("Stopping swap clients")
for c in cls.swap_clients: for c in cls.swap_clients:
c.finalise() c.finalise()
c.fp.close()
logging.info("Stopping coin nodes") logging.info("Stopping coin nodes")
stopDaemons(cls.xmr_daemons) stopDaemons(cls.xmr_daemons)