Ticket #13487: logging_systemd-journal_pull_request_182.path

File logging_systemd-journal_pull_request_182.path, 9.8 KB (added by rcrdnalor, 7 years ago)
Line 
1diff --git a/mythtv/bindings/python/MythTV/logging.py b/mythtv/bindings/python/MythTV/logging.py
2index 63cac19aff..102e2a83eb 100644
3--- a/mythtv/bindings/python/MythTV/logging.py
4+++ b/mythtv/bindings/python/MythTV/logging.py
5@@ -7,7 +7,12 @@ from MythTV.exceptions import MythError
6 import os
7 import syslog
8 import codecs
9-
10+try:
11+ # needs python-systemd package installed
12+ from systemd import journal
13+except:
14+ # bail only when '--systemd-journal' is selected
15+ journal = None
16 from sys import version_info, stdout, argv
17 from datetime import datetime
18 try:
19@@ -102,8 +107,9 @@ class MythLog( LOGLEVEL, LOGMASK, LOGFACILITY ):
20 cls._LOGFILE = stdout
21 cls._logwrite = cls._logfile
22 cls._QUIET = 0
23- cls._DBLOG = True
24+ cls._DBLOG = False
25 cls._SYSLOG = None
26+ cls._JOURNALLOG = False
27 cls._lock = allocate_lock()
28 cls._parseinput()
29
30@@ -111,6 +117,7 @@ class MythLog( LOGLEVEL, LOGMASK, LOGFACILITY ):
31 def _parseinput(cls):
32 args = iter(argv)
33 next(args)
34+ n = 0
35 try:
36 while True:
37 arg = next(args)
38@@ -118,57 +125,89 @@ class MythLog( LOGLEVEL, LOGMASK, LOGFACILITY ):
39 cls._QUIET += 1
40 elif arg == '--nodblog':
41 cls._DBLOG = False
42+ elif arg == '--enable-dblog':
43+ cls._DBLOG = True
44 elif arg == '--loglevel':
45 cls._setlevel(next(args))
46 elif arg == '--verbose':
47 cls._setmask(next(args))
48 elif arg == '--logfile':
49 cls._setfile(next(args))
50+ n += 1
51 elif arg == '--logpath':
52 cls._setpath(next(args))
53+ n += 1
54 elif arg == '--syslog':
55 cls._setsyslog(next(args))
56+ n += 1
57+ elif arg == '--systemd-journal':
58+ cls._setjournallog()
59+ n += 1
60 elif arg == '--':
61 break
62-
63 except StopIteration:
64 pass
65+ # only allow one logging method
66+ if (n > 1):
67+ raise MythError("Error: These logging options are mutually exclusive: " + \
68+ "'logfile', 'logpath', 'syslog' or 'systemd-journal'!")
69
70 @classmethod
71 def _optparseinput(cls):
72 opts, args = cls._parser.parse_args()
73+ n = 0
74 if opts.quiet:
75 cls._QUIET = opts.quiet
76 if opts.dblog:
77- cls._DBLOG = False
78+ cls._DBLOG = True
79 if opts.loglevel:
80 cls._setlevel(opts.loglevel)
81 if opts.verbose:
82 cls._setmask(opts.verbose)
83 if opts.logfile:
84 cls._setfile(opts.logfile)
85+ n += 1
86 if opts.logpath:
87 cls._setpath(opts.logpath)
88+ n += 1
89 if opts.syslog:
90 cls._setsyslog(opts.syslog)
91+ n += 1
92+ if opts.journallog:
93+ cls._setjournallog()
94+ n += 1
95+ if (n > 1):
96+ cls._parser.error("These logging options are mutually exclusive: " + \
97+ "'logfile', 'logpath', 'syslog' or 'systemd-journal'!")
98+
99
100 @classmethod
101 def _argparseinput(cls):
102 opts = cls._parser.parse_args()
103+ n = 0
104 if opts.quiet:
105 cls._QUIET = opts.quiet
106 if opts.dblog:
107- cls._DBLOG = False
108+ cls._DBLOG = True
109 if opts.loglevel:
110 cls._setlevel(opts.loglevel)
111 if opts.verbose:
112 cls._setmask(opts.verbose)
113 if opts.logfile:
114 cls._setfile(opts.logfile)
115+ n += 1
116 if opts.logpath:
117 cls._setpath(opts.logpath)
118+ n += 1
119 if opts.syslog:
120 cls._setsyslog(opts.syslog)
121+ n += 1
122+ if opts.journallog:
123+ cls._setjournallog()
124+ n += 1
125+ if (n > 1):
126+ cls._parser.error("These logging options are mutually exclusive: " + \
127+ "'logfile', 'logpath', 'syslog' or 'systemd-journal'!")
128
129 @classmethod
130 def loadOptParse(cls, parser):
131@@ -176,8 +215,10 @@ class MythLog( LOGLEVEL, LOGMASK, LOGFACILITY ):
132 cls._parseinput = cls._optparseinput
133 parser.add_option('--quiet', action="count", dest="quiet",
134 help="Run quiet. One use squelches terminal, two stops all logging.")
135- parser.add_option('--nodblog', action="store_true", dest="dblog",
136- help="Prevent logging to the database.")
137+ parser.add_option('--nodblog', action="store_true", dest="nodblog",
138+ help="Prevent logging to the database (legacy: disabled by default).")
139+ parser.add_option('--enable-dblog', action="store_true", dest="dblog",
140+ help="Enable logging to the database.")
141 parser.add_option('--loglevel', type="string", action="store", dest="loglevel",
142 help="Specify log verbosity, using standard syslog levels.")
143 parser.add_option('--verbose', type="string", action="store", dest="verbose",
144@@ -188,6 +229,8 @@ class MythLog( LOGLEVEL, LOGMASK, LOGFACILITY ):
145 help="Specify directory to log to, filename will be automatically decided.")
146 parser.add_option('--syslog', type="string", action="store", dest="syslog",
147 help="Specify syslog facility to log to.")
148+ parser.add_option('--systemd-journal', action="store_true", dest="journallog",
149+ help="Specify systemd-journal to log to.")
150
151 @classmethod
152 def loadArgParse(cls, parser):
153@@ -204,8 +247,10 @@ class MythLog( LOGLEVEL, LOGMASK, LOGFACILITY ):
154 cls._parseinput = cls._argparseinput
155 parser.add_argument('--quiet', action=Count, nargs=0, dest="quiet",
156 help="Run quiet. One use squelches terminal, two stops all logging.")
157- parser.add_argument('--nodblog', action="store_true", dest="dblog",
158- help="Prevent logging to the database.")
159+ parser.add_argument('--nodblog', action="store_true", dest="nodblog",
160+ help="Prevent logging to the database (legacy: disabled by default).")
161+ parser.add_argument('--enable-dblog', action="store_true", dest="dblog",
162+ help="Enable logging to the database.")
163 parser.add_argument('--loglevel', action="store", dest="loglevel",
164 help="Specify log verbosity, using standard syslog levels.")
165 parser.add_argument('--verbose', action="store", dest="verbose",
166@@ -216,6 +261,8 @@ class MythLog( LOGLEVEL, LOGMASK, LOGFACILITY ):
167 help="Specify directory to log to, filename will be automatically decided.")
168 parser.add_argument('--syslog', action="store", dest="syslog",
169 help="Specify syslog facility to log to.")
170+ parser.add_argument('--systemd-journal', action="store_true", dest="journallog",
171+ help="Specify systemd-journal to log to.")
172
173 def __repr__(self):
174 return "<%s '%s','%s' at %s>" % \
175@@ -275,9 +322,12 @@ class MythLog( LOGLEVEL, LOGMASK, LOGFACILITY ):
176 cls._LOGFILE.close()
177 cls._LOGFILE = fileobject
178 cls._logwrite = cls._logfile
179+ # clear other logging options:
180 if cls._SYSLOG:
181 cls._SYSLOG = None
182 syslog.closelog()
183+ if cls._JOURNALLOG:
184+ cls._JOURNALLOG = False
185
186 @classmethod
187 def _setsyslog(cls, facility=LOGFACILITY.USER):
188@@ -300,14 +350,37 @@ class MythLog( LOGLEVEL, LOGMASK, LOGFACILITY ):
189 raise MythError("Invalid syslog facility")
190
191 cls._SYSLOG = facility
192- syslog.openlog(argv[0].rsplit('/', 1)[1],
193- syslog.LOG_NDELAY|syslog.LOG_PID,
194- getattr(syslog, facility))
195+ application = argv[0]
196+ if '/' in application:
197+ application = application.rsplit('/', 1)[1]
198+ syslog.openlog(application, syslog.LOG_NDELAY|syslog.LOG_PID,
199+ getattr(syslog, facility))
200 cls._logwrite = cls._logsyslog
201+ # clear other logging options:
202 if cls._LOGFILE:
203 if cls._LOGFILE.fileno() != 1:
204 cls._LOGFILE.close()
205 cls._LOGFILE = None
206+ if cls._JOURNALLOG:
207+ cls._JOURNALLOG = False
208+
209+ @classmethod
210+ def _setjournallog(cls):
211+ cls._initlogger()
212+ if journal:
213+ cls._JOURNALLOG = True
214+ cls._logwrite = cls._logjournallog
215+ # clear other logging options:
216+ if cls._LOGFILE:
217+ if cls._LOGFILE.fileno() != 1:
218+ cls._LOGFILE.close()
219+ cls._LOGFILE = None
220+ if cls._SYSLOG:
221+ cls._SYSLOG = None
222+ syslog.closelog()
223+ else:
224+ raise MythError("Error: Python module 'systemd.journal' not available! " + \
225+ "Please install 'python-systemd' module.")
226
227 @classmethod
228 def _parsemask(cls, mstr=None):
229@@ -412,6 +485,17 @@ class MythLog( LOGLEVEL, LOGMASK, LOGFACILITY ):
230 syslog.syslog(level,
231 message + (' -- {0}'.format(detail) if detail else ''))
232
233+ def _logjournallog(self, mask, level, message, detail):
234+ if detail:
235+ detail = ' -- {0}'.format(detail)
236+ else:
237+ detail = ''
238+ application = argv[0]
239+ if '/' in application:
240+ application = application.rsplit('/', 1)[1]
241+ msg = ("[{0}]: {1}{2}".format(self.module, message, detail))
242+ journal.send(msg, PRIORITY=level, SYSLOG_IDENTIFIER=application )
243+
244 def _logdatabase(self, mask, level, message, detail):
245 if self.db and self._DBLOG:
246 with self.db.cursor(DummyLogger()) as cursor: