Rework install log (#3550)

This commit is contained in:
codefiles 2025-05-30 09:08:02 -04:00 committed by GitHub
parent a5d995b546
commit 1dccfe6c33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 51 additions and 57 deletions

View File

@ -22,9 +22,8 @@ from archinstall.lib.models.network_configuration import NetworkConfiguration
from archinstall.lib.models.packages import Repository from archinstall.lib.models.packages import Repository
from archinstall.lib.models.profile_model import ProfileConfiguration from archinstall.lib.models.profile_model import ProfileConfiguration
from archinstall.lib.models.users import Password, User from archinstall.lib.models.users import Password, User
from archinstall.lib.output import debug, error, warn from archinstall.lib.output import debug, error, logger, warn
from archinstall.lib.plugins import load_plugin from archinstall.lib.plugins import load_plugin
from archinstall.lib.storage import storage
from archinstall.lib.translationhandler import Language, tr, translation_handler from archinstall.lib.translationhandler import Language, tr, translation_handler
from archinstall.lib.utils.util import get_password from archinstall.lib.utils.util import get_password
from archinstall.tui.curses_menu import Tui from archinstall.tui.curses_menu import Tui
@ -389,7 +388,7 @@ class ArchConfigHandler:
args.silent = False args.silent = False
if args.debug: if args.debug:
warn(f'Warning: --debug mode will write certain credentials to {storage["LOG_PATH"]}/{storage["LOG_FILE"]}!') warn(f'Warning: --debug mode will write certain credentials to {logger.path}!')
if args.plugin: if args.plugin:
plugin_path = Path(args.plugin) plugin_path = Path(args.plugin)

View File

@ -12,8 +12,7 @@ from archinstall.tui.types import Alignment, FrameProperties, Orientation, Previ
from .args import ArchConfig from .args import ArchConfig
from .crypt import encrypt from .crypt import encrypt
from .general import JSON, UNSAFE_JSON from .general import JSON, UNSAFE_JSON
from .output import debug, warn from .output import debug, logger, warn
from .storage import storage
from .utils.util import get_password, prompt_dir from .utils.util import get_password, prompt_dir
@ -29,7 +28,7 @@ class ConfigurationOutput:
""" """
self._config = config self._config = config
self._default_save_path = storage.get('LOG_PATH', Path('.')) self._default_save_path = logger.directory
self._user_config_file = Path('user_configuration.json') self._user_config_file = Path('user_configuration.json')
self._user_creds_file = Path('user_credentials.json') self._user_creds_file = Path('user_credentials.json')

View File

@ -19,8 +19,7 @@ from shutil import which
from typing import Any, override from typing import Any, override
from .exceptions import RequirementError, SysCallError from .exceptions import RequirementError, SysCallError
from .output import debug, error from .output import debug, error, logger
from .storage import storage
# https://stackoverflow.com/a/43627833/929999 # https://stackoverflow.com/a/43627833/929999
_VT100_ESCAPE_REGEX = r'\x1B\[[?0-9;]*[a-zA-Z]' _VT100_ESCAPE_REGEX = r'\x1B\[[?0-9;]*[a-zA-Z]'
@ -415,7 +414,7 @@ class SysCommand:
def _append_log(file: str, content: str) -> None: def _append_log(file: str, content: str) -> None:
path = Path(f'{storage["LOG_PATH"]}/{file}') path = logger.directory / file
change_perm = not path.exists() change_perm = not path.exists()

View File

@ -44,7 +44,7 @@ from .models.locale import LocaleConfiguration
from .models.mirrors import MirrorConfiguration from .models.mirrors import MirrorConfiguration
from .models.network_configuration import Nic from .models.network_configuration import Nic
from .models.users import User from .models.users import User
from .output import debug, error, info, log, warn from .output import debug, error, info, log, logger, warn
from .pacman import Pacman from .pacman import Pacman
from .pacman.config import PacmanConfig from .pacman.config import PacmanConfig
from .plugins import plugins from .plugins import plugins
@ -132,13 +132,12 @@ class Installer:
# We avoid printing /mnt/<log path> because that might confuse people if they note it down # We avoid printing /mnt/<log path> because that might confuse people if they note it down
# and then reboot, and a identical log file will be found in the ISO medium anyway. # and then reboot, and a identical log file will be found in the ISO medium anyway.
log_file = os.path.join(storage['LOG_PATH'], storage['LOG_FILE']) Tui.print(str(tr('[!] A log file has been created here: {}').format(logger.path)))
Tui.print(str(tr('[!] A log file has been created here: {}').format(log_file)))
Tui.print(tr('Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues')) Tui.print(tr('Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues'))
raise exc_val raise exc_val
if not (missing_steps := self.post_install_check()): if not (missing_steps := self.post_install_check()):
msg = f'Installation completed without any errors.\nLog files temporarily available at {storage["LOG_PATH"]}.\nYou may reboot when ready.\n' msg = f'Installation completed without any errors.\nLog files temporarily available at {logger.directory}.\nYou may reboot when ready.\n'
log(msg, fg='green') log(msg, fg='green')
self.sync_log_to_install_medium() self.sync_log_to_install_medium()
return True return True
@ -148,7 +147,7 @@ class Installer:
for step in missing_steps: for step in missing_steps:
warn(f' - {step}') warn(f' - {step}')
warn(f'Detailed error logs can be found at: {storage["LOG_PATH"]}') warn(f'Detailed error logs can be found at: {logger.directory}')
warn('Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues') warn('Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues')
self.sync_log_to_install_medium() self.sync_log_to_install_medium()
@ -456,13 +455,12 @@ class Installer:
# Copy over the install log (if there is one) to the install medium if # Copy over the install log (if there is one) to the install medium if
# at least the base has been strapped in, otherwise we won't have a filesystem/structure to copy to. # at least the base has been strapped in, otherwise we won't have a filesystem/structure to copy to.
if self._helper_flags.get('base-strapped', False) is True: if self._helper_flags.get('base-strapped', False) is True:
if filename := storage.get('LOG_FILE', None): absolute_logfile = logger.path
absolute_logfile = os.path.join(storage.get('LOG_PATH', './'), filename)
if not os.path.isdir(f'{self.target}/{os.path.dirname(absolute_logfile)}'): if not os.path.isdir(f'{self.target}/{os.path.dirname(absolute_logfile)}'):
os.makedirs(f'{self.target}/{os.path.dirname(absolute_logfile)}') os.makedirs(f'{self.target}/{os.path.dirname(absolute_logfile)}')
shutil.copy2(absolute_logfile, f'{self.target}/{absolute_logfile}') shutil.copy2(absolute_logfile, f'{self.target}/{absolute_logfile}')
return True return True

View File

@ -8,7 +8,6 @@ from enum import Enum
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
from .storage import storage
from .utils.unicode import unicode_ljust, unicode_rjust from .utils.unicode import unicode_ljust, unicode_rjust
if TYPE_CHECKING: if TYPE_CHECKING:
@ -147,30 +146,46 @@ class Journald:
log_adapter.log(level, message) log_adapter.log(level, message)
def _check_log_permissions() -> None: class Logger:
filename = storage.get('LOG_FILE', None) def __init__(self, path: Path = Path('/var/log/archinstall')) -> None:
log_dir = storage.get('LOG_PATH', Path('./')) self._path = path
if not filename: @property
raise ValueError('No log file name defined') def path(self) -> Path:
return self._path / 'install.log'
log_file = log_dir / filename @property
def directory(self) -> Path:
return self._path
try: def _check_permissions(self) -> None:
log_dir.mkdir(exist_ok=True, parents=True) log_file = self.path
log_file.touch(exist_ok=True)
with log_file.open('a') as fp: try:
fp.write('') self._path.mkdir(exist_ok=True, parents=True)
except PermissionError: log_file.touch(exist_ok=True)
# Fallback to creating the log file in the current folder
fallback_dir = Path('./').absolute()
fallback_log_file = fallback_dir / filename
fallback_log_file.touch(exist_ok=True) with log_file.open('a') as f:
f.write('')
except PermissionError:
# Fallback to creating the log file in the current folder
logger._path = Path('./').absolute()
storage['LOG_PATH'] = fallback_dir warn(
warn(f'Not enough permission to place log file at {log_file}, creating it in {fallback_log_file} instead') f'Not enough permission to place log file at {log_file},',
'creating it in {logger.path} instead'
)
def log(self, level: int, content: str) -> None:
self._check_permissions()
with self.path.open('a') as f:
ts = _timestamp()
level_name = logging.getLevelName(level)
f.write(f'[{ts}] - {level_name} - {content}\n')
logger = Logger()
def _supports_color() -> bool: def _supports_color() -> bool:
@ -309,25 +324,15 @@ def log(
reset: bool = False, reset: bool = False,
font: list[Font] = [], font: list[Font] = [],
) -> None: ) -> None:
# leave this check here as we need to setup the logging text = ' '.join([str(x) for x in msgs])
# right from the beginning when the modules are loaded
_check_log_permissions()
text = orig_string = ' '.join([str(x) for x in msgs]) logger.log(level, text)
# Attempt to colorize the output if supported # Attempt to colorize the output if supported
# Insert default colors and override with **kwargs # Insert default colors and override with **kwargs
if _supports_color(): if _supports_color():
text = _stylize_output(text, fg, bg, reset, font) text = _stylize_output(text, fg, bg, reset, font)
log_file = storage['LOG_PATH'] / storage['LOG_FILE']
with log_file.open('a') as fp:
ts = _timestamp()
level_name = logging.getLevelName(level)
out = f'[{ts}] - {level_name} - {orig_string}\n'
fp.write(out)
Journald.log(text, level=level) Journald.log(text, level=level)
if level != logging.DEBUG: if level != logging.DEBUG:

View File

@ -5,7 +5,6 @@
# (4. Added the ~/.config directory as an additional option for future reasons) # (4. Added the ~/.config directory as an additional option for future reasons)
# #
# And Keeping this in dict ensures that variables are shared across imports. # And Keeping this in dict ensures that variables are shared across imports.
from pathlib import Path
from typing import TYPE_CHECKING, NotRequired, TypedDict from typing import TYPE_CHECKING, NotRequired, TypedDict
if TYPE_CHECKING: if TYPE_CHECKING:
@ -14,13 +13,8 @@ if TYPE_CHECKING:
class _StorageDict(TypedDict): class _StorageDict(TypedDict):
LOG_FILE: Path
LOG_PATH: Path
active_boot: NotRequired['Boot | None'] active_boot: NotRequired['Boot | None']
installation_session: NotRequired['Installer'] installation_session: NotRequired['Installer']
storage: _StorageDict = { storage: _StorageDict = {}
'LOG_FILE': Path('install.log'),
'LOG_PATH': Path('/var/log/archinstall'),
}