2046N/A#!/usr/bin/python
2046N/A#
2046N/A# CDDL HEADER START
2046N/A#
2046N/A# The contents of this file are subject to the terms of the
2046N/A# Common Development and Distribution License (the "License").
2046N/A# You may not use this file except in compliance with the License.
2046N/A#
2046N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2046N/A# or http://www.opensolaris.org/os/licensing.
2046N/A# See the License for the specific language governing permissions
2046N/A# and limitations under the License.
2046N/A#
2046N/A# When distributing Covered Code, include this CDDL HEADER in each
2046N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2046N/A# If applicable, add the following below this CDDL HEADER, with the
2046N/A# fields enclosed by brackets "[]" replaced with your own identifying
2046N/A# information: Portions Copyright [yyyy] [name of copyright owner]
2046N/A#
2046N/A# CDDL HEADER END
2046N/A#
2046N/A
2046N/A#
3158N/A# Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
2046N/A#
2046N/A
2046N/Aimport logging
3234N/Aimport os
3234N/Aimport six
2046N/Aimport sys
2046N/A
2434N/Afrom pkg.lint.base import DuplicateLintedAttrException, linted
2434N/A
2046N/A# a set of lint messages that can be produced.
2046N/ADEBUG, INFO, WARNING, ERROR, CRITICAL = range(5)
2046N/A
2046N/ALEVELS = {
2046N/A "DEBUG": DEBUG,
2046N/A "INFO": INFO,
2046N/A "WARNING": WARNING,
2046N/A "ERROR": ERROR,
2046N/A "CRITICAL": CRITICAL,
2046N/A # we are our own reverse map
2046N/A DEBUG: "DEBUG",
2046N/A INFO: "INFO",
2046N/A WARNING: "WARNING",
2046N/A ERROR: "ERROR",
2046N/A CRITICAL: "CRITICAL"
2046N/A }
2046N/A
2046N/Aclass LintMessage(object):
2046N/A """A base class for all lint messages."""
2046N/A
2046N/A msg = ""
2046N/A def __init__(self, msg, level=INFO, producer="unknown", msgid=None):
2046N/A self.msg = msg
2046N/A self.level = level
2046N/A self.producer = producer
2046N/A self.msgid = msgid
2046N/A
2046N/A def __str__(self):
2046N/A return str(self.msg)
2046N/A
2046N/A
2046N/Aclass TrackerHandler(logging.StreamHandler):
2046N/A """"Inspect a given pkg.client.progress.ProgressTracker, telling it
2046N/A to flush, before emitting output."""
2046N/A
2046N/A def __init__(self, tracker, strm=None):
2046N/A logging.StreamHandler.__init__(self, strm)
2046N/A
2046N/A if os.isatty(sys.stderr.fileno()) and \
2046N/A os.isatty(sys.stdout.fileno()):
2046N/A self.write_crs = True
2046N/A else:
2046N/A self.write_crs = False
2046N/A self.tracker = tracker
2046N/A
2046N/A def emit(self, record):
2046N/A if self.write_crs and self.tracker:
2046N/A self.tracker.flush()
2046N/A logging.StreamHandler.emit(self, record)
2046N/A
2046N/A
2046N/Aclass LogFormatter(object):
2046N/A """A class that formats log messages."""
2046N/A
2046N/A def __init__(self, tracker=None, level=INFO):
2046N/A self._level = level
2046N/A
2046N/A # install our own logger, writing to stderr
2046N/A self.logger = logging.getLogger("pkglint_checks")
2046N/A
2046N/A self._th = TrackerHandler(tracker, strm=sys.stderr)
2046N/A self.logger.setLevel(logging.INFO)
2046N/A self._th.setLevel(logging.INFO)
2046N/A self.logger.addHandler(self._th)
2046N/A self.emitted = False
2046N/A
2434N/A # the action and manifest we expect messages from. See advise()
2434N/A self.action = None
2434N/A self.manifest = None
2434N/A
2537N/A # when the logger is using an engine, the engine registers
2537N/A # itself here
2537N/A self.engine = None
2537N/A
2046N/A # setters/getters for the tracker being used, adding that
2046N/A # to our private log handler
2046N/A def _get_tracker(self):
2046N/A return self._th.tracker
2046N/A
2046N/A def _set_tracker(self, value):
2046N/A self._th.tracker = value
2046N/A
2046N/A def _del_tracker(self):
2046N/A del self._th.tracker
2046N/A
2046N/A # setters/getters for log level to allow us to set
2046N/A # string values for what's always stored as an integer
2046N/A def _get_level(self):
2046N/A return self._level
2046N/A
2046N/A def _set_level(self, value):
2046N/A if isinstance(value, str):
2046N/A if value.upper() not in LEVELS:
2046N/A raise ValueError(
3194N/A _("{value} is not a valid level").format(
3194N/A **value))
2046N/A self._level = LEVELS[value]
2046N/A else:
2046N/A self._level = value
2046N/A
2046N/A def _del_level(self):
2046N/A del self._level
2046N/A
2046N/A level = property(_get_level, _set_level, _del_level)
2046N/A tracker = property(_get_tracker, _set_tracker, _del_tracker)
2046N/A
2046N/A # convenience methods to log messages
2434N/A def debug(self, message, msgid=None, ignore_linted=False):
2434N/A self.format(LintMessage(message, level=DEBUG, msgid=msgid),
2434N/A ignore_linted=ignore_linted)
2046N/A
2434N/A def info(self, message, msgid=None, ignore_linted=False):
2434N/A self.format(LintMessage(message, level=INFO, msgid=msgid),
2434N/A ignore_linted=ignore_linted)
2046N/A
2434N/A def warning(self, message, msgid=None, ignore_linted=False):
2434N/A self.format(LintMessage(message, level=WARNING, msgid=msgid),
2434N/A ignore_linted=ignore_linted)
2046N/A
2434N/A def error(self, message, msgid=None, ignore_linted=False):
2434N/A self.format(LintMessage(message, level=ERROR, msgid=msgid),
2434N/A ignore_linted=ignore_linted)
2046N/A
2434N/A def critical(self, message, msgid=None, ignore_linted=False):
2434N/A self.format(LintMessage(message, level=CRITICAL, msgid=msgid),
2434N/A ignore_linted=ignore_linted)
2046N/A
2046N/A def open(self):
2046N/A """Start a new log file"""
2046N/A pass
2046N/A
2046N/A def format(self, message):
2046N/A """Given a LintMessage message, format that object
2046N/A appropriately."""
2046N/A pass
2046N/A
2046N/A def close(self):
2046N/A """End a log file"""
2046N/A pass
2046N/A
2046N/A def produced_lint_msgs(self):
2046N/A """Called to determine if this logger produced any lint
2046N/A messages at a level >= its log level."""
2046N/A return self.emitted
2046N/A
2434N/A def advise(self, action=None, manifest=None):
2434N/A """Called to tell the logger to expect lint messages concerning
2434N/A the given action and/or manifest."""
2434N/A if action:
2434N/A self.action = action
2434N/A if manifest:
2434N/A self.manifest = manifest
2434N/A
2046N/A
2046N/Aclass PlainLogFormatter(LogFormatter):
2046N/A """A basic log formatter, just prints the message."""
2046N/A
2434N/A def format(self, msg, ignore_linted=False):
2046N/A
2434N/A if not isinstance(msg, LintMessage):
2046N/A self.logger.warning(msg)
2046N/A self.emitted = True
2434N/A return
2434N/A
2434N/A if msg.level >= self._level:
2434N/A if not msg.msgid:
2434N/A msg.msgid = "unknown"
2434N/A
2434N/A # Format the message level and message identifier
3158N/A key = "{0} {1}".format(LEVELS[msg.level], msg.msgid)
2434N/A if not ignore_linted:
2434N/A linted_flag = False
2434N/A try:
2434N/A linted_flag = linted(action=self.action,
2434N/A manifest=self.manifest,
2434N/A lint_id=msg.msgid)
3171N/A except DuplicateLintedAttrException as err:
3158N/A lint_key = ("{0} pkglint001.6".format(
3158N/A LEVELS[ERROR]))
3158N/A self.logger.warning("{0}{1}".format(
3158N/A lint_key.ljust(34),
3158N/A _("Logging error: {0}").format(
3158N/A err)))
2434N/A
2434N/A if linted_flag:
2537N/A # we may have asked not to report errors
2537N/A # that have been marked as pkg.linted
2537N/A if self.engine:
2537N/A report = self.engine.get_param(
2537N/A "pkglint001.5.report-linted")
2537N/A if report and \
2537N/A report.lower() == "false":
2537N/A return
3158N/A key = ("{0} pkglint001.5".format(
3158N/A LEVELS[INFO]))
2434N/A linted_msg = _(
3158N/A "Linted message: {id} "
3158N/A "{msg}").format(
3158N/A id=msg.msgid, msg=msg)
3158N/A self.logger.warning("{0}{1}".format(
3158N/A key.ljust(34), linted_msg))
2434N/A return
2434N/A
3158N/A self.logger.warning("{0}{1}".format(key.ljust(34),
3158N/A msg.msg))
2434N/A
2434N/A # We only treat errors, and criticals as being worthy
2434N/A # of a flag (pkglint returns non-zero if self.emitted)
2434N/A if msg.level > WARNING:
2434N/A self.emitted = True