Log truncation.

This commit is contained in:
tecnovert 2025-03-12 09:24:24 +02:00
parent 7d5f7e0936
commit 2e3d39032a
No known key found for this signature in database
GPG key ID: 8ED6D8750C4E3F93
12 changed files with 403 additions and 276 deletions

View file

@ -5,17 +5,18 @@
# Distributed under the MIT software license, see the accompanying
# 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 threading
import traceback
import os
import random
import shlex
import socket
import socks
import subprocess
import sys
import threading
import time
import traceback
import urllib
from sockshandler import SocksiPyHandler
@ -42,9 +43,9 @@ def getaddrinfo_tor(*args):
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.fp = fp
self.fail_code = 0
self.mock_time_offset = 0
@ -71,24 +72,33 @@ class BaseApp(DBMethods):
self.default_socket_timeout = socket.getdefaulttimeout()
self.default_socket_getaddrinfo = socket.getaddrinfo
def __del__(self):
if self.fp:
self.fp.close()
def stopRunning(self, with_code=0):
self.fail_code = with_code
with self.mxDB:
self.chainstate_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):
logging.setLoggerClass(BSXLogger)
self.log = logging.getLogger(self.log_name)
self.log.propagate = False
self.openLogFile()
# Remove any existing handlers
self.log.handlers = []
formatter = logging.Formatter(
"%(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":
stream_stdout.setFormatter(
logging.Formatter(
@ -98,6 +108,7 @@ class BaseApp(DBMethods):
)
else:
stream_stdout.setFormatter(formatter)
self.log_formatter = formatter
stream_fp = logging.StreamHandler(self.fp)
stream_fp.setFormatter(formatter)

View file

@ -11,6 +11,7 @@ import concurrent.futures
import copy
import datetime as dt
import json
import logging
import os
import random
import secrets
@ -61,6 +62,7 @@ from .util.address import (
from .util.crypto import (
sha256,
)
from basicswap.util.logging import trimLogFile
from basicswap.util.network import is_private_ip_address
from .chainparams import (
Coins,
@ -281,14 +283,13 @@ class BasicSwap(BaseApp):
def __init__(
self,
fp,
data_dir,
settings,
chain,
log_name="BasicSwap",
transient_instance=False,
):
super().__init__(fp, data_dir, settings, chain, log_name)
super().__init__(data_dir, settings, chain, log_name)
v = __version__.split(".")
self._version = struct.pack(">HHH", int(v[0]), int(v[1]), int(v[2]))
@ -352,6 +353,12 @@ class BasicSwap(BaseApp):
self._expire_db_records_after = self.get_int_setting(
"expire_db_records_after", 7 * 86400, 0, 31 * 86400
) # 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._notifications_cache = {}
self._is_encrypted = None
self._is_locked = None
@ -9724,6 +9731,30 @@ class BasicSwap(BaseApp):
self.checkAcceptedBids()
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()
trimLogFile(
os.path.join(self.data_dir, "basicswap.log"),
self._max_logfile_bytes,
)
self.openLogFile()
stream_fp = logging.StreamHandler(self.fp)
stream_fp.setFormatter(self.log_formatter)
self.log.addHandler(stream_fp)
self.log.info("Log file truncated.")
if now - self._last_checked_actions >= self.check_actions_seconds:
self.checkQueuedActions()
self._last_checked_actions = now

View file

@ -1733,48 +1733,45 @@ def test_particl_encryption(data_dir, settings, chain, use_tor_proxy):
swap_client = None
daemons = []
daemon_args = ["-noconnect", "-nodnsseed", "-nofindpeers", "-nostaking"]
with open(os.path.join(data_dir, "basicswap.log"), "a") as fp:
try:
swap_client = BasicSwap(
fp, data_dir, settings, chain, transient_instance=True
)
if not swap_client.use_tor_proxy:
# Cannot set -bind or -whitebind together with -listen=0
daemon_args.append("-nolisten")
c = Coins.PART
coin_name = "particl"
coin_settings = settings["chainclients"][coin_name]
daemon_args += getCoreBinArgs(c, coin_settings, prepare=True)
extra_config = {"stdout_to_file": True}
if coin_settings["manage_daemon"]:
filename: str = getCoreBinName(c, coin_settings, coin_name + "d")
daemons.append(
startDaemon(
coin_settings["datadir"],
coin_settings["bindir"],
filename,
daemon_args,
extra_config=extra_config,
)
try:
swap_client = BasicSwap(data_dir, settings, chain, transient_instance=True)
if not swap_client.use_tor_proxy:
# Cannot set -bind or -whitebind together with -listen=0
daemon_args.append("-nolisten")
c = Coins.PART
coin_name = "particl"
coin_settings = settings["chainclients"][coin_name]
daemon_args += getCoreBinArgs(c, coin_settings, prepare=True)
extra_config = {"stdout_to_file": True}
if coin_settings["manage_daemon"]:
filename: str = getCoreBinName(c, coin_settings, coin_name + "d")
daemons.append(
startDaemon(
coin_settings["datadir"],
coin_settings["bindir"],
filename,
daemon_args,
extra_config=extra_config,
)
swap_client.setDaemonPID(c, daemons[-1].handle.pid)
swap_client.setCoinRunParams(c)
swap_client.createCoinInterface(c)
swap_client.waitForDaemonRPC(c, with_wallet=True)
)
swap_client.setDaemonPID(c, daemons[-1].handle.pid)
swap_client.setCoinRunParams(c)
swap_client.createCoinInterface(c)
swap_client.waitForDaemonRPC(c, with_wallet=True)
if swap_client.ci(c).isWalletEncrypted():
logger.info("Particl Wallet is encrypted")
if WALLET_ENCRYPTION_PWD == "":
raise ValueError(
"Must set WALLET_ENCRYPTION_PWD to add coin when Particl wallet is encrypted"
)
swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
finally:
if swap_client:
swap_client.finalise()
del swap_client
for d in daemons:
finalise_daemon(d)
if swap_client.ci(c).isWalletEncrypted():
logger.info("Particl Wallet is encrypted")
if WALLET_ENCRYPTION_PWD == "":
raise ValueError(
"Must set WALLET_ENCRYPTION_PWD to add coin when Particl wallet is encrypted"
)
swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
finally:
if swap_client:
swap_client.finalise()
del swap_client
for d in daemons:
finalise_daemon(d)
def encrypt_wallet(swap_client, coin_type) -> None:
@ -1793,217 +1790,206 @@ def initialise_wallets(
coins_failed_to_initialise = []
with open(os.path.join(data_dir, "basicswap.log"), "a") as fp:
try:
swap_client = BasicSwap(
fp, data_dir, settings, chain, transient_instance=True
)
if not swap_client.use_tor_proxy:
# Cannot set -bind or -whitebind together with -listen=0
daemon_args.append("-nolisten")
coins_to_create_wallets_for = (
Coins.PART,
Coins.BTC,
Coins.LTC,
Coins.DOGE,
Coins.DCR,
Coins.DASH,
)
# Always start Particl, it must be running to initialise a wallet in addcoin mode
# Particl must be loaded first as subsequent coins are initialised from the Particl mnemonic
start_daemons = [
"particl",
] + [c for c in with_coins if c != "particl"]
for coin_name in start_daemons:
coin_settings = settings["chainclients"][coin_name]
wallet_name = coin_settings.get("wallet_name", "wallet.dat")
c = swap_client.getCoinIdFromName(coin_name)
try:
swap_client = BasicSwap(data_dir, settings, chain, transient_instance=True)
if not swap_client.use_tor_proxy:
# Cannot set -bind or -whitebind together with -listen=0
daemon_args.append("-nolisten")
coins_to_create_wallets_for = (
Coins.PART,
Coins.BTC,
Coins.LTC,
Coins.DOGE,
Coins.DCR,
Coins.DASH,
)
# Always start Particl, it must be running to initialise a wallet in addcoin mode
# Particl must be loaded first as subsequent coins are initialised from the Particl mnemonic
start_daemons = [
"particl",
] + [c for c in with_coins if c != "particl"]
for coin_name in start_daemons:
coin_settings = settings["chainclients"][coin_name]
wallet_name = coin_settings.get("wallet_name", "wallet.dat")
c = swap_client.getCoinIdFromName(coin_name)
if c == Coins.XMR:
if coin_settings["manage_wallet_daemon"]:
filename = (
coin_name
+ "-wallet-rpc"
+ (".exe" if os.name == "nt" else "")
if c == Coins.XMR:
if coin_settings["manage_wallet_daemon"]:
filename = (
coin_name + "-wallet-rpc" + (".exe" if os.name == "nt" else "")
)
filename: str = getWalletBinName(
c, coin_settings, coin_name + "-wallet-rpc"
)
daemons.append(
startXmrWalletDaemon(
coin_settings["datadir"],
coin_settings["bindir"],
filename,
)
filename: str = getWalletBinName(
c, coin_settings, coin_name + "-wallet-rpc"
)
elif c == Coins.WOW:
if coin_settings["manage_wallet_daemon"]:
filename: str = getWalletBinName(
c, coin_settings, coin_name + "-wallet-rpc"
)
daemons.append(
startXmrWalletDaemon(
coin_settings["datadir"],
coin_settings["bindir"],
filename,
)
daemons.append(
startXmrWalletDaemon(
coin_settings["datadir"],
coin_settings["bindir"],
filename,
)
elif c == Coins.DCR:
pass
else:
if coin_settings["manage_daemon"]:
filename: str = getCoreBinName(c, coin_settings, coin_name + "d")
coin_args = (
["-nofindpeers", "-nostaking"] if c == Coins.PART else []
)
coin_args += getCoreBinArgs(c, coin_settings, prepare=True)
if c == Coins.FIRO:
coin_args += [
"-hdseed={}".format(
swap_client.getWalletKey(Coins.FIRO, 1).hex()
)
)
elif c == Coins.WOW:
if coin_settings["manage_wallet_daemon"]:
filename: str = getWalletBinName(
c, coin_settings, coin_name + "-wallet-rpc"
)
daemons.append(
startXmrWalletDaemon(
coin_settings["datadir"],
coin_settings["bindir"],
filename,
)
)
elif c == Coins.DCR:
pass
else:
if coin_settings["manage_daemon"]:
filename: str = getCoreBinName(
c, coin_settings, coin_name + "d"
)
coin_args = (
["-nofindpeers", "-nostaking"] if c == Coins.PART else []
)
coin_args += getCoreBinArgs(c, coin_settings, prepare=True)
if c == Coins.FIRO:
coin_args += [
"-hdseed={}".format(
swap_client.getWalletKey(Coins.FIRO, 1).hex()
)
]
extra_config = {"stdout_to_file": True}
daemons.append(
startDaemon(
coin_settings["datadir"],
coin_settings["bindir"],
filename,
daemon_args + coin_args,
extra_config=extra_config,
)
)
swap_client.setDaemonPID(c, daemons[-1].handle.pid)
swap_client.setCoinRunParams(c)
swap_client.createCoinInterface(c)
if c in coins_to_create_wallets_for:
if c == Coins.DCR:
if coin_settings["manage_wallet_daemon"] is False:
continue
from basicswap.interface.dcr.util import createDCRWallet
dcr_password = (
coin_settings["wallet_pwd"]
if WALLET_ENCRYPTION_PWD == ""
else WALLET_ENCRYPTION_PWD
)
extra_opts = [
'--appdata="{}"'.format(coin_settings["datadir"]),
"--pass={}".format(dcr_password),
]
filename: str = getWalletBinName(c, coin_settings, "dcrwallet")
args = [
os.path.join(coin_settings["bindir"], filename),
"--create",
] + extra_opts
hex_seed = swap_client.getWalletKey(Coins.DCR, 1).hex()
createDCRWallet(args, hex_seed, logger, threading.Event())
continue
swap_client.waitForDaemonRPC(c, with_wallet=False)
# Create wallet if it doesn't exist yet
wallets = swap_client.callcoinrpc(c, "listwallets")
if wallet_name not in wallets:
logger.info(
f'Creating wallet "{wallet_name}" for {getCoinName(c)}.'
extra_config = {"stdout_to_file": True}
daemons.append(
startDaemon(
coin_settings["datadir"],
coin_settings["bindir"],
filename,
daemon_args + coin_args,
extra_config=extra_config,
)
)
swap_client.setDaemonPID(c, daemons[-1].handle.pid)
swap_client.setCoinRunParams(c)
swap_client.createCoinInterface(c)
if c in (Coins.BTC, Coins.LTC, Coins.DOGE, Coins.DASH):
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
if c in coins_to_create_wallets_for:
if c == Coins.DCR:
if coin_settings["manage_wallet_daemon"] is False:
continue
from basicswap.interface.dcr.util import createDCRWallet
use_descriptors = coin_settings.get(
"use_descriptors", False
)
dcr_password = (
coin_settings["wallet_pwd"]
if WALLET_ENCRYPTION_PWD == ""
else WALLET_ENCRYPTION_PWD
)
extra_opts = [
'--appdata="{}"'.format(coin_settings["datadir"]),
"--pass={}".format(dcr_password),
]
filename: str = getWalletBinName(c, coin_settings, "dcrwallet")
args = [
os.path.join(coin_settings["bindir"], filename),
"--create",
] + extra_opts
hex_seed = swap_client.getWalletKey(Coins.DCR, 1).hex()
createDCRWallet(args, hex_seed, logger, threading.Event())
continue
swap_client.waitForDaemonRPC(c, with_wallet=False)
# Create wallet if it doesn't exist yet
wallets = swap_client.callcoinrpc(c, "listwallets")
if wallet_name not in wallets:
logger.info(
f'Creating wallet "{wallet_name}" for {getCoinName(c)}.'
)
if c in (Coins.BTC, Coins.LTC, Coins.DOGE, Coins.DASH):
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
use_descriptors = coin_settings.get("use_descriptors", False)
swap_client.callcoinrpc(
c,
"createwallet",
[
wallet_name,
False,
True,
WALLET_ENCRYPTION_PWD,
False,
use_descriptors,
],
)
if use_descriptors:
swap_client.callcoinrpc(
c,
"createwallet",
[
wallet_name,
False,
coin_settings["watch_wallet_name"],
True,
WALLET_ENCRYPTION_PWD,
True,
"",
False,
use_descriptors,
],
)
if use_descriptors:
swap_client.callcoinrpc(
c,
"createwallet",
[
coin_settings["watch_wallet_name"],
True,
True,
"",
False,
use_descriptors,
],
)
swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
else:
swap_client.callcoinrpc(
c,
"createwallet",
[
wallet_name,
],
)
if WALLET_ENCRYPTION_PWD != "":
encrypt_wallet(swap_client, c)
if c == Coins.LTC:
password = (
WALLET_ENCRYPTION_PWD
if WALLET_ENCRYPTION_PWD != ""
else None
)
swap_client.ci(Coins.LTC_MWEB).init_wallet(password)
if c == Coins.PART:
if "particl" in with_coins:
logger.info("Loading Particl mnemonic")
if particl_wallet_mnemonic is None:
particl_wallet_mnemonic = swap_client.callcoinrpc(
Coins.PART, "mnemonic", ["new"]
)["mnemonic"]
generated_mnemonic = True
swap_client.callcoinrpc(
Coins.PART, "extkeyimportmaster", [particl_wallet_mnemonic]
)
# Particl wallet must be unlocked to call getWalletKey
if WALLET_ENCRYPTION_PWD != "":
swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
for coin_name in with_coins:
c = swap_client.getCoinIdFromName(coin_name)
if c in (Coins.PART,):
continue
if c not in (Coins.DCR,):
# initialiseWallet only sets main_wallet_seedid_
swap_client.waitForDaemonRPC(c)
try:
swap_client.initialiseWallet(c, raise_errors=True)
except Exception as e:
coins_failed_to_initialise.append((c, e))
if WALLET_ENCRYPTION_PWD != "" and c not in coins_to_create_wallets_for:
try:
swap_client.ci(c).changeWalletPassword(
"", WALLET_ENCRYPTION_PWD
else:
swap_client.callcoinrpc(
c,
"createwallet",
[
wallet_name,
],
)
except Exception as e: # noqa: F841
logger.warning(f"changeWalletPassword failed for {coin_name}.")
if WALLET_ENCRYPTION_PWD != "":
encrypt_wallet(swap_client, c)
finally:
if swap_client:
swap_client.finalise()
del swap_client
for d in daemons:
finalise_daemon(d)
if c == Coins.LTC:
password = (
WALLET_ENCRYPTION_PWD
if WALLET_ENCRYPTION_PWD != ""
else None
)
swap_client.ci(Coins.LTC_MWEB).init_wallet(password)
if c == Coins.PART:
if "particl" in with_coins:
logger.info("Loading Particl mnemonic")
if particl_wallet_mnemonic is None:
particl_wallet_mnemonic = swap_client.callcoinrpc(
Coins.PART, "mnemonic", ["new"]
)["mnemonic"]
generated_mnemonic = True
swap_client.callcoinrpc(
Coins.PART, "extkeyimportmaster", [particl_wallet_mnemonic]
)
# Particl wallet must be unlocked to call getWalletKey
if WALLET_ENCRYPTION_PWD != "":
swap_client.ci(c).unlockWallet(WALLET_ENCRYPTION_PWD)
for coin_name in with_coins:
c = swap_client.getCoinIdFromName(coin_name)
if c in (Coins.PART,):
continue
if c not in (Coins.DCR,):
# initialiseWallet only sets main_wallet_seedid_
swap_client.waitForDaemonRPC(c)
try:
swap_client.initialiseWallet(c, raise_errors=True)
except Exception as e:
coins_failed_to_initialise.append((c, e))
if WALLET_ENCRYPTION_PWD != "" and c not in coins_to_create_wallets_for:
try:
swap_client.ci(c).changeWalletPassword("", WALLET_ENCRYPTION_PWD)
except Exception as e: # noqa: F841
logger.warning(f"changeWalletPassword failed for {coin_name}.")
finally:
if swap_client:
swap_client.finalise()
del swap_client
for d in daemons:
finalise_daemon(d)
print("")
for pair in coins_failed_to_initialise:

View file

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

View file

@ -637,11 +637,10 @@ class HttpHandler(BaseHTTPRequestHandler):
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)
self.stop_event = threading.Event()
self.fp = fp
self.host_name = host_name
self.port_no = port_no
self.allow_cors = allow_cors

View file

@ -5,6 +5,7 @@
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import logging
import os
from basicswap.util.crypto import (
sha256,
)
@ -40,3 +41,58 @@ class BSXLogger(logging.Logger):
def info_s(self, msg, *args, **kwargs):
if self.safe_logs is False:
self.info(msg, *args, **kwargs)
def trimOpenLogFile(fp, max_bytes: int, add_trim_bytes: int = -1) -> bool:
if add_trim_bytes < 0:
# Set 1/4th of the total size by default
add_trim_bytes = max_bytes // 4
fp.seek(0, os.SEEK_END)
end_pos: int = fp.tell()
keep_bytes: int = max_bytes - add_trim_bytes
if end_pos <= keep_bytes:
return False
if keep_bytes <= 0:
fp.seek(0)
fp.write("... File truncated.\n")
fp.truncate()
return True
fp.seek(end_pos - keep_bytes)
readahead_bytes = min(end_pos - keep_bytes, 4096)
bytes_ahead = fp.read(readahead_bytes)
# Find next newline
for b in bytes_ahead:
keep_bytes -= 1
if b == "\n":
break
fp.seek(0)
fp.write("... File truncated.\n")
write_pos: int = fp.tell()
bytes_moved: int = 0
while bytes_moved < keep_bytes:
chunk_size: int = min(end_pos - bytes_moved, 8096)
fp.seek(end_pos - (keep_bytes - bytes_moved))
data_chunk = fp.read(chunk_size)
fp.seek(write_pos)
fp.write(data_chunk)
write_pos += chunk_size
bytes_moved += chunk_size
fp.truncate()
return True
def trimLogFile(filepath, max_bytes: int, add_trim_bytes: int = -1) -> bool:
if os.path.getsize(filepath) <= max_bytes:
return False
with open(filepath, "r+") as fp:
return trimOpenLogFile(fp, max_bytes, add_trim_bytes)

View file

@ -408,9 +408,8 @@ class Test(unittest.TestCase):
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
with open(settings_path) as fs:
settings = json.load(fs)
fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
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)
sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
@ -423,7 +422,7 @@ class Test(unittest.TestCase):
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)
t.start()
@ -484,7 +483,6 @@ class Test(unittest.TestCase):
t.join()
for c in cls.swap_clients:
c.finalise()
c.fp.close()
stopDaemons(cls.daemons)

View file

@ -0,0 +1,55 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) 2025 The Basicswap developers
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import os
import shutil
import unittest
from basicswap.util.logging import trimLogFile
class Test(unittest.TestCase):
def test_log_truncation(self):
test_dir: str = "/tmp/bsx_test"
if not os.path.isdir(test_dir):
os.makedirs(test_dir)
file_path: str = os.path.join(test_dir, "large.log")
with open(file_path, "w") as fp:
for i in range(1000000):
fp.write(f"Basicswap test log line {i}\n")
start_size = os.path.getsize(file_path)
file_path2: str = os.path.join(test_dir, "test2.log")
shutil.copyfile(file_path, file_path2)
trimLogFile(file_path2, 0)
file_path2_size = os.path.getsize(file_path2)
assert file_path2_size == 20
file_path3: str = os.path.join(test_dir, "test3.log")
shutil.copyfile(file_path, file_path3)
trimLogFile(file_path3, 70000)
file_path3_size = os.path.getsize(file_path3)
assert file_path3_size == 52503
file_path4: str = os.path.join(test_dir, "test4.log")
shutil.copyfile(file_path, file_path4)
trimLogFile(file_path4, 70000, 0)
file_path4_size = os.path.getsize(file_path4)
assert file_path4_size == 70018 # Extra bytes for truncated message
file_path5: str = os.path.join(test_dir, "test5.log")
shutil.copyfile(file_path, file_path5)
trimLogFile(file_path5, start_size - 7000)
file_path5_size = os.path.getsize(file_path5)
assert file_path5_size == 23161422 # ~1/4 of total size less
if __name__ == "__main__":
unittest.main()

View file

@ -325,9 +325,7 @@ class Test(unittest.TestCase):
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
with open(settings_path) as fs:
settings = json.load(fs)
fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
sc = BasicSwap(
fp,
basicswap_dir,
settings,
"regtest",
@ -338,7 +336,7 @@ class Test(unittest.TestCase):
sc.setDaemonPID(Coins.PART, cls.part_daemons[i].handle.pid)
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)
t.start()
@ -397,7 +395,6 @@ class Test(unittest.TestCase):
t.join()
for c in cls.swap_clients:
c.finalise()
c.fp.close()
stopDaemons(cls.part_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)
with open(settings_path) as fs:
settings = json.load(fs)
fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
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)
@ -376,7 +375,7 @@ class Test(unittest.TestCase):
sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
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)
t.start()
@ -437,7 +436,6 @@ class Test(unittest.TestCase):
t.join()
for c in cls.swap_clients:
c.finalise()
c.fp.close()
stopDaemons(cls.daemons)
cls.http_threads.clear()

View file

@ -414,9 +414,8 @@ class Test(unittest.TestCase):
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
with open(settings_path) as fs:
settings = json.load(fs)
fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
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)
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.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)
t.start()
@ -482,7 +481,6 @@ class Test(unittest.TestCase):
t.join()
for c in cls.swap_clients:
c.finalise()
c.fp.close()
stopDaemons(cls.daemons)
cls.http_threads.clear()

View file

@ -654,9 +654,7 @@ class BaseTest(unittest.TestCase):
if cls.restore_instance and i == 1:
cls.network_key = settings["network_key"]
cls.network_pubkey = settings["network_pubkey"]
fp = open(os.path.join(basicswap_dir, "basicswap.log"), "w")
sc = BasicSwap(
fp,
basicswap_dir,
settings,
"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.
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)
t.start()
# 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")
for c in cls.swap_clients:
c.finalise()
c.fp.close()
logging.info("Stopping coin nodes")
stopDaemons(cls.xmr_daemons)