Как добавить метку времени в журнал скриптов bash?

95

У меня есть постоянно работающий скрипт, который я выводил в файл журнала:

script.sh >> /var/log/logfile

Я хотел бы добавить отметку времени перед каждой строкой, добавляемой в журнал. Подобно:

Sat Sep 10 21:33:06 UTC 2011 The server has booted up.  Hmmph.

Могу ли я использовать джиу-джитсу?

Антоний Блох
источник
2
Смотрите этот квест. serverfault.com/questions/80749/… . Ответ пара будет применяться здесь.
Зоредаче
Для решения awk / gawk см .: stackoverflow.com/questions/21564/…
пользователь
Вот полная реализация ведения журнала для bash: github.com/codeforester/base/blob/master/lib/stdlib.sh
codeforester

Ответы:

88

Вы можете передать вывод скрипта через цикл, который ставит префикс текущей даты и времени:

./script.sh | while IFS= read -r line; do printf '%s %s\n' "$(date)" "$line"; done >>/var/log/logfile

Если вы будете часто этим пользоваться, легко сделать функцию bash для обработки цикла:

adddate() {
    while IFS= read -r line; do
        printf '%s %s\n' "$(date)" "$line";
    done
}

./thisscript.sh | adddate >>/var/log/logfile
./thatscript.sh | adddate >>/var/log/logfile
./theotherscript.sh | adddate >>/var/log/logfile
Гордон Дэвиссон
источник
3
@Nils - это хитрость, чтобы предотвратить readобрезку пробелов в начале и в строке. Он устанавливает IFS (внутренний разделитель полей bash, в основном список пробельных символов) для команды пустым read.
Гордон Дэвиссон
2
... и -r игнорирует escape-символ "\". Это действительно должно работать во всех случаях - отличная сценарий.
Нильс
7
@Nils не полностью пуленепробиваемый, так как некоторые реализации echoинтерпретируют escape-последовательности. Если вы действительно хотите, чтобы он не связывался с контентом (кроме добавления дат), замените echoкоманду наprintf "%s %s\n" "$(date)" "$line"
Гордон Дэвиссон
4
Возможно, вас заинтересует дата / время, совместимая с ISO-8601,date -u +"%Y-%m-%dT%H:%M:%SZ" или, может быть, более симпатичная date +"%Y-%m-%d %T".
Пабло А
1
Хотя этот сценарий работает должным образом, он порождает новый процесс (выполнение date) для каждой строки журнала, что может быть серьезным недостатком в зависимости от вашей машины и количества журналов. Я бы предпочел использовать, tsесли доступно, см. Ответ @willem
Майкл
57

Смотрите tsиз moreutilsпакета Ubuntu :

command | ts

Или, если $commandделает автоматическую буферизацию (требуется expect-devпакет):

unbuffer command | ts
Willem
источник
18

Команда date предоставит эту информацию

date -u
Sat Sep 10 22:39:24 UTC 2011

так что вы можете

echo $(date -u) "Some message or other"

это то, что вы хотели?

user9517
источник
Использование команды date было чем-то вроде того, что я имел в виду, но я не могу добавить это к самому сценарию, так что я ищу способ изменить эту строку: "script.sh >> / var / log / logfile ", чтобы добавить дату.
Антониус Блох
В этом случае перенаправьте вывод вашего скрипта в именованный канал и получите демон, прослушивающий вывод, который берет вывод скрипта и добавляет дату перед записью его в файл журнала. Вы можете изменить сценарий, который я написал здесь, чтобы сделать это. Я бы сделал это, потому что это меня интересует, но в Великобритании уже поздно, и у меня завтра раннее начало.
user9517 10.09.11
12

Вы можете просто отобразить вывод команды в файл журнала. т.е.

echo "`date -u` `./script.sh`" >> /var/log/logfile

Это действительно работает :)

Пример:

[sparx@E1]$ ./script.sh 
Hello Worldy
[sparx@E1]$ echo "`date -u` `./script.sh`" >> logfile.txt
[sparx@E1]$ cat logfile.txt 
Mon Sep 12 20:18:28 UTC 2011 Hello Worldy
[sparx@E1]$ 
Sparx
источник
Хм не работает для меня.
Антониус Блох
Что вы получаете, когда выполняете команду?
SparX
8
Это ставит метку времени перед всем выводом './script.sh', а не перед каждой строкой.
Клак
8

Сделать config.shфайл

#!/usr/bin/env bash
LOGFILE="/path/to/log.log"
TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"`

Когда вам нужно отправить в файл журнала использования

#!/usr/bin/env bash
source /path/to/config.sh

echo "$TIMESTAMP Say what you are doing" >> $LOGFILE

do_what_you_want >> $LOGFILE

Лог-файл будет выглядеть

2013-02-03 18:22:30 Say what you are doing

Так будет легко отсортировать по дате

SCLS
источник
8
Ваш '' config.sh '' будет запускать '' date '' ровно один раз, на '' source ... / config.sh ''.
Клак
5

Вы имеете в виду, как:

(date && script.sh) >> /var/log/logfile
CJC
источник
Боже мой, люди, все делают замену обратного тика, именованные каналы и т. Д. Просто заключите команду даты и сценарий в скобки! У парня, у которого есть функция, есть законный случай, если есть многострочный вывод, и журнал должен выглядеть хорошо с датой в каждой строке, но большинство из этих решений излишни и не используют семантику оболочки.
CJC
8
Это добавит отметку времени только один раз за выполнение script.sh. Для ОП требуется временная метка на строку.
Дэйв Форгак
1
хотя это не отвечает на вопрос ОП, я все же нашел полезную информацию.
Пользователь
4

Попробуй это

timestamp()
{
 date +"%Y-%m-%d %T"
}

Вызовите эту функцию отметки времени в каждой команде echo:

echo "$(timestamp): write your log here" >> /var/log/<logfile>.log
Санджай Ядав
источник
@ Shazbot: Спасибо за редактирование, это была опечатка, я не заметил.
Санджай Ядав
4

Принятый ответ https://serverfault.com/a/310104 может быть немного медленным, если нужно обработать много строк, с накладными расходами на запуск dateпроцесса, позволяющего в Ubuntu около 50 строк в секунду, и только около 10 -20 в Cygwin.

Когда bashможно предположить, что более быстрой альтернативой будет printfвстроенная функция со %(...)Tспецификатором формата. сравнить

>> while true; do date; done | uniq -c
     47 Wed Nov  9 23:17:18 STD 2016
     56 Wed Nov  9 23:17:19 STD 2016
     55 Wed Nov  9 23:17:20 STD 2016
     51 Wed Nov  9 23:17:21 STD 2016
     50 Wed Nov  9 23:17:22 STD 2016

>> while true; do printf '%(%F %T)T\n'; done | uniq -c
  20300 2016-11-09 23:17:56
  31767 2016-11-09 23:17:57
  32109 2016-11-09 23:17:58
  31036 2016-11-09 23:17:59
  30714 2016-11-09 23:18:00
KDB
источник
1

Вы можете заметить, что awk работает быстро,

gawk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }'

yes |head -5000000 |gawk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }' |uniq -c
 461592 [2017-02-28 19:46:44] y
 488555 [2017-02-28 19:46:45] y
 491205 [2017-02-28 19:46:46] y
 498568 [2017-02-28 19:46:47] y
 502605 [2017-02-28 19:46:48] y
 494048 [2017-02-28 19:46:49] y
 493299 [2017-02-28 19:46:50] y
 498005 [2017-02-28 19:46:51] y
 502916 [2017-02-28 19:46:52] y
 495550 [2017-02-28 19:46:53] y
  73657 [2017-02-28 19:46:54] y

Но sed работает намного быстрее,

sed -e "s/^/$(date -R) /"

yes |head -5000000 |sed -e "s/^/$(date -R) /" |uniq -c
5000000 Tue, 28 Feb 2017 19:57:00 -0500 y

Тем не менее, при ближайшем рассмотрении, набор, кажется, не меняет время,

vmstat 1 | sed -e "s/^/$(date -R) /"
ChuckCottrill
источник
1
это потому что bash оценивает "s/^/$(date -R) /"и запускает дату один раз перед sed. Sed передается статическая строка.
Эван Бенн
Обычный удар: yes | head -5000000 | while read line; do echo $((SECONDS)); done | uniq -cкоторый намного медленнее, чем поглазеть. tsУтилита имеет ту же производительность, что и цикл bash.
Ахан
Perl: yes |head -5000000 |perl -ne 'print localtime."\t".$_' |uniq -cнемного медленнее, чем awk.
Ахан
1

Ниже приведено содержимое моего журнала

xiongyu@ubuntu:~/search_start_sh$ tail restart_scrape.log 

2017-08-25 21:10:09 scrape_yy_news_main.py got down, now I will restart it

2017-08-25 21:10:09 check_yy_news_warn.py got down, now I will restart it

2017-08-25 21:14:53 scrape_yy_news_main.py got down, now I will restart it

часть моего содержимого оболочки, как показано ниже

log_file="restart_scrape.log"
TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"`
echo "$TIMESTAMP $search_py_file got down, now I will restart it" | tee -a $log_file 
Jayhello
источник
1

Этот скрипт выводит вывод в терминал, а также сохраняет в лог-файл.

#!/bin/bash

MY_LOG=/var/log/output.log

echolog(){
    if [ $# -eq 0 ]
    then cat - | while read -r message
        do
                echo "$(date +"[%F %T %Z] -") $message" | tee -a $MY_LOG
            done
    else
        echo -n "$(date +'[%F %T %Z]') - " | tee -a $MY_LOG
        echo $* | tee -a $MY_LOG
    fi
}

echolog "My script is starting"
whoami | echolog

Образец вывода:

[2017-10-29 19:46:36 UTC] - My script is starting
[2017-10-29 19:46:36 UTC] - root
Seff
источник
Ваша первая команда даты должна использовать одинарные кавычки, а не двойные.
Джейсон Харрисон
1

Другой вариант - настроить функцию для вызова каждый раз, когда вы хотите вывести данные в свой код:

PrintLog(){
  information=$1
  logFile=$2
  echo "$(date +'%Y-%m-%d %H:%M:%S" $information} >> $logFile
}

Затем каждый раз в своем коде вы хотите отправить его в файл журнала вызова

PrintLog "Stuff you want to add..." ${LogFileVariable}

Очень просто....

Джош Уильямс
источник
0

Труба "сед":

script.sh | sed "s|^|$('date') :: |" >> /var/log/logfile
eubide
источник
Что касается другого ответа, то это просто дата запуска bash. sed не обновляет время в строке.
Эван Бенн