На днях столкнулся с проблемой. На моем ноуте крутится несколько crontab-задач. Хотелось бы видеть, когда сработала каждая из них и если нет, то почему. Какое-то время я выкручивался с помощью логов. Оба канала (stdout и stderr) перехватываются в общий пайп, где каждая строка предваряется текущий датой и записывается в файл. Например:

SHELL=/bin/bash

0 */3 * * * /path/to/command &> >( while read line; do echo "$(date): ${line}"; done >> /Users/ivan/logs/command.log)

В результате и обычный, и ошибочный выхлоп оседают в файле. Выглядит это так:

Tue Jun 22 15:58:02 MSK 2021: warning: Skipping file ...
Tue Jun 22 15:58:19 MSK 2021: Completed 0 file(s) with ...
Tue Jun 22 15:59:02 MSK 2021: warning: Skipping file /Users/ivan/...
Tue Jun 22 15:59:19 MSK 2021: Completed 0 file(s) with ...

Недостаток в том, что логи нужно регулярно проверять. Об этом легко забыть на месяц, а потом выяснится, что задача не работала из-за глупой ошибки.

Тут я вспомнил, что крон умеет отправлять выхлоп задачи на почту. Если задана переменная MAILTO=<email>, то на эту почту свалится выхлоп. Это значит, отпадают логи, ведь теперь достаточно проверить почту. Проверять ее можно и с телефона, что в разы удобней.

Отправка письма работает за счет утилиты mail. В общем виде ей пользуются так:

cat something | mail -s 'subject' friend@domain.com

Эта команда отправит письмо на сервер domain.com, при этом отправитель будет выглядеть как ivan@ivan.local, то есть имя пользователя и текущего хоста. То же самое можно получить командой:

echo `whoami`@`hostname`

Обратите внимание на кавычки – они означают выполнить выражение в новом шелле и подставить результат.

Отправленное письмо никто не получит, что потому что современные сервера отметают письма, где айпи отправителя не совпадает с айпи его домена. Это хорошо, иначе бы мы утонули в спаме. Будем отправлять письма с серверов Гугла. А чтобы mail мог подключиться к Гуглу, сделаем три вещи.

Первое. В файл /etc/postfix/main.cf дописать строки ниже. Делать это нужно под sudo:

relayhost = [smtp.gmail.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_use_tls = yes
smtp_sasl_mechanism_filter = plain

Второе. Создать файл /etc/postfix/sasl_passwd и вписать в него:

[smtp.gmail.com]:587 username@gmail.com:password

, где password — пароль приложения для почты. Сгенерить этот пароль можно на специальной странице Гугла.

Замечу, что вместо gmail.com может быть ваш домен, если он нацелен на гугловые сервера как в моем случае.

Третье — указать postfix новые конфиги и перезагрузить его:

sudo chmod 600 /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd
sudo launchctl stop org.postfix.master
sudo launchctl start org.postfix.master

Все. Пробуем отправить письмо. Сообщим в нем содержимое домашней папки:

ls -l | mail -s 'my home dir' ivan@grishaev.me

Мне пришло с первого раза. Plain text, красота.

Так вот, теперь когда почта работает, настроим крон. Открываем его командой crontab -e и пишем сверху:

MAILTO=<ваша@почта>

Кроме того, в конец каждой команды добавляем эхо, например:

0 */3 * * * cd /some/path && command --foo 42 && echo OK

Зачем нужно эхо? Оказалось, что крон не отправляет письмо, если выхлоп задачи пуст. А поскольку я всегда хочу знать о запуске задачи, то делаю вывод не пустым. Вместо echo OK можно вывести текущую дату с помощью && date.

В результате мы получим письмо, где тема — команда задачи, а тело — ее выхлоп. Работает в том числе и для задач, которые не сработали из-за ошибки.

Я уж не говорю о том, что с помощью mail можно быстро что-то скидывать себе на почту, организовать какие-то заметки и прочее. Что самое прекрасное — нет интерфейса, все простое и открытое, работает везде. Чудо.

В следующий раз расскажу, что именно я ставлю на крон, там тоже интересные вещи.