Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

# 

# anaconda_logging.py: Support for logging to multiple destinations with log 

# levels - basically an extension to the Python logging 

# module with Anaconda specififc enhancements. 

# 

# Copyright (C) 2000, 2001, 2002, 2005, 2017 Red Hat, Inc. All rights reserved. 

# 

# This program is free software; you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation; either version 2 of the License, or 

# (at your option) any later version. 

# 

# This program is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

# GNU General Public License for more details. 

# 

# You should have received a copy of the GNU General Public License 

# along with this program. If not, see <http://www.gnu.org/licenses/>. 

# 

 

import logging 

from logging.handlers import SysLogHandler, SocketHandler 

from systemd.journal import JournalHandler 

import os 

import sys 

import warnings 

import wrapt 

 

from pyanaconda.flags import flags 

from pyanaconda.core import constants 

 

DEFAULT_LEVEL = logging.INFO 

ENTRY_FORMAT = "%(asctime)s,%(msecs)03d %(levelname)s %(name)s: %(message)s" 

STDOUT_FORMAT = "%(asctime)s %(message)s" 

DATE_FORMAT = "%H:%M:%S" 

 

# the Anaconda log uses structured logging 

ANACONDA_ENTRY_FORMAT = "%(asctime)s,%(msecs)03d %(levelname)s %(log_prefix)s: %(message)s" 

ANACONDA_SYSLOG_FORMAT = "anaconda: %(log_prefix)s: %(message)s" 

 

MAIN_LOG_FILE = "/tmp/anaconda.log" 

PROGRAM_LOG_FILE = "/tmp/program.log" 

STORAGE_LOG_FILE = "/tmp/storage.log" 

PACKAGING_LOG_FILE = "/tmp/packaging.log" 

SENSITIVE_INFO_LOG_FILE = "/tmp/sensitive-info.log" 

ANACONDA_SYSLOG_FACILITY = SysLogHandler.LOG_LOCAL1 

ANACONDA_SYSLOG_IDENTIFIER = "anaconda" 

 

from threading import Lock 

program_log_lock = Lock() 

 

logLevelMap = {"debug": logging.DEBUG, 

"info": logging.INFO, 

"warning": logging.WARNING, 

"error": logging.ERROR, 

"critical": logging.CRITICAL} 

 

# sets autoSetLevel for the given handler 

def autoSetLevel(handler, value): 

handler.autoSetLevel = value 

 

# all handlers of given logger with autoSetLevel == True are set to level 

def setHandlersLevel(logr, level): 

for handler in filter(lambda hdlr: hasattr(hdlr, "autoSetLevel") and hdlr.autoSetLevel, logr.handlers): 

handler.setLevel(level) 

 

def forwardToJournal(logr, log_formatter=None, log_filter=None): 

"""Forward everything that goes in the logger to the journal daemon.""" 

if flags.imageInstall or flags.dirInstall: 

# don't clutter up the system logs when doing an image install 

return 

 

# Don't add syslog tag if custom formatter is in use. 

# This also means that custom formatters need to make sure they 

# add the tag correctly themselves. 

if log_formatter: 

tag = None 

else: 

tag = logr.name 

journal_handler = AnacondaJournalHandler(tag=tag) 

journal_handler.setLevel(logging.DEBUG) 

if log_filter: 

journal_handler.addFilter(log_filter) 

if log_formatter: 

journal_handler.setFormatter(log_formatter) 

logr.addHandler(journal_handler) 

 

 

class _AnacondaLogFixer(object): 

""" A mixin for logging.StreamHandler that does not lock during format. 

 

Add this mixin before the Handler type in the inheritance order. 

""" 

 

# filter, emit, lock, and acquire need to be implemented in a subclass 

 

def handle(self, record): 

# copied from logging.Handler, minus the lock acquisition 

rv = self.filter(record) # pylint: disable=no-member 

if rv: 

self.emit(record) # pylint: disable=no-member 

return rv 

 

@property 

def stream(self): 

return self._stream 

 

@stream.setter 

def stream(self, value): 

# Wrap the stream write in a lock acquisition 

# Use an object proxy in order to work with types that may not allow 

# the write property to be set. 

class WriteProxy(wrapt.ObjectProxy): 

# pylint: disable=no-self-argument 

# rename self so we can reference the Handler object 

def write(wrapped_self, *args, **kwargs): 

self.acquire() # pylint: disable=no-member 

try: 

wrapped_self.__wrapped__.write(*args, **kwargs) 

finally: 

self.release() # pylint: disable=no-member 

 

# Live with this attribute being defined outside of init to avoid the 

# hassle of having an init. If _stream is not set, then stream was 

# never set on the StreamHandler object, so accessing it in that case 

# is supposed to be an error. 

self._stream = WriteProxy(value) # pylint: disable=attribute-defined-outside-init 

 

class AnacondaJournalHandler(_AnacondaLogFixer, JournalHandler): 

def __init__(self, tag='', facility=ANACONDA_SYSLOG_FACILITY, 

identifier=ANACONDA_SYSLOG_IDENTIFIER): 

self.tag = tag 

JournalHandler.__init__(self, 

SYSLOG_FACILITY=facility, 

SYSLOG_IDENTIFIER=identifier) 

 

def emit(self, record): 

if self.tag: 

original_msg = record.msg 

record.msg = '%s: %s' % (self.tag, original_msg) 

JournalHandler.emit(self, record) 

record.msg = original_msg 

else: 

JournalHandler.emit(self, record) 

 

class AnacondaSocketHandler(_AnacondaLogFixer, SocketHandler): 

def makePickle(self, record): 

return bytes(self.formatter.format(record) + "\n", "utf-8") 

 

class AnacondaFileHandler(_AnacondaLogFixer, logging.FileHandler): 

pass 

 

class AnacondaStreamHandler(_AnacondaLogFixer, logging.StreamHandler): 

pass 

 

class AnacondaPrefixFilter(logging.Filter): 

"""Add a log_prefix field, which is based on the name property, 

but without the "anaconda." prefix. 

 

Also if name is equal to "anaconda", generally meaning some sort of 

general (or miss-directed) log message, set the log_prefix to "misc". 

""" 

 

def filter(self, record): 

record.log_prefix = "" 

if record.name: 

# messages going to the generic "anaconda" logger get the log prefix "misc" 

if record.name == "anaconda": 

record.log_prefix = "misc" 

elif record.name.startswith("anaconda."): 

# drop "anaconda." from the log prefix 

record.log_prefix = record.name[9:] 

return True 

 

class AnacondaLog(object): 

SYSLOG_CFGFILE = "/etc/rsyslog.conf" 

VIRTIO_PORT = "/dev/virtio-ports/org.fedoraproject.anaconda.log.0" 

 

def __init__(self): 

self.loglevel = DEFAULT_LEVEL 

self.remote_syslog = None 

# Rename the loglevels so they are the same as in syslog. 

logging.addLevelName(logging.CRITICAL, "CRT") 

logging.addLevelName(logging.ERROR, "ERR") 

logging.addLevelName(logging.WARNING, "WRN") 

logging.addLevelName(logging.INFO, "INF") 

logging.addLevelName(logging.DEBUG, "DBG") 

 

# Create the base of the logger hierarchy. 

# Disable propagation to the parent logger, since the root logger is 

# handled by a FileHandler(/dev/null), which can deadlock. 

self.anaconda_logger = logging.getLogger("anaconda") 

self.anaconda_logger.propagate = False 

self.addFileHandler(MAIN_LOG_FILE, self.anaconda_logger, 

minLevel=logging.DEBUG, 

fmtStr=ANACONDA_ENTRY_FORMAT, 

log_filter=AnacondaPrefixFilter()) 

warnings.showwarning = self.showwarning 

 

# Create the storage logger. 

storage_logger = logging.getLogger(constants.LOGGER_BLIVET) 

storage_logger.propagate = False 

self.addFileHandler(STORAGE_LOG_FILE, storage_logger, 

minLevel=logging.DEBUG) 

 

# Set the common parameters for anaconda and storage loggers. 

for logr in [self.anaconda_logger, storage_logger]: 

logr.setLevel(logging.DEBUG) 

 

# forward both logs to syslog 

forwardToJournal(self.anaconda_logger, 

log_filter=AnacondaPrefixFilter(), 

log_formatter=logging.Formatter(ANACONDA_SYSLOG_FORMAT)) 

forwardToJournal(storage_logger) 

 

# External program output log 

program_logger = logging.getLogger(constants.LOGGER_PROGRAM) 

program_logger.propagate = False 

program_logger.setLevel(logging.DEBUG) 

self.addFileHandler(PROGRAM_LOG_FILE, program_logger, 

minLevel=logging.DEBUG) 

forwardToJournal(program_logger) 

 

# Create the packaging logger. 

packaging_logger = logging.getLogger(constants.LOGGER_PACKAGING) 

packaging_logger.setLevel(logging.DEBUG) 

packaging_logger.propagate = False 

self.addFileHandler(PACKAGING_LOG_FILE, packaging_logger, 

minLevel=logging.INFO, 

autoLevel=True) 

forwardToJournal(packaging_logger) 

 

# Create the dnf logger and link it to packaging 

dnf_logger = logging.getLogger(constants.LOGGER_DNF) 

dnf_logger.setLevel(logging.DEBUG) 

self.addFileHandler(PACKAGING_LOG_FILE, dnf_logger, 

minLevel=logging.NOTSET) 

forwardToJournal(dnf_logger) 

 

# Create the simpleline logger and link it to anaconda 

simpleline_logger = logging.getLogger(constants.LOGGER_SIMPLELINE) 

simpleline_logger.setLevel(logging.DEBUG) 

self.addFileHandler(MAIN_LOG_FILE, simpleline_logger, 

minLevel=logging.NOTSET) 

forwardToJournal(simpleline_logger) 

 

# Create the sensitive information logger 

# * the sensitive-info.log file is not copied to the installed 

# system, as it might contain sensitive information that 

# should not be persistently stored by default 

sensitive_logger = logging.getLogger(constants.LOGGER_SENSITIVE_INFO) 

sensitive_logger.propagate = False 

self.addFileHandler(SENSITIVE_INFO_LOG_FILE, sensitive_logger, 

minLevel=logging.DEBUG) 

 

# Create a second logger for just the stuff we want to dup on 

# stdout. Anything written here will also get passed up to the 

# parent loggers for processing and possibly be written to the 

# log. 

stdout_logger = logging.getLogger(constants.LOGGER_STDOUT) 

stdout_logger.setLevel(logging.INFO) 

# Add a handler for the duped stuff. No fancy formatting, thanks. 

self.addFileHandler(sys.stdout, stdout_logger, 

fmtStr=STDOUT_FORMAT, minLevel=logging.INFO) 

 

# Stderr logger 

stderr_logger = logging.getLogger(constants.LOGGER_STDERR) 

stderr_logger.setLevel(logging.INFO) 

self.addFileHandler(sys.stderr, stderr_logger, 

fmtStr=STDOUT_FORMAT, minLevel=logging.INFO) 

 

# Add a simple handler - file or stream, depending on what we're given. 

def addFileHandler(self, dest, addToLogger, minLevel=DEFAULT_LEVEL, 

fmtStr=ENTRY_FORMAT, 

autoLevel=False, 

log_filter=None): 

try: 

if isinstance(dest, str): 

logfile_handler = AnacondaFileHandler(dest) 

else: 

logfile_handler = AnacondaStreamHandler(dest) 

 

if log_filter: 

logfile_handler.addFilter(log_filter) 

logfile_handler.setLevel(minLevel) 

logfile_handler.setFormatter(logging.Formatter(fmtStr, DATE_FORMAT)) 

autoSetLevel(logfile_handler, autoLevel) 

addToLogger.addHandler(logfile_handler) 

except IOError: 

pass 

 

# pylint: disable=redefined-builtin 

def showwarning(self, message, category, filename, lineno, 

file=sys.stderr, line=None): 

""" Make sure messages sent through python's warnings module get logged. 

 

The warnings mechanism is used by some libraries we use, 

notably pykickstart. 

""" 

self.anaconda_logger.warning("%s", warnings.formatwarning( 

message, category, filename, lineno, line)) 

 

def setup_remotelog(self, host, port): 

remotelog = AnacondaSocketHandler(host, port) 

remotelog.setFormatter(logging.Formatter(ENTRY_FORMAT, DATE_FORMAT)) 

remotelog.setLevel(logging.DEBUG) 

logging.getLogger().addHandler(remotelog) 

 

def restartSyslog(self): 

# Import here instead of at the module level to avoid an import loop 

from pyanaconda.core.util import restart_service 

restart_service("rsyslog") 

 

def updateRemote(self, remote_syslog): 

"""Updates the location of remote rsyslogd to forward to. 

 

Requires updating rsyslogd config and restarting rsyslog 

""" 

 

template = "*.* @@%s\n" 

 

self.remote_syslog = remote_syslog 

with open(self.SYSLOG_CFGFILE, 'a') as cfgfile: 

forward_line = template % remote_syslog 

cfgfile.write(forward_line) 

self.restartSyslog() 

 

def setupVirtio(self): 

"""Setup virtio rsyslog logging. 

""" 

 

template = "*.* %s;anaconda_syslog\n" 

 

vport = flags.cmdline.get('virtiolog') or self.VIRTIO_PORT 

 

if not os.access(vport, os.W_OK): 

return 

 

with open(self.SYSLOG_CFGFILE, 'a') as cfgfile: 

cfgfile.write(template % (vport,)) 

self.restartSyslog() 

 

 

logger = None 

def init(): 

global logger 

logger = AnacondaLog()