我有一个日志文件,我需要在添加每一行时附加时间戳。因此,我正在寻找将时间戳附加到日志行中的每个条目并且可以作为 cron 作业运行的脚本。
答案1
一般方法
$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log
怎么运行的:
cat
读取调用的文件input.log
并将其打印到其标准输出流。通常标准输出连接到终端,但是这个小脚本包含
|
所以 shell 将 的标准输出重定向cat
到 的标准输入sed
。sed
读取数据(cat
生成数据时),处理数据(根据-e
选项提供的脚本),然后将其打印到标准输出。该脚本"s/^/$(date -R) /"
意味着将每个行的开头替换为date -R
命令生成的文本(替换命令的一般结构是:)s/pattern/replace/
。然后根据
>>
bash
将输出重定向sed
到一个名为的文件output.log
(>
表示替换文件内容并>>
表示追加到末尾)。
问题是$(date -R)
运行脚本时评估一次,因此它将插入当前的每行开头的时间戳。当前时间戳可能与生成消息的时刻相差甚远。为了避免这种情况,您必须在消息写入文件时对其进行处理,而不是使用 cron 作业。
先进先出
上面描述的标准流重定向称为管道。您不仅可以|
在脚本中的命令之间重定向它,还可以通过先进先出文件(又名命名管道)。一个程序将写入文件,另一个程序将读取数据并在第一次发送时接收它。
选一个例子:
$ mkfifo foo.log.fifo
$ while true; do cat foo.log.fifo | sed -e "s/^/$(date -R) /" >> foo.log; done;
# have to open a second terminal at this point
$ echo "foo" > foo.log.fifo
$ echo "bar" > foo.log.fifo
$ echo "baz" > foo.log.fifo
$ cat foo.log
Tue, 20 Nov 2012 15:32:56 +0400 foo
Tue, 20 Nov 2012 15:33:27 +0400 bar
Tue, 20 Nov 2012 15:33:30 +0400 baz
怎么运行的:
mkfifo
创建命名管道while true; do sed ... ; done
运行无限循环,并且在每次迭代时都会sed
重定向foo.log.fifo
到其标准输入;sed
等待输入数据的块然后处理收到的消息并将其打印到重定向到的标准输出foo.log
。此时你必须打开一个新的终端窗口,因为循环占用了当前终端。
echo ... > foo.log.fifo
将消息打印到重定向到 fifo 文件的标准输出,并sed
接收它并处理并写入常规文件。
重要的是,fifo 就像任何其他管道一样,如果其一侧未连接到任何进程,则没有任何意义。如果您尝试写入管道,当前进程将堵塞直到有人读取管道另一端的数据。如果你想从管道中读取数据,该过程将堵塞直到有人将数据写入管道。上面示例中的循环sed
不执行任何操作(休眠),直到执行echo
.
对于您的特定情况,您只需将应用程序配置为将日志消息写入 fifo 文件。如果您无法配置它 - 只需删除原始日志文件并创建一个 fifo 文件即可。但请再次注意,如果循环sed
由于某种原因而终止 - 您的程序将在尝试访问write
该文件时被阻止,直到有人read
从 fifo 中访问为止。
好处是当前的当程序将消息写入文件时,时间戳会被评估并附加到消息上。
异步处理tailf
为了使写入日志和处理更加独立,您可以使用两个带有tailf
.应用程序将消息写入原始文件,其他进程读取新行(遵循异步写入)并处理数据并写入第二个文件。
让我们举个例子:
# will occupy current shell
$ tailf -n0 bar.raw.log | while read line; do echo "$(date -R) $line" >> bar.log; done;
$ echo "foo" >> bar.raw.log
$ echo "bar" >> bar.raw.log
$ echo "baz" >> bar.raw.log
$ cat bar.log
Wed, 21 Nov 2012 16:15:33 +0400 foo
Wed, 21 Nov 2012 16:15:36 +0400 bar
Wed, 21 Nov 2012 16:15:39 +0400 baz
怎么运行的:
运行
tailf
将跟随写入的进程bar.raw.log
并将它们打印到重定向到无限循环的标准输出while read ... echo
。此循环执行两个操作:将数据从标准输入读取到名为的缓冲区变量line
,然后将生成的时间戳与以下缓冲数据写入到bar.log
.向 写入一些消息
bar.raw.log
。您必须在单独的终端窗口中执行此操作,因为第一个终端窗口将被占用,tailf
它将跟随写入并完成其工作。非常简单。
优点是如果您终止,您的应用程序不会阻塞tailf
。缺点是不太准确的时间戳和重复的日志文件。
答案2
你可以使用ts
perl 脚本moreutils
:
$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
答案3
修改自德米特里·瓦西利亚诺夫的答案。
在 bash 脚本中,您可以使用时间戳逐行重定向和包装输出。
何时使用:
- 对于 bash 脚本作业,请在主脚本之前插入该行
- 对于非脚本作业,创建脚本来调用程序。
- 对于系统的服务控制,最好使用
tailf
日志文件,如 Dmitry Vasilianov 所说。
这是一个名为的示例foo.sh
:
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "foo"
sleep 1
echo "bar" >&2
sleep 1
echo "foobar"
结果:
$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar
怎么运行的
exec &>
将 stdout 和 stderr 重定向到同一位置>( ... )
通过管道输出到异步内部命令- 其余的工作正如德米特里·瓦西利亚诺夫所解释的那样。
例如:
管道时间戳和日志到文件
#!/bin/bash exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;) echo "some script commands" /path-to/some-thrid-party-programs
或者打印时间戳并记录到标准输出
#!/bin/bash exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line"; done;) echo "some script commands" /path-to/some-thrid-party-programs
然后将它们保存在
/etc/crontab
设置中* * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
答案4
我使用ts
这种方法在错误日志中获取带有时间戳的条目,该脚本用于获取 Cacti 并填充远程主机的统计信息。
为了测试 Cacti,我rand
添加了一些随机值,用于温度图来监控系统温度。
Pushmonstats.sh 是一个脚本,用于收集我的 PC 的系统温度统计数据并将其发送到运行 Cacti 的 Raspberry Pi。前段时间,网络卡住了。我的错误日志中只显示 SSH 超时。不幸的是,该日志中没有时间条目。我不知道如何向日志条目添加时间戳。因此,经过在互联网上的一些搜索后,我偶然发现了这篇文章,这就是我使用ts
.
为了测试它,我使用了一个未知选项rand
。这给了 stderr 一个错误。为了捕获它,我将其重定向到一个临时文件。然后我使用 cat 显示文件的内容并将其通过管道传输到ts
,添加我在这篇文章中找到的时间格式,最后将其记录到错误文件中。然后我清除临时文件的内容,否则我会得到相同错误的重复条目。
定时任务:
* * * * * /home/monusr/bin/pushmonstats.sh 1>> /home/monusr/pushmonstats.log 2> /home/monusr/.err;/bin/cat /home/monusr/.err|/usr/bin/ts %F-%H:%M:%.S 1>> /home/monusr/pushmonstats.err;> /home/monusr/.err
这在我的错误日志中给出了以下内容:
2014-03-22-19:17:53.823720 rand: unknown option -- '-l'
也许这不是一个非常优雅的方法,但它确实有效。我想知道是否有更优雅的方法。