И как всегда немного предыстории :)

Предыстория

Есть такая библиотечка для создания логов приложения: glog. Есть она для C++: https://github.com/google/glog. Есть для Golang: https://github.com/golang/glog. Всем хороша, пишет отдельные логи для INFO, WARNING, ERROR, FATAL. Но есть некоторые особенности. А особенности эти следующие:

  • для каждого лога создаются символические ссылки с именами типа server.WARNING, server.INFO, где server имя бинарника.
  • новый лог с уникальным именем создается каждый раз при рестарте приложения
  • соответственно, “ротация” файлов логов производится самой библиотекой
  • из этого вытекает следующее: logrotate нормально работать с этим не может, он считает, что ротировать там просто нечего
  • и следующее: для ротации таких логов приходится писать штуки самостоятельно

Я уже писал скрипт, которые старые логи просто сжимает с помощью gzip и оставляет их там же, где они и лежали. А тут потребовалось слегка проапгрейдить скрипт и добавить отправку сжатых логов в бакет на GCS. Написать я написал, но похоже где-то накосячил (кстати, до сих пор не нашел, где именно) и, кроме пожатых логов, были удалены и актуальные, куда приложение пишет в текущий момент.

Надо как-то восстанавливать.

Восстановление

Как вернуть удаленный файлы я уже знал. На практике ни разу нужные файлы не восстанавливал, как-то не было нужды, а вот ради интереса пробовал. Итак, напомню вкратце, если кто подзабыл. Смотрим PID приложения:

pidof <app-name>

Смотрим, какие файлы открыты приложением с этим PID:

ls -l /proc/<PID>/fd

И видим, что файлы логов были удалены:

l-wx------ 1 user group 64 May 29 08:31 4 -> /var/log/server.log.INFO.20180529-083134.4995.1 (deleted)
…
l-wx------ 1 user group 64 May 31 07:45 59 -> /var/log/server.log.WARNING.20180529-083250.4995.1 (deleted)

Кстати, не знаю, почему тут в имени файла присутствует .1, поскольку реальный файл такого в имени не имеет:

lrwxrwxrwx 1 user group      58 May 29 08:31 server.INFO -> server.log.INFO.20180529-083134.4995
lrwxrwxrwx 1 user group      61 May 29 08:32 server.WARNING -> server.log.WARNING.20180529-083250.4995

Самый простой способ вернуть удаленный файл - просто скопировать в нужное место, например, файл INFO:

cp /proc/<PID>/fd/4 > /tmp/restored_info

Или даже на его место, где он раньше был:

cp /proc/<PID>/fd/4 > /var/log/server.log.INFO.20180529-083134.4995

Отлично. Файл восстановили, только вот приложение продолжает работать, а в восстановленный файл уже не пишет :(

Честно скажу, нормального (на мой взгляд) решения я не нашел, только несколько костыльное. А именно: читать из дескриптора и писать в нужный файл:

tail -c+1 -f --pid=<PID> /proc/<PID>/fd/<FD> > /var/log/server.log.INFO.20180529-083134.4995

Проверить можно обычным образом:

tail -f /var/log/server.log.INFO.20180529-083134.4995

или

tail -f /var/log/server.INFO

Немного про опции для tail:

  • -c+1: выводить, начиная с первого байта файла
  • --pid=<PID>: самая интересная часть. Вместе с -f завершит работу tail после того, как процесс с указанным PID умрет.

Лучше, конечно, запускать это в screen или tmux.