Mam program, którego dane wyjściowe przekierowuję do pliku dziennika:
./my_app > log
Chciałbym od czasu do czasu wyczyścić (tj. Opróżnić) dziennik (na żądanie) i wypróbować różne rzeczy
cat "" > log
Jednak zawsze wydaje się, że oryginalny potok jest wtedy zakłócany, a program nie przekierowuje już swojego wyjścia do pliku dziennika.
Czy jest na to jakiś sposób?
Aktualizacja
Pamiętaj, że nie mogę modyfikować aplikacji generującej dane wyjściowe. Po prostu wyrzuca go na standardowe wyjście i chcę zapisać go w dzienniku, aby móc go sprawdzić, kiedy jest to potrzebne, i wyczyścić, kiedy chcę. Nie powinienem jednak ponownie uruchamiać aplikacji.
bash
io-redirection
Bangnab
źródło
źródło
syslogd
lublogrotate
./my_app >> log
(aby wymusić dołączanie) icp /dev/null log
obciąć?cat "" > log
nie jest prawidłowymcat
poleceniem, ponieważ nie ma wywołanego pliku""
.Odpowiedzi:
Inna postać tego problemu występuje w przypadku długo działających aplikacji, których dzienniki są okresowo obracane. Nawet jeśli przeniesiesz oryginalny dziennik (np.
mv log.txt log.1
) I zastąpisz go natychmiast plikiem o tej samej nazwie, zanim nastąpi rzeczywiste rejestrowanie, jeśli proces utrzymuje plik otwarty, albo skończy się na zapisielog.1
(ponieważ nadal może to być otwarta i-węzeł) lub do zera.Częstym sposobem radzenia sobie z tym (sam logger systemu działa w ten sposób) jest zaimplementowanie procedury obsługi sygnału w procesie, który zamyka i ponownie otwiera swoje dzienniki. Następnie, gdy tylko chcesz przenieść lub wyczyścić (usuwając) dziennik, natychmiast wyślij ten sygnał do procesu.
Oto prosta demonstracja bash - wybacz moje umiejętności z grubej skorupy (ale jeśli zamierzasz edytować to dla najlepszych praktyk itp., Upewnij się, że najpierw rozumiesz funkcjonalność i przetestujesz swoją wersję przed edycją):
Rozpocznij od przejścia do tła:
Zauważ, że zgłasza swój PID do terminala, a następnie zaczyna się logować
log.txt
. Masz teraz 2 minuty na zabawę. Poczekaj kilka sekund i spróbuj:Po prostu
kill -2 12356
może tu również działać. Sygnał 2 to SIGINT (to też robi Ctrl-C, więc możesz wypróbować to na pierwszym planie i przenieść lub usunąć plik dziennika z innego terminala), którytrap
powinien zatrzymać. Sprawdzić;Zobaczmy teraz, czy nadal pisze do,
log.txt
mimo że go przenieśliśmy:Zauważ, że szło dalej tak, jak zostało przerwane. Jeśli nie chcesz przechowywać rekordu, po prostu wyczyść dziennik, usuwając go
Czek:
Wciąż idzie.
Nie można tego zrobić w skrypcie powłoki dla wykonanego podprocesu, niestety, ponieważ jeśli jest on na pierwszym planie, własne procedury obsługi sygnałów bash
trap
są zawieszone, a jeśli rozwidlisz go w tle, nie możesz ponownie przypisać jego wynik. Tj. Musisz to zaimplementować w swojej aplikacji.Jednak...
Jeśli nie możesz zmodyfikować aplikacji (np. Ponieważ jej nie napisałeś), mam narzędzie CLI, którego możesz użyć jako pośrednika. Możesz również zaimplementować prostą wersję tego w skrypcie, który służy jako potok do dziennika:
Nazwijmy to
pipetrap.sh
. Teraz potrzebujemy osobnego programu do testowania, naśladując aplikację, którą chcesz zalogować:To będzie
test.sh
:Są to dwa oddzielne procesy z osobnymi PID. Aby wyczyścić
test.sh
dane wyjściowe, które są wprowadzane poprzezpipetrap.sh
:Czek:
15858,,
test.sh
nadal działa, a jego dane wyjściowe są rejestrowane. W takim przypadku nie są wymagane żadne modyfikacje aplikacji.źródło
TL; DR
Otwórz plik dziennika w trybie dołączania :
Następnie możesz bezpiecznie obciąć go za pomocą:
Detale
Dzięki powłoce podobnej do Bourne'a istnieją 3 główne sposoby otwierania pliku do zapisu. W trybie tylko do zapisu (
>
), odczytu + zapisu (<>
) lub dołączania (i tylko zapisu>>
).W pierwszych dwóch jądrach zapamiętuje bieżącą pozycję, którą ty (mam na myśli, opis otwartego pliku , udostępniony przez wszystkie deskryptory plików, które zduplikowały lub odziedziczyły go, przechodząc od tego, w którym otworzyłeś plik) jesteś w plik.
Kiedy to zrobisz:
log
jest otwarty w trybie tylko do zapisu przez powłokę dla standardowego wejściacmd
.cmd
(jego początkowy proces został zaszczepiony przez powłokę i wszystkie możliwe dzieci) podczas pisania na standardowe wyjście, pisz w bieżącej pozycji kursora utrzymywanej przez otwarty opis pliku, który udostępniają w tym pliku.Na przykład, jeśli
cmd
początkowo zapisujezzz
, pozycja będzie w przesunięciu bajtu 4 do pliku, a następnym razemcmd
lub jego dzieci zapisują do pliku, to tam zapisywane będą dane bez względu na to, czy plik urósł, czy zmniejszył się w tym przedziale .Jeśli plik się skurczył, na przykład jeśli został obcięty za pomocą
i
cmd
piszexx
,xx
zostaną one zapisane z przesunięciem4
, a pierwsze 3 znaki zostaną zastąpione znakami NUL.Oznacza to, że nie można obciąć pliku, który został otwarty w trybie tylko do zapisu (i to samo dotyczy odczytu i zapisu ), tak jak w przypadku procesów, w których deskryptory plików były otwarte na pliku, pozostawi znaki NUL na początku plik (te, z wyjątkiem OS / X, zwykle nie zajmują miejsca na dysku, stają się rzadkimi plikami).
Zamiast tego (a zauważysz, że większość aplikacji robi to podczas zapisywania plików dziennika), powinieneś otworzyć plik w trybie dołączania :
lub
jeśli chcesz zacząć od pustego pliku.
W trybie dołączania wszystkie zapisy są wykonywane na końcu pliku, niezależnie od tego, gdzie był ostatni zapis:
Jest to również bezpieczniejsze, ponieważ jeśli dwa procesy otworzyły (w ten sposób) plik przez pomyłkę (na przykład, jeśli uruchomiłeś dwa wystąpienia tego samego demona), ich dane wyjściowe się nie zastąpią.
W najnowszych wersjach systemu Linux można sprawdzić bieżącą pozycję i sprawdzić, czy deskryptor pliku został otwarty w trybie dołączania , patrząc na
/proc/<pid>/fdinfo/<fd>
:Lub z:
Te flagi odpowiadają flagom O ..._ przekazanym do
open
wywołania systemowego.(
O_APPEND
jest 0x400 lub ósemkowy 02000)Więc powłoka
>>
otwiera plik za pomocąO_WRONLY|O_APPEND
(a 0100000 to O_LARGEFILE, co nie ma związku z tym pytaniem), podczas gdy>
jestO_WRONLY
tylko (i<>
jestO_RDWR
tylko).Jeśli wykonasz:
Aby wyszukać pliki otwarte za pomocą
O_APPEND
, znajdziesz większość plików dziennika aktualnie otwartych do zapisu w systemie.źródło
:
(dwukropka) w: >
?:
. Bez polecenia zachowanie różni się w zależności od powłoki.Jeśli dobrze rozumiem,
tee
wydaje się to rozsądnym podejściem:źródło
Jako szybkie rozwiązanie można użyć dziennika z rotacją (na przykład rotacja dzienna):
i przekieruj do niego logowanie
./my_app >> log$date.log
źródło
Jest to problem, który od dawna został rozwiązany za pomocą syslog (we wszystkich jego wariantach), ale istnieją dwa narzędzia, które rozwiązałyby konkretny problem przy minimalnym wysiłku.
Pierwszym, bardziej przenośnym, ale mniej wszechstronnym rozwiązaniem jest rejestrator (niezbędny dla każdego zestawu narzędzi dla administratorów). Jest to proste narzędzie, które kopiuje standardowe dane wejściowe do syslog. (przekazanie złotówki i uczynienie z obracania pliku problemu logrotate i syslog)
Drugim, bardziej eleganckim, ale mniej przenośnym rozwiązaniem jest syslog-ng, który oprócz przyjmowania komunikatów dziennika ze standardowych gniazd syslog może uruchamiać programy, których dane wyjściowe są filtrowane przez rejestrator. (Nie korzystałem jeszcze z tej funkcji, ale wygląda ona idealnie do tego, co chcesz zrobić).
źródło