blob: cc086123f0b92fc497f7fae3f3dd513dbd59fad3 [file] [log] [blame]
# elog/__init__.py - elog core functions
# Copyright 2006-2014 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
import sys
if sys.hexversion >= 0x3000000:
# pylint: disable=W0622
basestring = str
import portage
portage.proxy.lazyimport.lazyimport(globals(),
'portage.util:writemsg',
)
from portage.const import EBUILD_PHASES
from portage.exception import AlarmSignal, PortageException
from portage.process import atexit_register
from portage.elog.messages import collect_ebuild_messages, collect_messages
from portage.elog.filtering import filter_loglevels
from portage.localization import _
from portage import os
def _preload_elog_modules(settings):
logsystems = settings.get("PORTAGE_ELOG_SYSTEM", "").split()
for s in logsystems:
# allow per module overrides of PORTAGE_ELOG_CLASSES
if ":" in s:
s, levels = s.split(":", 1)
levels = levels.split(",")
# - is nicer than _ for module names, so allow people to use it.
s = s.replace("-", "_")
try:
_load_mod("portage.elog.mod_" + s)
except ImportError:
pass
def _merge_logentries(a, b):
rValue = {}
phases = set(a)
phases.update(b)
for p in phases:
merged_msgs = []
rValue[p] = merged_msgs
for d in a, b:
msgs = d.get(p)
if msgs:
merged_msgs.extend(msgs)
return rValue
def _combine_logentries(logentries):
# generate a single string with all log messages
rValue = []
for phase in EBUILD_PHASES:
if not phase in logentries:
continue
previous_type = None
for msgtype, msgcontent in logentries[phase]:
if previous_type != msgtype:
previous_type = msgtype
rValue.append("%s: %s" % (msgtype, phase))
if isinstance(msgcontent, basestring):
rValue.append(msgcontent.rstrip("\n"))
else:
for line in msgcontent:
rValue.append(line.rstrip("\n"))
if rValue:
rValue.append("")
return "\n".join(rValue)
_elog_mod_imports = {}
def _load_mod(name):
global _elog_mod_imports
m = _elog_mod_imports.get(name)
if m is None:
m = __import__(name)
for comp in name.split(".")[1:]:
m = getattr(m, comp)
_elog_mod_imports[name] = m
return m
_elog_listeners = []
def add_listener(listener):
'''
Listeners should accept four arguments: settings, key, logentries and logtext
'''
_elog_listeners.append(listener)
def remove_listener(listener):
'''
Remove previously added listener
'''
_elog_listeners.remove(listener)
_elog_atexit_handlers = []
def elog_process(cpv, mysettings, phasefilter=None):
global _elog_atexit_handlers
logsystems = mysettings.get("PORTAGE_ELOG_SYSTEM","").split()
for s in logsystems:
# allow per module overrides of PORTAGE_ELOG_CLASSES
if ":" in s:
s, levels = s.split(":", 1)
levels = levels.split(",")
# - is nicer than _ for module names, so allow people to use it.
s = s.replace("-", "_")
try:
_load_mod("portage.elog.mod_" + s)
except ImportError:
pass
if "T" in mysettings:
ebuild_logentries = collect_ebuild_messages(
os.path.join(mysettings["T"], "logging"))
else:
# A build dir isn't necessarily required since the messages.e*
# functions allow messages to be generated in-memory.
ebuild_logentries = {}
all_logentries = collect_messages(key=cpv, phasefilter=phasefilter)
if cpv in all_logentries:
# Messages generated by the python elog implementation are assumed
# to come first. For example, this ensures correct order for einfo
# messages that are generated prior to the setup phase.
all_logentries[cpv] = \
_merge_logentries(all_logentries[cpv], ebuild_logentries)
else:
all_logentries[cpv] = ebuild_logentries
my_elog_classes = set(mysettings.get("PORTAGE_ELOG_CLASSES", "").split())
logsystems = {}
for token in mysettings.get("PORTAGE_ELOG_SYSTEM", "").split():
if ":" in token:
s, levels = token.split(":", 1)
levels = levels.split(",")
else:
s = token
levels = ()
levels_set = logsystems.get(s)
if levels_set is None:
levels_set = set()
logsystems[s] = levels_set
levels_set.update(levels)
for key in all_logentries:
default_logentries = filter_loglevels(all_logentries[key], my_elog_classes)
# in case the filters matched all messages and no module overrides exist
if len(default_logentries) == 0 and (not ":" in mysettings.get("PORTAGE_ELOG_SYSTEM", "")):
continue
default_fulllog = _combine_logentries(default_logentries)
# call listeners
for listener in _elog_listeners:
listener(mysettings, str(key), default_logentries, default_fulllog)
# pass the processing to the individual modules
for s, levels in logsystems.items():
# allow per module overrides of PORTAGE_ELOG_CLASSES
if levels:
mod_logentries = filter_loglevels(all_logentries[key], levels)
mod_fulllog = _combine_logentries(mod_logentries)
else:
mod_logentries = default_logentries
mod_fulllog = default_fulllog
if len(mod_logentries) == 0:
continue
# - is nicer than _ for module names, so allow people to use it.
s = s.replace("-", "_")
try:
m = _load_mod("portage.elog.mod_" + s)
# Timeout after one minute (in case something like the mail
# module gets hung).
try:
AlarmSignal.register(60)
m.process(mysettings, str(key), mod_logentries, mod_fulllog)
finally:
AlarmSignal.unregister()
if hasattr(m, "finalize") and not m.finalize in _elog_atexit_handlers:
_elog_atexit_handlers.append(m.finalize)
atexit_register(m.finalize)
except (ImportError, AttributeError) as e:
writemsg(_("!!! Error while importing logging modules "
"while loading \"mod_%s\":\n") % str(s))
writemsg("%s\n" % str(e), noiselevel=-1)
except AlarmSignal:
writemsg("Timeout in elog_process for system '%s'\n" % s,
noiselevel=-1)
except PortageException as e:
writemsg("%s\n" % str(e), noiselevel=-1)