# elog/__init__.py - elog core functions # Copyright 2006-2011 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 import sys if sys.hexversion >= 0x3000000: 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)