Пример #1
0
def setup_logfile_handler(
    log_path,
    log_level=None,
    log_format=None,
    date_format=None,
    max_bytes=0,
    backup_count=0,
    user=None,
):
    """
    Setup the log file handler

    Since version 0.10.6 we support logging to syslog, some examples:

        tcp://localhost:514/LOG_USER
        tcp://localhost/LOG_DAEMON
        udp://localhost:5145/LOG_KERN
        udp://localhost
        file:///dev/log
        file:///dev/log/LOG_SYSLOG
        file:///dev/log/LOG_DAEMON

    The above examples are self explanatory, but:
        <file|udp|tcp>://<host|socketpath>:<port-if-required>/<log-facility>

    Thinking on doing remote logging you might also be thinking that
    you could point Salt's logging to the remote syslog. **Please Don't!**
    An issue has been reported when doing this over TCP where the logged lines
    get concatenated. See #3061.

    The preferred way to do remote logging is setup a local syslog, point
    Salt's logging to the local syslog(unix socket is much faster) and then
    have the local syslog forward the log messages to the remote syslog.
    """
    if is_logfile_handler_configured():
        log.warning("Logfile logging already configured")
        return

    atexit.register(shutdown_logfile_handler)
    log.trace(
        "Setting up log file logging: %s",
        dict(
            log_path=log_path,
            log_level=log_level,
            log_format=log_format,
            date_format=date_format,
            max_bytes=max_bytes,
            backup_count=backup_count,
            user=user,
        ),
    )

    if log_path is None:
        log.warning("log_path setting is set to `None`. Nothing else to do")
        return

    if log_level is None:
        log_level = logging.WARNING

    log_level = get_logging_level_from_string(log_level)

    parsed_log_path = urllib.parse.urlparse(log_path)

    if parsed_log_path.scheme in ("tcp", "udp", "file"):
        syslog_opts = {
            "facility": SysLogHandler.LOG_USER,
            "socktype": socket.SOCK_DGRAM,
        }

        if parsed_log_path.scheme == "file" and parsed_log_path.path:
            path = pathlib.Path(parsed_log_path.path)
            facility_name = path.stem.upper()
            try:
                if not facility_name.startswith("LOG_"):
                    # The user is not specifying a syslog facility
                    facility_name = "LOG_USER"  # Syslog default
                    syslog_opts["address"] = str(path.resolve())
                else:
                    # The user has set a syslog facility, let's update the path to
                    # the logging socket
                    syslog_opts["address"] = str(path.resolve().parent)
            except OSError as exc:
                raise LoggingRuntimeError(
                    "Failed to setup the Syslog logging handler: {}".format(
                        exc)) from exc
        elif parsed_log_path.path:
            # In case of udp or tcp with a facility specified
            path = pathlib.Path(parsed_log_path.path)
            facility_name = path.stem.upper()
            if not facility_name.startswith("LOG_"):
                # Logging facilities start with LOG_ if this is not the case
                # fail right now!
                raise LoggingRuntimeError(
                    "The syslog facility '{}' is not known".format(
                        facility_name))
        else:
            # This is the case of udp or tcp without a facility specified
            facility_name = "LOG_USER"  # Syslog default

        facility = getattr(SysLogHandler, facility_name, None)
        if facility is None:
            # This python syslog version does not know about the user provided
            # facility name
            raise LoggingRuntimeError(
                "The syslog facility '{}' is not known".format(facility_name))
        syslog_opts["facility"] = facility

        if parsed_log_path.scheme in ("tcp", "udp"):
            syslog_opts["address"] = (
                parsed_log_path.hostname,
                parsed_log_path.port or logging.handlers.SYSLOG_UDP_PORT,
            )
            if parsed_log_path.scheme == "tcp":
                syslog_opts["socktype"] = socket.SOCK_STREAM

        elif parsed_log_path.scheme == "file":
            syslog_opts.pop("socktype", None)

        try:
            # Et voilá! Finally our syslog handler instance
            handler = SysLogHandler(**syslog_opts)
        except OSError as exc:
            raise LoggingRuntimeError(
                "Failed to setup the Syslog logging handler: {}".format(
                    exc)) from exc
    else:
        # make sure, the logging directory exists and attempt to create it if necessary
        if user is None:
            import salt.utils.user

            user = salt.utils.user.get_user()

        import salt.utils.files
        import salt.utils.verify

        # Logfile is not using Syslog, verify
        with salt.utils.files.set_umask(0o027):
            salt.utils.verify.verify_log_files([log_path], user)
        try:
            # Logfile logging is UTF-8 on purpose.
            # Since salt uses YAML and YAML uses either UTF-8 or UTF-16, if a
            # user is not using plain ASCII, their system should be ready to
            # handle UTF-8.
            if max_bytes > 0:
                handler = RotatingFileHandler(
                    log_path,
                    mode="a",
                    maxBytes=max_bytes,
                    backupCount=backup_count,
                    encoding="utf-8",
                    delay=0,
                )
            else:
                handler = WatchedFileHandler(log_path,
                                             mode="a",
                                             encoding="utf-8",
                                             delay=0)
        except OSError:
            log.warning(
                "Failed to open log file, do you have permission to write to %s?",
                log_path,
            )
            # Do not proceed with any more configuration since it will fail, we
            # have the console logging already setup and the user should see
            # the error.
            return

    handler.setLevel(log_level)

    if not log_format:
        log_format = DFLT_LOG_FMT_LOGFILE
    if not date_format:
        date_format = DFLT_LOG_DATEFMT_LOGFILE

    formatter = logging.Formatter(log_format, datefmt=date_format)

    handler.setFormatter(formatter)
    logging.root.addHandler(handler)

    setup_logfile_handler.__handler__ = handler
Пример #2
0
    def _log(
        self,
        level,
        msg,
        args,
        exc_info=None,
        extra=None,  # pylint: disable=arguments-differ
        stack_info=False,
        stacklevel=1,
        exc_info_on_loglevel=None,
    ):
        if extra is None:
            extra = {}

        # pylint: disable=no-member
        current_jid = RequestContext.current.get("data", {}).get("jid", None)
        log_fmt_jid = RequestContext.current.get("opts",
                                                 {}).get("log_fmt_jid", None)
        # pylint: enable=no-member

        if current_jid is not None:
            extra["jid"] = current_jid

        if log_fmt_jid is not None:
            extra["log_fmt_jid"] = log_fmt_jid

        # If both exc_info and exc_info_on_loglevel are both passed, let's fail
        if exc_info and exc_info_on_loglevel:
            raise LoggingRuntimeError(
                "Only one of 'exc_info' and 'exc_info_on_loglevel' is permitted"
            )
        if exc_info_on_loglevel is not None:
            if isinstance(exc_info_on_loglevel, str):
                exc_info_on_loglevel = LOG_LEVELS.get(exc_info_on_loglevel,
                                                      logging.ERROR)
            elif not isinstance(exc_info_on_loglevel, int):
                raise RuntimeError(
                    "The value of 'exc_info_on_loglevel' needs to be a "
                    "logging level or a logging level name, not '{}'".format(
                        exc_info_on_loglevel))
        if extra is None:
            extra = {"exc_info_on_loglevel": exc_info_on_loglevel}
        else:
            extra["exc_info_on_loglevel"] = exc_info_on_loglevel

        if sys.version_info < (3, ):
            LOGGING_LOGGER_CLASS._log(self,
                                      level,
                                      msg,
                                      args,
                                      exc_info=exc_info,
                                      extra=extra)
        elif sys.version_info < (3, 8):
            LOGGING_LOGGER_CLASS._log(
                self,
                level,
                msg,
                args,
                exc_info=exc_info,
                extra=extra,
                stack_info=stack_info,
            )
        else:
            LOGGING_LOGGER_CLASS._log(
                self,
                level,
                msg,
                args,
                exc_info=exc_info,
                extra=extra,
                stack_info=stack_info,
                stacklevel=stacklevel,
            )
Пример #3
0
    def _log(
            self,
            level,
            msg,
            args,
            exc_info=None,
            extra=None,  # pylint: disable=arguments-differ
            stack_info=False,
            stacklevel=1,
            exc_info_on_loglevel=None):
        if extra is None:
            extra = {}

        # pylint: disable=no-member
        current_jid = RequestContext.current.get('data', {}).get('jid', None)
        log_fmt_jid = RequestContext.current.get('opts',
                                                 {}).get('log_fmt_jid', None)
        # pylint: enable=no-member

        if current_jid is not None:
            extra['jid'] = current_jid

        if log_fmt_jid is not None:
            extra['log_fmt_jid'] = log_fmt_jid

        # If both exc_info and exc_info_on_loglevel are both passed, let's fail
        if exc_info and exc_info_on_loglevel:
            raise LoggingRuntimeError(
                'Only one of \'exc_info\' and \'exc_info_on_loglevel\' is '
                'permitted')
        if exc_info_on_loglevel is not None:
            if isinstance(exc_info_on_loglevel, six.string_types):
                exc_info_on_loglevel = LOG_LEVELS.get(exc_info_on_loglevel,
                                                      logging.ERROR)
            elif not isinstance(exc_info_on_loglevel, int):
                raise RuntimeError(
                    'The value of \'exc_info_on_loglevel\' needs to be a '
                    'logging level or a logging level name, not \'{}\''.format(
                        exc_info_on_loglevel))
        if extra is None:
            extra = {'exc_info_on_loglevel': exc_info_on_loglevel}
        else:
            extra['exc_info_on_loglevel'] = exc_info_on_loglevel

        try:
            logging._acquireLock()
            if sys.version_info < (3, ):
                LOGGING_LOGGER_CLASS._log(self,
                                          level,
                                          msg,
                                          args,
                                          exc_info=exc_info,
                                          extra=extra)
            elif sys.version_info < (3, 8):
                LOGGING_LOGGER_CLASS._log(self,
                                          level,
                                          msg,
                                          args,
                                          exc_info=exc_info,
                                          extra=extra,
                                          stack_info=stack_info)
            else:
                LOGGING_LOGGER_CLASS._log(self,
                                          level,
                                          msg,
                                          args,
                                          exc_info=exc_info,
                                          extra=extra,
                                          stack_info=stack_info,
                                          stacklevel=stacklevel)
        except:
            pass
        finally:
            logging._releaseLock()