如何将 dmesg 时间戳转换为自定义日期格式?

我试图理解 dmesg时间戳,发现很难将其转换为 java 日期/自定义日期格式。

Dmesg 日志示例:

[14614.647880] airo(eth1): link lost (missed beacons)

那么如何将 14614.647880转换为标准日期呢?

215233 次浏览

理解 dmesg时间戳非常简单: 内核启动后的时间以秒为单位。因此,有了启动时间(uptime) ,您可以将秒数加起来,并以您喜欢的任何格式显示它们。

或者,您可以使用 dmesg-T命令行选项并解析人类可读的格式。

来自 手册:

-T, --ctime
Print human readable timestamps. The timestamp could be inaccurate!


The time source used for the logs is not updated after system SUSPEND/RESUME.

您需要在/proc/stat 中引用“ btime”,这是系统最近一次引导时的 Unix 纪元时间。然后,您可以基于该系统启动时间,然后添加 dmesg 中给出的耗时秒数,以计算每个事件的时间戳。

对于较老的 Linux 发行版,另一种选择是使用包装脚本,例如在 Perl 或 Python 中。

点击这里查看解决方案:

Http://linuxaria.com/article/how-to-make-dmesg-timestamp-human-readable?lang=en Http://jmorano.moretrix.com/2012/03/dmesg-human-readable-timestamps/

博士答案的帮助下,我编写了一个变通方法,将转换放入您的。巴希尔。如果没有任何时间戳或者已经正确的时间戳,它不会破坏任何东西。

dmesg_with_human_timestamps () {
$(type -P dmesg) "$@" | perl -w -e 'use strict;
my ($uptime) = do { local @ARGV="/proc/uptime";<>}; ($uptime) = ($uptime =~ /^(\d+)\./);
foreach my $line (<>) {
printf( ($line=~/^\[\s*(\d+)\.\d+\](.+)/) ? ( "[%s]%s\n", scalar localtime(time - $uptime + $1), $2 ) : $line )
}'
}
alias dmesg=dmesg_with_human_timestamps

另外,对 dmesg 时间戳转换逻辑以及如何在没有时间戳的情况下启用时间戳也有很好的了解: Https://supportcenter.checkpoint.com/supportcenter/portal?eventsubmit_dogoviewsolutiondetails=&solutionid=sk92677

在 dmesg 的最新版本中,只需调用 dmesg -T即可。

如果你没有 dmesg-T选项,例如在 Andoid 上,你可以使用 busybox版本。以下内容还解决了其他一些问题:

  1. [0.0000]格式前面有一些看起来像是放错了颜色信息的东西,前缀如 <6>
  2. 从浮点数生成整数。

它的灵感来自 这篇博文

#!/bin/sh
# Translate dmesg timestamps to human readable format


# uptime in seconds
uptime=$(cut -d " " -f 1 /proc/uptime)


# remove fraction
uptime=$(echo $uptime | cut -d "." -f1)


# run only if timestamps are enabled
if [ "Y" = "$(cat /sys/module/printk/parameters/time)" ]; then
dmesg | sed "s/[^\[]*\[/\[/" | sed "s/^\[[ ]*\?\([0-9.]*\)\] \(.*\)/\\1 \\2/" | while read timestamp message; do
timestamp=$(echo $timestamp | cut -d "." -f1)
ts1=$(( $(busybox date +%s) - $uptime + $timestamp ))
ts2=$(busybox date -d "@${ts1}")
printf "[%s] %s\n" "$ts2" "$message"
done
else
echo "Timestamps are disabled (/sys/module/printk/parameters/time)"
fi

但是请注意,这个实现相当慢。

因此 KevZero要求一个不那么笨拙的解决方案,所以我想出了以下方案:

sed -r 's#^\[([0-9]+\.[0-9]+)\](.*)#echo -n "[";echo -n $(date --date="@$(echo "$(grep btime /proc/stat|cut -d " " -f 2)+\1" | bc)" +"%c");echo -n "]";echo -n "\2"#e'

这里有一个例子:

$ dmesg|tail | sed -r 's#^\[([0-9]+\.[0-9]+)\](.*)#echo -n "[";echo -n $(date --date="@$(echo "$(grep btime /proc/stat|cut -d " " -f 2)+\1" | bc)" +"%c");echo -n "]";echo -n "\2"#e'
[2015-12-09T04:29:20 COT] cfg80211:   (57240000 KHz - 63720000 KHz @ 2160000 KHz), (N/A, 0 mBm), (N/A)
[2015-12-09T04:29:23 COT] wlp3s0: authenticate with dc:9f:db:92:d3:07
[2015-12-09T04:29:23 COT] wlp3s0: send auth to dc:9f:db:92:d3:07 (try 1/3)
[2015-12-09T04:29:23 COT] wlp3s0: authenticated
[2015-12-09T04:29:23 COT] wlp3s0: associate with dc:9f:db:92:d3:07 (try 1/3)
[2015-12-09T04:29:23 COT] wlp3s0: RX AssocResp from dc:9f:db:92:d3:07 (capab=0x431 status=0 aid=6)
[2015-12-09T04:29:23 COT] wlp3s0: associated
[2015-12-09T04:29:56 COT] thinkpad_acpi: EC reports that Thermal Table has changed
[2015-12-09T04:29:59 COT] i915 0000:00:02.0: BAR 6: [??? 0x00000000 flags 0x2] has bogus alignment
[2015-12-09T05:00:52 COT] thinkpad_acpi: EC reports that Thermal Table has changed

如果您希望它执行得更好一些,那么将 proc 的时间戳放到一个变量中:)

对于没有“ dmesg-T”的系统,比如 RHEL/CentOS 6,我喜欢 Lucas-Cimon之前提供的“ dmesg _ with _ human _ time戳”函数。不过,我们的一些大正常运行时间的盒子有一点麻烦。原来 dmesg 中的内核时间戳是从单个 CPU 保持的正常运行时间值派生出来的。随着时间的推移,这与实时时钟不同步。因此,对于最近的 dmesg 条目,最精确的转换将基于 CPU 时钟,而不是/proc/uptime。例如,在这里的一个 CentOS 6.6框中:

# grep "\.clock" /proc/sched_debug  | head -1
.clock                         : 32103895072.444568
# uptime
15:54:05 up 371 days, 19:09,  4 users,  load average: 3.41, 3.62, 3.57
# cat /proc/uptime
32123362.57 638648955.00

考虑到 CPU 的正常运行时间以毫秒为单位,这里有将近5个半小时的偏移量。因此,我修改了脚本,并在此过程中将其转换为原生 bash:

dmesg_with_human_timestamps () {
FORMAT="%a %b %d %H:%M:%S %Y"


now=$(date +%s)
cputime_line=$(grep -m1 "\.clock" /proc/sched_debug)


if [[ $cputime_line =~ [^0-9]*([0-9]*).* ]]; then
cputime=$((BASH_REMATCH[1] / 1000))
fi


dmesg | while IFS= read -r line; do
if [[ $line =~ ^\[\ *([0-9]+)\.[0-9]+\]\ (.*) ]]; then
stamp=$((now-cputime+BASH_REMATCH[1]))
echo "[$(date +"${FORMAT}" --date=@${stamp})] ${BASH_REMATCH[2]}"
else
echo "$line"
fi
done
}


alias dmesgt=dmesg_with_human_timestamps

其他答案似乎没有提到的一个警告是,时间是由 dmesg 不考虑任何睡眠/暂停时间显示的。因此,有些情况下,通常使用 dmesg -T的答案不起作用,并显示了一个完全错误的时间。

解决这种情况的方法是在已知的时间向内核日志写入一些内容,然后使用该条目作为引用来计算其他时间。显然,它只能在最后一次挂起之后工作。

因此,要显示机器上最近的条目的正确时间,这些条目可能自上次启动以来已经被暂停,可以使用我的 另一个答案中的类似内容:

# write current time to kernel ring buffer so it appears in dmesg output
echo "timecheck: $(date +%s) = $(date +%F_%T)" | sudo tee /dev/kmsg


# use our "timecheck" entry to get the difference
# between the dmesg timestamp and real time
offset=$(dmesg | grep timecheck | tail -1 \
| perl -nle '($t1,$t2)=/^.(\d+)\S+ timecheck: (\d+)/; print $t2-$t1')


# pipe dmesg output through a Perl snippet to
# convert it's timestamp to correct readable times
dmesg | tail \
| perl -pe 'BEGIN{$offset=shift} s/^\[(\d+)\S+/localtime($1+$offset)/e' $offset

dmesg -T可能显示错误的时间,不同于 date命令输出。

变通方法是 journal ctl with-k,—— dmesg. 我使用的是-k,因为它比较短:

journalctl -k

它将只显示内核消息和正确的时间。

只显示与短语匹配的内核行:

journalctl -kg phrase