add logfile
This commit is contained in:
parent
df44bc7bf1
commit
69582fa16e
31
README.md
31
README.md
@ -74,26 +74,29 @@ chkbit will
|
|||||||
Run `chkbit PATH` to verify only.
|
Run `chkbit PATH` to verify only.
|
||||||
|
|
||||||
```
|
```
|
||||||
usage: chkbit [-h] [-u] [--show-ignored-only] [--algo ALGO] [-f] [-s] [--index-name NAME] [--ignore-name NAME] [-w N] [--plain] [-q] [-v] [PATH ...]
|
usage: chkbit [-h] [-u] [--show-ignored-only] [--algo ALGO] [-f] [-s] [-l FILE] [--log-verbose] [--index-name NAME] [--ignore-name NAME] [-w N] [--plain] [-q] [-v] [PATH ...]
|
||||||
|
|
||||||
Checks the data integrity of your files. See https://github.com/laktak/chkbit-py
|
Checks the data integrity of your files. See https://github.com/laktak/chkbit-py
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
PATH directories to check
|
PATH directories to check
|
||||||
|
|
||||||
options:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
-u, --update update indices (without this chkbit will verify files in readonly mode)
|
-u, --update update indices (without this chkbit will verify files in readonly mode)
|
||||||
--show-ignored-only only show ignored files
|
--show-ignored-only only show ignored files
|
||||||
--algo ALGO hash algorithm: md5, sha512, blake3 (default: blake3)
|
--algo ALGO hash algorithm: md5, sha512, blake3 (default: blake3)
|
||||||
-f, --force force update of damaged items
|
-f, --force force update of damaged items
|
||||||
-s, --skip-symlinks do not follow symlinks
|
-s, --skip-symlinks do not follow symlinks
|
||||||
--index-name NAME filename where chkbit stores its hashes (default: .chkbit)
|
-l FILE, --log-file FILE
|
||||||
--ignore-name NAME filename that chkbit reads its ignore list from (default: .chkbitignore)
|
write to a logfile if specified
|
||||||
-w N, --workers N number of workers to use (default: 5)
|
--log-verbose verbose logging
|
||||||
--plain show plain status instead of being fancy
|
--index-name NAME filename where chkbit stores its hashes (default: .chkbit)
|
||||||
-q, --quiet quiet, don't show progress/information
|
--ignore-name NAME filename that chkbit reads its ignore list from (default: .chkbitignore)
|
||||||
-v, --verbose verbose output
|
-w N, --workers N number of workers to use (default: 5)
|
||||||
|
--plain show plain status instead of being fancy
|
||||||
|
-q, --quiet quiet, don't show progress/information
|
||||||
|
-v, --verbose verbose output
|
||||||
|
|
||||||
.chkbitignore rules:
|
.chkbitignore rules:
|
||||||
each line should contain exactly one name
|
each line should contain exactly one name
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
from __future__ import annotations
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
class Status(Enum):
|
class Status(Enum):
|
||||||
@ -11,3 +13,16 @@ class Status(Enum):
|
|||||||
IGNORE = "ign"
|
IGNORE = "ign"
|
||||||
INTERNALEXCEPTION = "EXC"
|
INTERNALEXCEPTION = "EXC"
|
||||||
UPDATE_INDEX = "iup"
|
UPDATE_INDEX = "iup"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_level(status: Status):
|
||||||
|
if status == Status.INTERNALEXCEPTION:
|
||||||
|
return logging.CRITICAL
|
||||||
|
elif status in [Status.ERR_DMG, Status.ERR_IDX]:
|
||||||
|
return logging.ERROR
|
||||||
|
if status == Status.WARN_OLD:
|
||||||
|
return logging.WARNING
|
||||||
|
elif status in [Status.NEW, Status.UPDATE, Status.OK, Status.IGNORE]:
|
||||||
|
return logging.INFO
|
||||||
|
else:
|
||||||
|
return logging.DEBUG
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import argparse
|
import argparse
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import queue
|
import queue
|
||||||
import shutil
|
import shutil
|
||||||
@ -50,12 +51,16 @@ class Main:
|
|||||||
self.num_new = 0
|
self.num_new = 0
|
||||||
self.num_upd = 0
|
self.num_upd = 0
|
||||||
self.verbose = False
|
self.verbose = False
|
||||||
|
self.log = logging.getLogger("")
|
||||||
|
self.log_verbose = False
|
||||||
self.progress = Progress.Fancy
|
self.progress = Progress.Fancy
|
||||||
self.total = 0
|
self.total = 0
|
||||||
self.term_width = shutil.get_terminal_size()[0]
|
self.term_width = shutil.get_terminal_size()[0]
|
||||||
max_stat = int((self.term_width - 70) / 2)
|
max_stat = int((self.term_width - 70) / 2)
|
||||||
self.fps = RateCalc(timedelta(seconds=1), max_stat=max_stat)
|
self.fps = RateCalc(timedelta(seconds=1), max_stat=max_stat)
|
||||||
self.bps = RateCalc(timedelta(seconds=1), max_stat=max_stat)
|
self.bps = RateCalc(timedelta(seconds=1), max_stat=max_stat)
|
||||||
|
# disable
|
||||||
|
self.log.setLevel(logging.CRITICAL + 1)
|
||||||
|
|
||||||
def _log(self, stat: Status, path: str):
|
def _log(self, stat: Status, path: str):
|
||||||
if stat == Status.UPDATE_INDEX:
|
if stat == Status.UPDATE_INDEX:
|
||||||
@ -73,8 +78,18 @@ class Main:
|
|||||||
elif stat == Status.NEW:
|
elif stat == Status.NEW:
|
||||||
self.num_new += 1
|
self.num_new += 1
|
||||||
|
|
||||||
|
lvl = Status.get_level(stat)
|
||||||
|
if self.log_verbose or not stat in [Status.OK, Status.IGNORE]:
|
||||||
|
self.log.log(lvl, f"{stat.value} {path}")
|
||||||
|
|
||||||
if self.verbose or not stat in [Status.OK, Status.IGNORE]:
|
if self.verbose or not stat in [Status.OK, Status.IGNORE]:
|
||||||
CLI.printline(stat.value, " ", path)
|
CLI.printline(
|
||||||
|
CLI_ALERT_FG if lvl >= logging.WARNING else "",
|
||||||
|
stat.value,
|
||||||
|
" ",
|
||||||
|
path,
|
||||||
|
CLI.style.reset,
|
||||||
|
)
|
||||||
|
|
||||||
def _res_worker(self, context: Context):
|
def _res_worker(self, context: Context):
|
||||||
last = datetime.now()
|
last = datetime.now()
|
||||||
@ -182,10 +197,9 @@ class Main:
|
|||||||
iunit2 = lambda x, u1, u2: f"{x} {u2 if x!=1 else u1}"
|
iunit2 = lambda x, u1, u2: f"{x} {u2 if x!=1 else u1}"
|
||||||
|
|
||||||
if self.progress != Progress.Quiet:
|
if self.progress != Progress.Quiet:
|
||||||
cprint(
|
status = f"Processed {iunit(self.total, 'file')}{' in readonly mode' if not context.update else ''}."
|
||||||
CLI_OK_FG,
|
cprint(CLI_OK_FG, status)
|
||||||
f"Processed {iunit(self.total, 'file')}{' in readonly mode' if not context.update else ''}.",
|
self.log.info(status)
|
||||||
)
|
|
||||||
|
|
||||||
if self.progress == Progress.Fancy and self.total > 0:
|
if self.progress == Progress.Fancy and self.total > 0:
|
||||||
elapsed = datetime.now() - self.fps.start
|
elapsed = datetime.now() - self.fps.start
|
||||||
@ -219,13 +233,14 @@ class Main:
|
|||||||
for err in self.dmg_list:
|
for err in self.dmg_list:
|
||||||
print(err, file=sys.stderr)
|
print(err, file=sys.stderr)
|
||||||
n = len(self.dmg_list)
|
n = len(self.dmg_list)
|
||||||
eprint(
|
status = f"error: detected {iunit(n, 'file')} with damage!"
|
||||||
CLI_ALERT_FG,
|
self.log.error(status)
|
||||||
f"error: detected {iunit(n, 'file')} with damage!",
|
eprint(CLI_ALERT_FG, status)
|
||||||
)
|
|
||||||
|
|
||||||
if self.err_list:
|
if self.err_list:
|
||||||
eprint(CLI_ALERT_FG, "chkbit ran into errors:")
|
status = "chkbit ran into errors"
|
||||||
|
self.log.error(status + "!")
|
||||||
|
eprint(CLI_ALERT_FG, status + ":")
|
||||||
for err in self.err_list:
|
for err in self.err_list:
|
||||||
print(err, file=sys.stderr)
|
print(err, file=sys.stderr)
|
||||||
|
|
||||||
@ -270,6 +285,18 @@ class Main:
|
|||||||
"-s", "--skip-symlinks", action="store_true", help="do not follow symlinks"
|
"-s", "--skip-symlinks", action="store_true", help="do not follow symlinks"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-l",
|
||||||
|
"--log-file",
|
||||||
|
metavar="FILE",
|
||||||
|
type=str,
|
||||||
|
help="write to a logfile if specified",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--log-verbose", action="store_true", help="verbose logging"
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--index-name",
|
"--index-name",
|
||||||
metavar="NAME",
|
metavar="NAME",
|
||||||
@ -315,6 +342,18 @@ class Main:
|
|||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
self.verbose = args.verbose or args.show_ignored_only
|
self.verbose = args.verbose or args.show_ignored_only
|
||||||
|
if args.log_file:
|
||||||
|
self.log_verbose = args.log_verbose
|
||||||
|
self.log.setLevel(logging.INFO)
|
||||||
|
fh = logging.FileHandler(args.log_file)
|
||||||
|
fh.setFormatter(
|
||||||
|
logging.Formatter(
|
||||||
|
"%(asctime)s %(levelname).4s %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.log.addHandler(fh)
|
||||||
|
|
||||||
if args.quiet:
|
if args.quiet:
|
||||||
self.progress = Progress.Quiet
|
self.progress = Progress.Quiet
|
||||||
elif not sys.stdout.isatty():
|
elif not sys.stdout.isatty():
|
||||||
@ -323,6 +362,7 @@ class Main:
|
|||||||
self.progress = Progress.Plain
|
self.progress = Progress.Plain
|
||||||
|
|
||||||
if args.paths:
|
if args.paths:
|
||||||
|
self.log.info(f"chkbit {', '.join(args.paths)}")
|
||||||
context = self.process(args)
|
context = self.process(args)
|
||||||
if context and not context.show_ignored_only:
|
if context and not context.show_ignored_only:
|
||||||
self.print_result(context)
|
self.print_result(context)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user