Teraz mam centralny moduł w ramy, które spawns kilka procesów z użyciem Pythona 2.6 multiprocessing
moduł . Ponieważ używa multiprocessing
, istnieje moduł obsługujący wieloprocesowy dziennik na poziomie modułu LOG = multiprocessing.get_logger()
. Zgodnie z dokumentami ten program rejestrujący ma blokady współużytkowane przez proces, dzięki czemu nie rozbijasz rzeczy w sys.stderr
(lub jakimkolwiek uchwycie pliku) przez jednoczesne pisanie do niego wielu procesów.
Problem, który mam teraz, polega na tym, że inne moduły w frameworku nie obsługują wielu procesów. Z mojego punktu widzenia muszę uzależnić wszystkie zależności od tego modułu centralnego od rejestrowania uwzględniającego przetwarzanie wieloprocesowe. To denerwujące w ramach, nie mówiąc już o wszystkich klientach. Czy są alternatywy, o których nie myślę?
źródło
multiprocessing.get_logger()
? Wydaje się, że w oparciu o te inne sposoby rejestrowania funkcja rejestrowaniamultiprocessing
ma niewielką wartość.get_logger()
to rejestrator używany przezmultiprocessing
sam moduł. Jest to przydatne, jeśli chcesz debugowaćmultiprocessing
problem.Odpowiedzi:
Jedynym sposobem radzenia sobie z tym nieinwazyjnie jest:
select
podstawie deskryptorów plików potoków wykonaj sortowanie według dostępnych wpisów w dzienniku i opróżnij dziennik scentralizowany. Powtórz.)źródło
atexit
użyję :-). Problem w tym, że nie da ci odczytu w czasie rzeczywistym. Może to być częścią ceny wieloprocesowości, a nie wielowątkowości.multiprocessing.Queue
nie będzie prostsze, jeśli do użycia będzie dużo kodumultiprocessing.Queue
i / lub jeśli problem stanowi wydajnośćWłaśnie napisałem własny moduł obsługi dziennika, który przekazuje wszystko do procesu nadrzędnego za pomocą potoku. Testowałem go tylko przez dziesięć minut, ale wydaje się, że działa całkiem dobrze.
( Uwaga: To jest zakodowane na stałe
RotatingFileHandler
, co jest moim własnym przypadkiem użycia.)Aktualizacja: @javier utrzymuje teraz to podejście jako pakiet dostępny na Pypi - patrz rejestrowanie wieloprocesowe na Pypi, github na https://github.com/jruere/multiprocessing-logging
Aktualizacja: wdrożenie!
To teraz używa kolejki do poprawnej obsługi współbieżności, a także poprawnie odzyskuje po błędach. Używam tego w produkcji od kilku miesięcy, a obecna wersja poniżej działa bez problemu.
źródło
multiprocessing.Queue
używa wątku doput()
. Dlatego nie należy wywoływaćput
(tj. Rejestrować wiadomości przy użyciu modułuMultiProcessingLog
obsługi) przed utworzeniem wszystkich podprocesów. W przeciwnym razie wątek będzie martwy w procesie potomnym. Jednym z rozwiązań jest wywołanieQueue._after_fork()
na początku każdego procesu potomnego lub użyciemultiprocessing.queues.SimpleQueue
zamiast niego, który nie wymaga wątku, ale blokuje.multiprocessing-logging
.QueueHandler
jest natywny w Pythonie 3.2+ i robi to dokładnie. Jest łatwo replikowany w poprzednich wersjach.Dokumenty Pythona mają dwa pełne przykłady: Logowanie się do jednego pliku z wielu procesów
Jeśli używasz Pythona <3.2, po prostu skopiuj
QueueHandler
do własnego kodu z: https://gist.github.com/vsajip/591589 lub alternatywnie zaimportuj logutils .Każdy proces (w tym proces nadrzędny) umieszcza swoje logowanie
Queue
, a następnielistener
wątek lub proces (dla każdego podano jeden przykład) zbiera je i zapisuje w pliku - bez ryzyka uszkodzenia lub zakłócenia.źródło
Poniżej znajduje się inne rozwiązanie z naciskiem na prostotę dla każdego innego (takiego jak ja), który przybywa tutaj z Google. Logowanie powinno być łatwe! Tylko dla wersji 3.2 lub wyższej.
źródło
QueueHandler
IQueueListener
klasy mogą być wykorzystane na Python 2.7, jak również, dostępny wlogutils
pakiecie.Jeszcze inną alternatywą mogą być różne programy obsługi rejestrowania nie oparte na plikach w
logging
pakiecie :SocketHandler
DatagramHandler
SyslogHandler
(i inni)
W ten sposób możesz łatwo mieć demona rejestrującego, w którym możesz bezpiecznie pisać i poprawnie obsługiwać wyniki. (Np. Prosty serwer z gniazdami, który po prostu usuwa wiadomość i wysyła ją do własnej obrotowej procedury obsługi plików.)
SyslogHandler
Będzie dbać o to dla ciebie. Oczywiście możesz użyć własnego wystąpieniasyslog
, a nie systemowego.źródło
Wariant innych, który oddziela wątek rejestrowania i kolejki.
źródło
fileConfig()
w MainProcess i ledwo skonfigurowanego rejestratora w PoolWorkers (tylko zsetLevel(logging.NOTSET)
). Jak wspomniałem w innym komentarzu, korzystam z Pool, więc musiałem uzyskać kolejkę (proxy) od Managera zamiast wieloprocesowego przetwarzania, aby można było ją marynować. To pozwala mi przekazywać kolejkę do pracownika wewnątrz słownika (który w większości pochodzi z argsparse obiektuvars()
). Wydaje mi się, że ostatecznie jest to najlepsze podejście dla MS Windows, które nie ma fork () i psuje rozwiązanie @zzzeak.fork
. W ten sposób każdy proces będzie miał własną niezależną, bezużyteczną kolejkę. Drugie podejście w połączonych pytaniach nie działa na takich platformach. Jest to sposób na nieprzenośny kod.multiprocessing.Queue
z procesem głównym i od tego czasu ciągle go używam. Nie będę twierdził, że rozumie, dlaczego to działa.Wszystkie obecne rozwiązania są zbyt sprzężone z konfiguracją rejestrowania za pomocą modułu obsługi. Moje rozwiązanie ma następującą architekturę i funkcje:
multiprocessing.Queue
logging.Logger
(i już zdefiniowane wystąpienia) są załatane, aby wysłać wszystkie rekordy do kolejkiKod z przykładem użycia i danymi wyjściowymi można znaleźć na następującej liście: https://gist.github.com/schlamar/7003737
źródło
daemon_thread.daemon
naTrue
. Musiałem to zrobić, aby mój program Python poprawnie zakończył działanie, gdy wystąpi wyjątek w menedżerze kontekstu.func
wlogged_call
przeciwnym razie wyjątek zostałby zniekształcony przez inne zarejestrowane dane wyjściowe. Oto moja zmodyfikowana wersja tego: gist.github.com/blah238/8ab79c4fe9cdb254f5c37abfc5dc85bfPonieważ możemy reprezentować rejestrowanie wieloprocesowe jak największej liczby wydawców i jednego subskrybenta (słuchacza), użycie ZeroMQ do implementacji przesyłania komunikatów PUB-SUB jest rzeczywiście opcją.
Ponadto moduł PyZMQ , powiązania Pythona dla ZMQ, implementuje PUBHandler , który jest obiektem do publikowania komunikatów rejestrujących przez gniazdo zmq.PUB.
W sieci istnieje rozwiązanie do scentralizowanego rejestrowania z aplikacji rozproszonej przy użyciu PyZMQ i PUBHandler, które można łatwo przystosować do lokalnej pracy z wieloma procesami publikacji.
źródło
Podoba mi się również odpowiedź Zzzeka, ale Andre ma rację, że kolejka jest wymagana, aby zapobiec zakłóceniom. Miałem trochę szczęścia z fajką, ale widziałem dudnienie, które jest nieco oczekiwane. Wdrożenie go okazało się trudniejsze niż myślałem, szczególnie ze względu na działanie w systemie Windows, gdzie istnieją dodatkowe ograniczenia dotyczące zmiennych globalnych i innych rzeczy (zobacz: Jak zaimplementowano wieloprocesowość Pythona w systemie Windows? )
Ale w końcu udało mi się to uruchomić. Ten przykład prawdopodobnie nie jest idealny, dlatego komentarze i sugestie są mile widziane. Nie obsługuje również ustawiania formatyzatora ani niczego innego niż główny rejestrator. Zasadniczo musisz ponownie uruchomić program rejestrujący w każdym z procesów puli w kolejce i skonfigurować inne atrybuty programu rejestrującego.
Ponownie, wszelkie sugestie dotyczące ulepszenia kodu są mile widziane. Na pewno jeszcze nie znam wszystkich sztuczek Pythona :-)
źródło
if 'MainProcess' == multiprocessing.current_process().name:
można go wykorzystać zamiast przejśćchild
?po prostu opublikuj gdzieś swoje wystąpienie programu rejestrującego. w ten sposób inne moduły i klienci mogą korzystać z interfejsu API, aby uzyskać rejestrator bez konieczności
import multiprocessing
.źródło
import logging; logging.basicConfig(level=logging.DEBUG); logging.debug('spam!')
z dowolnego miejsca i sprawić, by działał poprawnie.Podobała mi się odpowiedź Zzzeka. Po prostu zastąpiłbym potok kolejką, ponieważ jeśli wiele wątków / procesów używa tego samego końca potoku do generowania komunikatów dziennika, zostaną one zniekształcone.
źródło
Co powiesz na delegowanie całego logowania do innego procesu, który odczytuje wszystkie wpisy dziennika z kolejki?
Po prostu udostępnij LOG_QUEUE za pośrednictwem dowolnego mechanizmu wieloprocesowego lub nawet dziedziczenia, a wszystko działa dobrze!
źródło
Mam rozwiązanie podobne do ironhackera, z tym wyjątkiem, że używam rejestrowania. Wyjątek w niektórych moich kodach i stwierdziłem, że muszę sformatować wyjątek przed przekazaniem go z powrotem do kolejki, ponieważ śledzenia nie można ustawić:
źródło
Poniżej znajduje się klasa, która może być używana w środowisku Windows, wymaga ActivePython. Możesz także dziedziczyć dla innych programów rejestrujących (StreamHandler itp.)
A oto przykład, który pokazuje użycie:
źródło
multiprocessing.Lock()
zamiast Windows Mutex uczyniłoby to rozwiązanie przenośnym.Oto mój prosty hack / obejście ... nie jest to najbardziej kompleksowe, ale łatwo modyfikowalne i łatwiejsze do odczytania i zrozumienia, myślę, niż jakiekolwiek inne odpowiedzi, które znalazłem przed napisaniem tego:
źródło
Jest ten świetny pakiet
Pakiet: https://pypi.python.org/pypi/multiprocessing-logging/
kod: https://github.com/jruere/multiprocessing-logging
Zainstalować:
Następnie dodaj:
źródło
Jedną z alternatyw jest zapisanie dziennika przetwarzania wielokrotnego do znanego pliku i zarejestrowanie modułu
atexit
obsługi, aby dołączył do tych procesów i odczytał go ponownie na stderr; jednak w ten sposób nie uzyskasz przepływu w czasie rzeczywistym do komunikatów wyjściowych na stderr.źródło
Jeśli masz zakleszczenia występujące w kombinacji blokad, wątków i widelców w
logging
module, jest to zgłaszane w raporcie o błędzie 6721 (patrz także powiązane pytanie SO ).Jest mała rozwiązanie fixup pisał tutaj .
To jednak naprawi wszelkie potencjalne impasy
logging
. To nie naprawi, że rzeczy mogą być zniekształcone. Zobacz inne odpowiedzi przedstawione tutaj.źródło
Najprostszy pomysł, jak wspomniano:
[WatchedFileHandler][1]
. Powody tej procedury obsługi zostały szczegółowo omówione tutaj , ale w skrócie istnieją inne gorsze warunki wyścigu z innymi przewodnikami. Ten ma najkrótsze okno na warunki wyścigu.źródło
Dla każdego, kto może tego potrzebować, napisałem dekorator dla pakietu multiprocessing_logging, który dodaje bieżącą nazwę procesu do dzienników, aby stało się jasne, kto co loguje.
Działa również install_mp_handler (), więc uruchomienie go przed utworzeniem puli staje się bezużyteczne.
To pozwala mi zobaczyć, który pracownik tworzy, które rejestruje wiadomości.
Oto plan z przykładem:
źródło
Moim dzieciom, które od dziesięcioleci spotykają ten sam problem i znalazłem to pytanie na tej stronie, zostawiam tę odpowiedź.
Prostota kontra nadmierna komplikacja. Po prostu użyj innych narzędzi. Python jest niesamowity, ale nie został zaprojektowany do robienia niektórych rzeczy.
Poniższy fragment kodu dla demona logrotate działa dla mnie i nie komplikuje rzeczy. Zaplanuj, aby działał co godzinę i
Tak go instaluję (dowiązania symboliczne nie działają dla logrotate):
źródło