Rework install log (#3550)
This commit is contained in:
parent
a5d995b546
commit
1dccfe6c33
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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'),
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue