| # 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) |
| |