Назад | Перейти на главную страницу

Как я могу полностью регистрировать все действия сценариев bash?

Из вывода моего скрипта я хочу захватить ВСЕ данные журналов с сообщениями об ошибках и перенаправить их все в файл журнала.

У меня есть сценарий, как показано ниже:

#!/bin/bash
(
echo " `date` : part 1 - start "
ssh -f admin@server.com 'bash /www/htdocs/server.com/scripts/part1.sh logout exit'
echo " `date` : sleep 120"
sleep 120
echo " `date` : part 2 - start"
ssh admin@server.com 'bash /www/htdocs/server.com/scripts/part2.sh logout exit'
echo " `date` : part 3 - start"
ssh admin@server.com 'bash /www/htdocs/server.com/scripts/part3.sh logout exit'
echo " `date` : END"
) | tee -a /home/scripts/cron/logs

Я хочу видеть все действия в файле /home/scripts/cron/logs

Но я вижу только то, что поставил после echo команда.

Как проверить в журналах, что SSH команда прошла успешно?

Мне нужно собрать все журналы. Мне это нужно, чтобы отслеживать результат каждой команды в моем скрипте, чтобы лучше анализировать, что происходит, когда скрипт не работает.

Обычно я помещаю что-то похожее на следующее в начале каждого скрипта (особенно если он будет работать как демон):

#!/bin/bash
exec 3>&1 4>&2
trap 'exec 2>&4 1>&3' 0 1 2 3
exec 1>log.out 2>&1
# Everything below will go to the file 'log.out':

Пояснение:

  1. exec 3>&1 4>&2

    Сохраняет файловые дескрипторы, чтобы их можно было восстановить в том виде, в котором они были до перенаправления, или использовать самих себя для вывода в те файлы, которые были до следующего перенаправления.

  2. trap 'exec 2>&4 1>&3' 0 1 2 3

    Восстановить файловые дескрипторы для определенных сигналов. Обычно не требуется, так как они должны быть восстановлены при выходе из суб-оболочки.

  3. exec 1>log.out 2>&1

    Перенаправить stdout подавать log.out затем перенаправить stderr к stdout. Обратите внимание, что порядок важен, если вы хотите, чтобы они обращались к одному и тому же файлу. stdout должен быть перенаправленным раньше stderr перенаправлен на stdout.

С этого момента, чтобы увидеть вывод на консоли (возможно), вы можете просто перенаправить на &3. Например,

echo "$(date) : part 1 - start" >&3

пойдет куда угодно stdout был направлен, предположительно, консоли, до выполнения строки 3 выше.

чтобы получить вывод ssh в файл журнала, вы должны перенаправить stderr к stdout. вы можете сделать это, добавив 2>&1 после вашего сценария bash.

это должно выглядеть так:

#!/bin/bash
(
...
) 2>&1 | tee ...

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

#!/bin/bash
((
...
) 2>&1) | tee ...

Когда я читаю ваш вопрос, вы не хотите регистрировать вывод, но вся последовательность команд, и в этом случае другие ответы вам не помогут.

Вызов сценариев оболочки с -x для вывода всего:

sh -x foo.sh

Войдите в нужный файл с помощью:

sh -x foo.sh >> /home/scripts/cron/logs

В bash вы можете поставить set -x и после этого он распечатает каждую команду, которую он выполняет (и переменные bash). Вы можете выключить его с помощью set +x.

Если вы хотите быть параноиком, вы можете поставить set -o errexit в вашем сценарии. Это означает, что сценарий завершится ошибкой и остановится, если одна команда вернула ненулевой код выхода, что является стандартным способом unix для сигнализации о том, что что-то пошло не так.

Если вы хотите получить более качественные журналы, вам следует взглянуть на ts в moreutils пакет debian / ubuntu. Он добавит к каждой строке отметку времени и распечатает ее. Так вы можете видеть, когда что-то происходило.

Следуя тому, что говорили другие, набор руководство хороший ресурс. Я кладу:

#!/usr/bin/env bash
exec 1> command.log 2>&1
set -x

В верхней части скриптов я хочу продолжить, или set -ex если он должен выйти из-за ошибки.

Я обнаружил, что @nicerobot (Как я могу полностью регистрировать все действия сценариев bash? ) ответа может быть недостаточно, чтобы полностью перенаправить весь вывод на консоль. Некоторые выходные данные все еще могут быть потеряны.

Полное перенаправление выглядит так:

#!/bin/bash

# some basic initialization steps including `NEST_LVL` and `SCRIPTS_LOGS_ROOT variables...
source ".../__myinit__.sh"

# no local logging if nested call
(( ! IMPL_MODE && ! NEST_LVL )) && {
  export IMPL_MODE=1
  exec 3>&1 4>&2
  trap 'exec 2>&4 1>&3' EXIT HUP INT QUIT RETURN

  [[ ! -e "${SCRIPTS_LOGS_ROOT}/.log" ]] && mkdir "${SCRIPTS_LOGS_ROOT}/.log"

  # RANDOM instead of milliseconds
  case $BASH_VERSION in
    # < 4.2
    [123].* | 4.[01] | 4.0* | 4.1[^0-9]*)
      LOG_FILE_NAME_SUFFIX=$(date "+%Y'%m'%d_%H'%M'%S''")$(( RANDOM % 1000 ))
      ;;
    # >= 4.2
    *)
      printf -v LOG_FILE_NAME_SUFFIX "%(%Y'%m'%d_%H'%M'%S'')T$(( RANDOM % 1000 ))" -1
      ;;
  esac

  (
  (
    myfoo1
    #...
    myfooN

    # self script reentrance...
    exec $0 "$@"
  ) | tee -a "${SCRIPTS_LOGS_ROOT}/.log/${LOG_FILE_NAME_SUFFIX}.myscript.log" 2>&1
  ) 1>&3 2>&4

  exit $?
}

(( NEST_LVL++ ))

# usual script body goes here...

Объяснение:

  1. Недостаточно просто перенаправить вывод, потому что вам все равно нужно перенаправить внутренний echo вызывает как консоль, так и файл, поэтому труба плюс тройник - единственный способ разделить выходной поток.
  2. Мы должны использовать (...) для перенаправления каждого потока в отдельный файл или для восстановления всех потоков обратно в исходное состояние отдельными шагами. Вторая причина, потому что myfoo вызовы перед повторным входом должны работать как есть, без дополнительного перенаправления.
  3. Поскольку сценарий может быть вызван из вложенного сценария, мы должны перенаправить вывод в файл только один раз при вызове верхнего уровня. Итак, мы используем NEST_LVL переменная для указания уровня вызова гнезда.
  4. Поскольку перенаправление может выполняться извне независимо от сценария, мы должны явно включить внутреннее перенаправление с помощью IMPL_MODE переменная.
  5. Мы должны использовать значения даты и времени для автоматического создания нового уникального файла журнала при каждом запуске сценария.

TL; DR - Вчера я написал набор инструментов для регистрации запусков и сессий программ.

В настоящее время доступно на https://github.com/wwalker/quick-log

Как администратор, я всегда хочу регистрировать вывод какой-либо команды, часто не сценария. Чтобы решить эту проблему, я написал несколько вещей. Самый простой - использовать программу "скрипт", как упомянуто xX0v0Xx. Я обнаружил, что вызов скрипта (без каких-либо аргументов) часто приводит к перезаписи вывода предыдущего скрипта. Итак, я создал этот псевдоним. Все, что он делает, это предотвращает перезапись. Вам нужен каталог ~ / tmp.

$ alias scr='script ~/tmp/typescript-$(date +%FT%T)'
$ scr
Script started, file is /home/wwalker/tmp/typescript-2019-12-05T18:56:31
$

Это здорово, когда я хочу поймать интерактивный сеанс.

Когда я хочу зарегистрировать вывод команды (скрипт или двоичный файл), мне нужен либо точный вывод, либо вывод с отметками времени перед каждой строкой. Итак, я написал эти две функции bash:

alias iso8601="date +%Y-%m-%dT%H:%M:%S"

justlog(){
  name=$(basename "$1")
  log=~/logs/${name}-$(iso8601)
  "$@" > "$log" 2>&1
}

timelog(){
  name=$(basename "$1")
  log=~/logs/${name}-$(iso8601)
  # https://github.com/wwalker/ilts
  # You could replace ilts with ts
  # /usr/bin/ts %FT%H:%M:%.S
  "$@" |& ilts -S -E > "$log" 2>&1
}

Просто запустите свою команду, как обычно:

justlog run rcn rcn-work\\\\\* 'ps -ef -o lstart,cmd | grep [s]upervisor'

или

timelog run rcn rcn-work\\\\\* 'ps -ef -o lstart,cmd | grep [s]upervisor'

Это только что создало 2 файла с именем:

wwalker@polonium:~ ✓ $ ls -l ~/logs/run*
-rw-r--r-- 1 wwalker wwalker 10495 2019-12-05 18:21:14.985 /home/wwalker/logs/run-2019-12-05T18:21:13
-rw-r--r-- 1 wwalker wwalker  1694 2019-12-05 18:24:02.878 /home/wwalker/logs/run-2019-12-05T18:24:01
-rw-r--r-- 1 wwalker wwalker  7623 2019-12-05 18:25:07.873 /home/wwalker/logs/run-2019-12-05T18:25:06
-rw-r--r-- 1 wwalker wwalker 10296 2019-12-05 18:34:59.546 /home/wwalker/logs/run-2019-12-05T18:34:57

Но подождите, это еще не все !!

Я не хотел выполнять команду ls, чтобы найти имя файла журнала, который только что создал для меня justlog или timelog. Итак, я добавил еще 3 функции:

newestlog(){
  name=$(basename "$1")
  ls  ~/logs/"${name}"* | tail -1
}

viewlog(){
  name=$(basename "$1")
  view $( newestlog "$name" )
}

lesslog(){
  name=$(basename "$1")
  less $( newestlog "$name" )
}

Итак, вы запускаете свою команду с помощью justlog (или timelog), а затем просто используете lesslog или viewlog (я, вероятно, создам журнал emacs для этих людей):

justlog run rcn rcn-work\\\\\* 'ps -ef -o lstart,cmd | grep [s]upervisor'
lesslog run

Все, нет ls ~/tmp, нет игр с завершением вкладки, чтобы найти имя файла. Просто запустите lesslog (или viewlog, если вам нравится использовать vim для просмотра журналов).

Но ждать! Есть еще кое-что!

«Я все время использую grep в своих файлах журнала». Ответ, как вы уже догадались, greplog

Сначала получите текст из всех поврежденных файлов /etc/cron.d/atop сервера 800:

justlog fordcrun 'cat /etc/cron.d/atop; md5dum /etc/cron.d/atop'

Затем получите имена хостов (в строке над выводом в файле :-)) с помощью greplog:

wwalker@polonium:~ ✓ $ greplog fordcrun  -B1 -F "0 0 * * root"
int-salt-01:
    0 0 * * root systemctl restart atop
--
rcn-pg-01:
    0 0 * * root systemctl restart atop
rcn-pg-02:
    0 0 * * root systemctl restart atop
rcn-pg-03:
    0 0 * * root systemctl restart atop

Вы можете просто использовать «скрипт».

man script для подробностей.

Пример:

script -a -e -c "set -x; echo this is logging ..; <your script can be called here>" mylogfile.log