Coverage for daklib/daklog.py: 88%
47 statements
« prev ^ index » next coverage.py v7.6.0, created at 2026-01-04 16:18 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2026-01-04 16:18 +0000
1"""
2Logging functions
4@contact: Debian FTP Master <ftpmaster@debian.org>
5@copyright: 2001, 2002, 2006 James Troup <james@nocrew.org>
6@license: GNU General Public License version 2 or later
7"""
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23################################################################################
25import fcntl
26import os
27import sys
28import time
29import traceback
30from typing import Any
32from . import utils
34################################################################################
37class Logger:
38 "Logger object"
40 __shared_state: dict[str, Any] = {}
42 def __init__(
43 self, program="unknown", debug=False, print_starting=True, include_pid=False
44 ):
45 self.__dict__ = self.__shared_state
47 self.program = program
48 self.debug = debug
49 self.include_pid = include_pid
51 if not getattr(self, "logfile", None):
52 self._open_log(debug)
54 if print_starting:
55 self.log(["program start"])
57 def _open_log(self, debug: bool) -> None:
58 # Create the log directory if it doesn't exist
59 from daklib.config import Config
61 logdir = Config()["Dir::Log"]
62 if not os.path.exists(logdir):
63 umask = os.umask(00000)
64 os.makedirs(logdir, 0o2775)
65 os.umask(umask)
67 # Open the logfile
68 logfilename = "%s/%s" % (logdir, time.strftime("%Y-%m"))
69 logfile = None
71 if debug: 71 ↛ 72line 71 didn't jump to line 72 because the condition on line 71 was never true
72 logfile = sys.stderr
73 else:
74 umask = os.umask(0o0002)
75 logfile = open(logfilename, "a")
76 os.umask(umask)
78 self.logfile = logfile
80 def log(self, details: list[object]) -> None:
81 "Log an event"
82 # Prepend timestamp, program name, and user name
83 details_str = [time.strftime("%Y%m%d%H%M%S"), self.program, utils.getusername()]
84 # Force the contents of the list to be string.join-able
85 details_str.extend(str(i) for i in details)
86 fcntl.lockf(self.logfile, fcntl.LOCK_EX)
87 # Write out the log in TSV
88 self.logfile.write("|".join(details_str) + "\n")
89 # Flush the output to enable tail-ing
90 self.logfile.flush()
91 fcntl.lockf(self.logfile, fcntl.LOCK_UN)
93 def log_traceback(self, info, ex) -> None:
94 "Log an exception with a traceback"
95 self.log([info, repr(ex)])
96 for line in traceback.format_exc().split("\n")[:-1]:
97 self.log(["traceback", line])
99 def close(self) -> None:
100 "Close a Logger object"
101 self.log(["program end"])
102 self.logfile.close()