我试图理解dmesg
时间戳,但发现很难将其转换为java日期/自定义日期格式。
dmesg日志示例:
[14614.647880] airo(eth1): link lost (missed beacons)
那么,如何将14614.647880
转换为标准日期呢?
理解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.
在dr answer的帮助下,我编写了一个变通方法,将转换放入.bashrc中。如果您没有任何时间戳或已经正确的时间戳,它不会破坏任何内容。
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]%sn", scalar localtime(time - $uptime + $1), $2 ) : $line )
}'
}
alias dmesg=dmesg_with_human_timestamps
此外,还很好地阅读了dmesg时间戳转换逻辑&如何在没有时间戳的情况下启用时间戳:https://supportcenter.checkpoint.com/supportcenter/portal?eventSubmit_doGoviewsolutiondetails=&溶液d=sk92677
对于没有"dmesg-T"的系统,如RHEL/CNTOS6,我喜欢lucas cimon早期提供的"dmesg_with_human_timestamps"功能。不过,我们的一些盒子有点问题,正常运行时间很长。事实证明,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
所以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
可能显示错误的时间,与date
命令输出不同。
解决方法是带有-k,--dmesg的journalctl。我正在使用-k,因为它更短:
journalctl -k
它将只显示内核消息和正确的时间。
只显示与短语匹配的内核行:
journalctl -kg phrase
在最新版本的dmesg中,您只需调用dmesg -T
即可。
如果dmesg
没有-T
选项,例如在Andoid上,则可以使用busybox
版本。以下也解决了一些其他问题:
[0.0000]
格式前面有一些看起来像错位的颜色信息,前缀像<6>
- 从浮点数生成整数
它的灵感来自于这篇博客文章。
#!/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] %sn" "$ts2" "$message"
done
else
echo "Timestamps are disabled (/sys/module/printk/parameters/time)"
fi
但是,请注意,此实现相当缓慢。
您需要引用/proc/stat中的"btime",这是系统最近一次启动时的Unix epoch时间。然后,您可以基于该系统启动时间,然后添加dmesg中给定的运行秒数,以计算每个事件的时间戳。
对于较旧的Linux发行版,另一种选择是使用包装脚本,例如在Perl或Python中。
查看此处的解决方案:
http://linuxaria.com/article/how-to-make-dmesg-timestamp-human-readable?lang=enhttp://jmorano.moretrix.com/2012/03/dmesg-human-readable-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
我需要在旧的嵌入式Linux设备上查看环形缓冲区日志。该设备没有dmesg的新-t选项,没有bc,甚至没有与上述示例一起使用的sed或bash版本。最后,我使用AWK创建了一个基于runejuhl的sed脚本的日志日期格式脚本。这是脚本,以防对某人有用。
#!/bin/sh
# print kernel ring buffer (dmesg log) with readable times if (dmsg -T not available)
# boot time in seconds
boottime=$(echo $(grep -m1 "btime" /proc/stat) | grep -Eo "[0-9]*$")
lines=$1
if [ -z $lines ]; then
lines=10
fi
#dislpay last x lines of kernel log using awk script instead of sed / bc
dmesg | tail -n $lines | awk 'match($0, /^[ *([0-9]+.[0-9]+)]/)
{ print strftime("[%a %d/%m/%Y %H:%M:%S]",sprintf("%.0f", ('$boottime' + substr($0, RSTART+1, RLENGTH-2)))) substr($0, RLENGTH+1) }'
它读取系统启动的时间并将其存储在一个变量中,然后解析日志,提取每一行的时间戳,将其添加到启动时间中,将其格式化为日期-时间字符串,并将其与该行的其余部分一起写入。
从/proc/stat读取引导时间线(btime),提取时间boottime=$(echo $(grep -m1 "btime" /proc/stat) | grep -Eo "[0-9]*$")
AWK匹配命令用于查找格式为[0.0]的时间戳match($0, /^[ *([0-9]+.[0-9]+)]/)
正则表达式匹配后,RSTART和RLENGTH变量保存匹配字符的起始和长度。使用子字符串命令提取时间戳,忽略[]substr($0, RSTART+1, RLENGTH-2)
然后将时间戳添加到引导时间值上,并使用sprintf("%.0f", timestamp + boottime)
四舍五入为整数
最后,使用strftime("[%a %d/%m/%Y %H:%M:%S]", logdate)
将时间格式化为可读值,并使用另一个子字符串命令打印日志行的其余部分,该子字符串命令采用原始时间戳substr($0, RLENGTH+1)
之后的行的剩余部分
下面的命令可以提供更好的输出
dmesg | awk -F ] '{"cat /proc/uptime | cut -d " " -f 1" | getline st;a=substr( $1,2, length($1) - 1);print strftime("%F %H:%M:%S %Z",systime()-st+a)" -> "$0}'