Example #1
0
def test_file_logging() -> None:
    native = Native()
    native.init_rust_logging(
        level=LogLevel.INFO.level,  # Tests assume a log level of INFO
        log_show_rust_3rdparty=False,
        use_color=False,
        show_target=False,
        log_levels_by_target={},
        message_regex_filters=(),
    )
    logger = logging.getLogger("my_file_logger")
    with temporary_dir() as tmpdir:
        setup_logging_to_file(LogLevel.INFO, log_dir=tmpdir)
        log_file = Path(tmpdir, "pants.log")

        cat = "🐈"
        logger.warning("this is a warning")
        logger.info("this is some info")
        logger.debug("this is some debug info")
        logger.info(f"unicode: {cat}")

        loglines = log_file.read_text().splitlines()
        print(loglines)
        assert len(loglines) == 3
        assert "[WARN] this is a warning" in loglines[0]
        assert "[INFO] this is some info" in loglines[1]
        assert f"[INFO] unicode: {cat}" in loglines[2]
Example #2
0
    def _pantsd_logging(self) -> Iterator[None]:
        """A context manager that runs with pantsd logging.

        Asserts that stdio (represented by file handles 0, 1, 2) is closed to ensure that we can
        safely reuse those fd numbers.
        """

        # Ensure that stdio is closed so that we can safely reuse those file descriptors.
        for fd in (0, 1, 2):
            try:
                os.fdopen(fd)
                raise AssertionError(f"pantsd logging cannot initialize while stdio is open: {fd}")
            except OSError:
                pass

        # Redirect stdio to /dev/null for the rest of the run to reserve those file descriptors.
        with stdio_as(stdin_fd=-1, stdout_fd=-1, stderr_fd=-1):
            # Reinitialize logging for the daemon context.
            global_options = self._bootstrap_options.for_global_scope()
            setup_logging(global_options, stderr_logging=False)

            log_dir = os.path.join(self._work_dir, self.name)
            setup_logging_to_file(global_options.level, log_dir=log_dir, log_filename=self.LOG_NAME)

            self._logger.debug("Logging reinitialized in pantsd context")
            yield
Example #3
0
    def _pantsd_logging(self) -> Iterator[IO[str]]:
        """A context manager that runs with pantsd logging.

        Asserts that stdio (represented by file handles 0, 1, 2) is closed to ensure that we can
        safely reuse those fd numbers.
        """

        # Ensure that stdio is closed so that we can safely reuse those file descriptors.
        for fd in (0, 1, 2):
            try:
                os.fdopen(fd)
                raise AssertionError(
                    f"pantsd logging cannot initialize while stdio is open: {fd}"
                )
            except OSError:
                pass

        # Redirect stdio to /dev/null for the rest of the run, to reserve those file descriptors
        # for further forks.
        with stdio_as(stdin_fd=-1, stdout_fd=-1, stderr_fd=-1):
            # Reinitialize logging for the daemon context.
            init_rust_logger(self._log_level, self._log_show_rust_3rdparty)
            # We can't statically prove it, but we won't execute `launch()` (which
            # calls `run_sync` which calls `_pantsd_logging`) unless PantsDaemon
            # is launched with full_init=True. If PantsdDaemon is launched with
            # full_init=True, we can guarantee self._native and self._bootstrap_options
            # are non-None.
            native = cast(Native, self._native)
            bootstrap_options = cast(OptionValueContainer,
                                     self._bootstrap_options)

            level = self._log_level
            ignores = bootstrap_options.for_global_scope(
            ).ignore_pants_warnings
            clear_previous_loggers()
            setup_logging_to_stderr(level, warnings_filter_regexes=ignores)
            log_dir = os.path.join(self._work_dir, self.name)
            log_handler = setup_logging_to_file(
                level,
                log_dir=log_dir,
                log_filename=self.LOG_NAME,
                warnings_filter_regexes=ignores)

            native.override_thread_logging_destination_to_just_pantsd()

            # Do a python-level redirect of stdout/stderr, which will not disturb `0,1,2`.
            # TODO: Consider giving these pipes/actual fds, in order to make them "deep" replacements
            # for `1,2`, and allow them to be used via `stdio_as`.
            sys.stdout = _LoggerStream(logging.getLogger(), logging.INFO,
                                       log_handler)  # type: ignore[assignment]
            sys.stderr = _LoggerStream(logging.getLogger(), logging.WARN,
                                       log_handler)  # type: ignore[assignment]

            self._logger.debug("logging initialized")
            yield log_handler.stream
Example #4
0
 def logger(
         self, log_level: LogLevel
 ) -> Iterator[Tuple[Logger, NativeHandler, Path]]:
     native = self.scheduler._scheduler._native
     # TODO(gregorys) - if this line isn't here this test fails with no stdout. Figure out why.
     print(f"Native: {native}")
     logger = logging.getLogger("my_file_logger")
     with temporary_dir() as tmpdir:
         handler = setup_logging_to_file(log_level, log_dir=tmpdir)
         log_file = Path(tmpdir, "pants.log")
         yield logger, handler, log_file
Example #5
0
    def _pantsd_logging(self) -> Iterator[IO[str]]:
        """A context manager that runs with pantsd logging.

        Asserts that stdio (represented by file handles 0, 1, 2) is closed to ensure that we can
        safely reuse those fd numbers.
        """

        # Ensure that stdio is closed so that we can safely reuse those file descriptors.
        for fd in (0, 1, 2):
            try:
                os.fdopen(fd)
                raise AssertionError(
                    f"pantsd logging cannot initialize while stdio is open: {fd}"
                )
            except OSError:
                pass

        # Redirect stdio to /dev/null for the rest of the run, to reserve those file descriptors
        # for further forks.
        with stdio_as(stdin_fd=-1, stdout_fd=-1, stderr_fd=-1):
            # Reinitialize logging for the daemon context.
            use_color = self._bootstrap_options.for_global_scope().colors
            init_rust_logger(self._log_level,
                             self._log_show_rust_3rdparty,
                             use_color=use_color)

            level = self._log_level
            ignores = self._bootstrap_options.for_global_scope(
            ).ignore_pants_warnings
            clear_logging_handlers()
            log_dir = os.path.join(self._work_dir, self.name)
            log_handler = setup_logging_to_file(
                level,
                log_dir=log_dir,
                log_filename=self.LOG_NAME,
                warnings_filter_regexes=ignores)

            self._native.override_thread_logging_destination_to_just_pantsd()

            # Do a python-level redirect of stdout/stderr, which will not disturb `0,1,2`.
            # TODO: Consider giving these pipes/actual fds, in order to make them "deep" replacements
            # for `1,2`, and allow them to be used via `stdio_as`.
            sys.stdout = _LoggerStream(logging.getLogger(), logging.INFO,
                                       log_handler)  # type: ignore[assignment]
            sys.stderr = _LoggerStream(logging.getLogger(), logging.WARN,
                                       log_handler)  # type: ignore[assignment]

            self._logger.debug("logging initialized")
            yield log_handler.stream
Example #6
0
    def create(
        cls,
        env: Mapping[str, str],
        options_bootstrapper: OptionsBootstrapper,
        specs: Optional[Specs] = None,
        daemon_graph_session: Optional[LegacyGraphSession] = None,
    ) -> "LocalPantsRunner":
        """Creates a new LocalPantsRunner instance by parsing options.

        :param env: The environment (e.g. os.environ) for this run.
        :param options_bootstrapper: The OptionsBootstrapper instance to reuse.
        :param specs: The specs for this run, i.e. either the address or filesystem specs.
        :param daemon_graph_session: The graph helper for this session.
        """
        build_root = get_buildroot()
        global_options = options_bootstrapper.bootstrap_options.for_global_scope(
        )
        # This works as expected due to the encapsulated_logger in DaemonPantsRunner and
        # we don't have to gate logging setup anymore.

        level = LogLevel.ERROR if getattr(global_options, "quiet",
                                          False) else global_options.level
        ignores = global_options.ignore_pants_warnings
        clear_previous_loggers()
        setup_logging_to_stderr(level, warnings_filter_regexes=ignores)
        log_dir = global_options.logdir
        if log_dir:
            setup_logging_to_file(level,
                                  log_dir=log_dir,
                                  warnings_filter_regexes=ignores)

        options, build_config = LocalPantsRunner.parse_options(
            options_bootstrapper)

        # Option values are usually computed lazily on demand,
        # but command line options are eagerly computed for validation.
        for scope in options.scope_to_flags.keys():
            options.for_scope(scope)

        # Verify configs.
        if global_options.verify_config:
            options.verify_configs(options_bootstrapper.config)

        union_membership = UnionMembership(build_config.union_rules())

        # If we're running with the daemon, we'll be handed a session from the
        # resident graph helper - otherwise initialize a new one here.
        graph_session = (daemon_graph_session
                         if daemon_graph_session else cls._init_graph_session(
                             options_bootstrapper, build_config, options))

        if specs is None:
            global_options = options.for_global_scope()
            specs = SpecsCalculator.create(
                options=options,
                build_root=build_root,
                session=graph_session.scheduler_session,
                exclude_patterns=tuple(global_options.exclude_target_regexp),
                tags=tuple(global_options.tag),
            )

        profile_path = env.get("PANTS_PROFILE")

        return cls(
            build_root=build_root,
            options=options,
            options_bootstrapper=options_bootstrapper,
            build_config=build_config,
            specs=specs,
            graph_session=graph_session,
            union_membership=union_membership,
            is_daemon=daemon_graph_session is not None,
            profile_path=profile_path,
        )