我如何知道 SLES11 系统上的系统启动时间?

How can I know system boot time on SLES11 systems?

我正在尝试计算在 SUSE linux 系统(SLES11 和 SLES12 系统)上重启所花费的时间,如下所示:

reboot_time = end_time - start_time

where
    start_time is "time at which reboot command is triggered"
    end_time   is "time at which system is ready after booting process, finishing up startup and userspace programs" OR "time when login is prompted soon after reboot"

我能知道start_time。但无法知道 SLES11 系统(init.d SysV 版本)的时间 end_time。对于 SLES12(systemd 初始化),systemd-analyze 提供了所需的信息,但我无法为 init.d 系统找到可靠的方法。在 SysV 初始化系统或 SLES11 上是否有任何类似的替代方案可以让我花在引导上的时间(启动内核,完成 运行 启动程序并完成用户空间初始化)?

可能有点hacky,但你可以添加一个最后执行的脚本,即/etc/init.d/K99WriteShutdownTime(其中K表示关闭脚本,数字越大,执行越晚)。

脚本可能包含类似

的内容
#!/usr/bin/env bash

timestamp_logfile="/var/log/shutdown_timestamp"
date +"%s" > "$timestamp_logfile"

然后你可以有另一个脚本,即 /etc/init.d/S99WriteGetTime like

#!/usr/bin/env bash

timestamp_logfile="/var/log/shutdown_timestamp"
boot_time_logfile="/var/log/boot_time.log"

if [ -f "$logfile" ]; then
   boot_timestamp=$(date +"%s")
   shutdown_timestamp=$(cat "$timestamp_logfile")
   delta=$((boot_timestamp - shutdown_timestamp))
   echo "$(date +"%Y-%m-%d %H:%M:%S"): It took $delta seconds to boot $(hostname)" >> boot_time_logfile
else
   echo "$(date +"%Y-%m-%d %H:%M:%S"): No prior shutdown time detected. Cannot compute boot time" >> boot_time_logfile
fi

您可能会使用以下命令获得(非精确的第二个)上次关机日期

last -x | egrep "shutdown|reboot" | head -1 | awk '{print " "" "" "}'

last -x 可以为您提供系统启动/重启和关闭时间,以及自系统创建以来的所有系统启动日志。问题是它不显示年份,而且只有分钟分辨率。

last读取各种log二进制日志文件(/var/log/wtmputmp和btmp) 您可以使用 utmpdump 实用程序获取具有更精确时间戳的关机和重启时间戳

utmpdump /var/log/wtmp | egrep "shutdown|reboot" | awk -F '[' '{print }' | sed 's/ ]//g'

最终,您可以使用以下(长得可怕)一行获取从关机到重启所经过的时间:

echo "System $(hostname) took $(($(date -d "$(LC_ALL=C utmpdump /var/log/wtmp 2>/dev/null| grep "reboot" | grep -Eo '[[:alpha:]]{3} [[:alpha:]]{3} [[:digit:]]{2} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2} [[:digit:]]{4}|[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}T[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}' | tail -1)" "+%s") - $(date -d "$(LC_ALL=C utmpdump /var/log/wtmp 2>/dev/null| grep "shutdown" | grep -Eo '[[:alpha:]]{3} [[:alpha:]]{3} [[:digit:]]{2} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2} [[:digit:]]{4}|[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}T[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}' | tail -1)" "+%s"))) to reboot"

解释:

  • LC_ALL=C 使以下命令的输出未本地化
  • utmpdump /var/log/wtmp 2>/dev/null 将列出所有事件,而 2>/dev/null 将重定向错误和不需要的输出。
  • grep "reboot"grep "shutdown" 将过滤 utmpdump 输出以仅显示重启或关机事件
  • '[[:alpha:]]{3} [[:alpha:]]{3} [[:digit:]]{2} [[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2} [[:digit:]]{4}' 是一个正则表达式,用于捕获看起来像“Fri Aug 02 00:01:27 2019”的非本地化日期字符串,这些在 CentOS
  • 上使用
  • '[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}T[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}' 是一个正则表达式,用于捕获看起来像“2021-08-17T10:11:03”的 ISO 8601 日期字符串,这些在 Debian
  • 上使用
  • date -d "datestring" 将日期转换为纪元
  • $(command)是命令的输出
  • $((x - y)) 用于数学运算(例如减去两个 epoch)

您还可以使用 utmpdump 的输出来检索登录时间戳。 请注意 shutdown/reboot 周期不同于 shutdown/system 引导周期,因此必须根据您的使用情况调整命令。 另请注意,任何强制系统重置都不会包含在日志中,因此会提供虚假值。

在初始化系统(如SLES11)上,只有在启动所有启动程序后才会向用户显示登录提示。所以我们可以依靠从 wtmp 文件获取登录提示时间(如用户“Orsiris de Jong”所建议的那样)。

下面是 python 脚本,用于在上次重启后获取登录提示时间。

#!/usr/bin/env python3
import subprocess
import re
from datetime import datetime
from typing import List


class UnableToFindLoginTimeError(Exception):
    pass


class UnexpectedCommandException(Exception):
    pass


class CommandResult:
    def __init__(self, command, stdout, stderr, exit_code):
        self.command = command
        self.stdout = stdout
        self.stderr = stderr
        self.exit_code = exit_code

    def validate(self, check_stderr=True, check_exit_code=True):
        if (check_stderr and self.stderr) or (check_exit_code and self.exit_code != 0):
            raise UnexpectedCommandException('Unexpected command result')


def run_command(command, timeout=600):
    completed_process = subprocess.run(
        command,
        encoding='utf-8',
        timeout=timeout,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        shell=True,
        errors='ignore',
    )
    command_result = CommandResult(
        stdout=completed_process.stdout.strip(),
        stderr=completed_process.stderr.strip(),
        exit_code=completed_process.returncode,
        command=command,
    )
    return command_result


def _extract_data_in_square_brackets(row: str) -> List[str]:
    """
    Extract data within square brackets from a string.

    Example:
    '[One] [Two ] [Three   ] [Wed Aug 25 09:21:59 2021 UTC]'
    returns
    ['One', 'Two', 'Three', 'Wed Aug 25 09:21:59 2021 UTC']
    """
    regex = r'\[(.*?)\]'
    columns_list = re.findall(regex, row)
    return [item.strip() for item in columns_list]


def convert_datetime_string_to_epoch(datetime_str: str) -> int:
    """
    Run following command to automatically parse datetime string (in any valid format) into epoch:
    # date -d '{datetime_str}' +%s

    Note: The Unix epoch is the number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT).
    At any point of time, epoch will be same throughout all time zones.

    Example: epoch for "Fri Sep  3 11:08:09 UTC 2021" will be 1630667289.
    """
    command_result = run_command(f"date -d '{datetime_str}' +%s")
    command_result.validate()
    epoch = round(float(command_result.stdout.strip()))
    return epoch


def get_login_time_from_wtmp() -> datetime:
    """
    Read through /var/log/wtmp binary file using utmpdump
    and find least LOGIN time after last reboot.

    wtmp gives historical data of utmp (gives information about user logins, logouts, system boot etc.).
    In case of failed logins, we see multiple entries for same tty in the output.
    In such case, we get first occurred entry after last reboot since that is when
    startup processes have been completed and welcome screen appeared.

    Sample:
    -------
    Output:
    [2] [00000] [~~  ] [reboot  ] [~           ] [3.10.0-957.12.2.el7.x86_64] [0.0.0.0        ] [Mon Aug 16 06:21:06 2021 UTC]
    [6] [01828] [tty1] [LOGIN   ] [tty1        ] [                    ] [0.0.0.0        ] [Mon Aug 16 06:21:26 2021 UTC]
    [2] [00000] [~~  ] [reboot  ] [~           ] [3.10.0-957.12.2.el7.x86_64] [0.0.0.0        ] [Wed Aug 25 09:21:34 2021 UTC]
    [6] [01815] [tty1] [LOGIN   ] [tty1        ] [                    ] [0.0.0.0        ] [Wed Aug 25 09:21:59 2021 UTC]

    Returns: "Wed Aug 25 09:21:59 2021 UTC" as datetime object
    """
    command_result = run_command(f'utmpdump /var/log/wtmp | grep -e reboot -e LOGIN')
    command_result.validate(check_stderr=False)
    output = command_result.stdout
    list_of_login_epochs = []
    # read output in reverse order
    for line in output.splitlines()[::-1]:
        if 'reboot' in line:
            # exit loop since we dont require data before last reboot
            break
        items = _extract_data_in_square_brackets(line)
        if 'LOGIN' in items:
            login_time_str = items[-1].strip()
            epoch = convert_datetime_string_to_epoch(login_time_str)
            list_of_login_epochs.append(epoch)

    if not list_of_login_epochs:
        raise UnableToFindLoginTimeError()

    # collect least login time out of all since we need time at which login was prompted first.
    least_epoch = min(list_of_login_epochs)
    login_datetime_object = datetime.utcfromtimestamp(round(least_epoch))
    return login_datetime_object


if __name__ == '__main__':
    print(
        f'Login screen was displayed to user after last reboot at {get_login_time_from_wtmp()} UTC'
    )