class ThreadedWriter(Service): """ An non-blocking Eliot log destination that wraps a blocking destination, writing log messages to the latter in a managed thread. Unfortunately Python's Queue is not reentrant (http://bugs.python.org/issue14976) and neither is RLock (http://bugs.python.org/issue13697). In order to queue items in a thread we therefore rely on the self-pipe trick, and the easiest way to do that is by running another reactor in the thread. @ivar _reactor: A private reactor running in a thread which will do the log writes. @ivar _thread: C{None}, or a L{threading.Thread} running the private reactor. """ name = "Eliot Log Writer" def __init__(self, destination, reactor): """ @param destination: The underlying destination for log files. This will be called from a non-reactor thread. @param reactor: The main reactor. """ self._destination = destination self._reactor = Reactor() # Ick. See https://twistedmatrix.com/trac/ticket/6982 for real solution. self._reactor._registerAsIOThread = False self._mainReactor = reactor self._thread = None def startService(self): """ Start the writer thread. """ Service.startService(self) self._thread = threading.Thread(target=self._writer) self._thread.start() addDestination(self) def stopService(self): """ Stop the writer thread, wait for it to finish. """ Service.stopService(self) removeDestination(self) self._reactor.callFromThread(self._reactor.stop) return deferToThreadPool( self._mainReactor, self._mainReactor.getThreadPool(), self._thread.join ) def __call__(self, data): """ Add the data to the queue, to be serialized to JSON and written by the writer thread with a newline added. @param data: C{bytes} to write to disk. """ self._reactor.callFromThread(self._destination, data) def _writer(self): """ The function run by the writer thread. """ self._reactor.run(installSignalHandlers=False)
class ThreadedFileWriter(Service): """ An Eliot log destination that writes log messages as lines to a file, using a managed thread. Unfortunately Python's Queue is not reentrant (http://bugs.python.org/issue14976) and neither is RLock (http://bugs.python.org/issue13697). In order to queue items in a thread we therefore rely on the self-pipe trick, and the easiest way to do that is by running another reactor in the thread. @ivar _reactor: A private reactor running in a thread which will do the log writes. @ivar _thread: C{None}, or a L{threading.Thread} running the private reactor. """ name = u"Eliot Log Writer" def __init__(self, logFile, reactor): """ @param logFile: A C{file}-like object that is at the end of its existing contents (e.g. opened with append mode) and accepts bytes. @type logFile: C{file}, or any file-like object with C{write}, C{flush} and C{close} methods e.g. a L{twisted.python.logfile.LogFile} if you want log rotation. @param reactor: The main reactor. """ self._logFile = logFile self._reactor = Reactor() # Ick. See https://twistedmatrix.com/trac/ticket/6982 for real solution. self._reactor._registerAsIOThread = False self._mainReactor = reactor self._thread = None def startService(self): """ Start the writer thread. """ Service.startService(self) self._thread = threading.Thread(target=self._writer) self._thread.start() addDestination(self) def stopService(self): """ Stop the writer thread, wait for it to finish. """ Service.stopService(self) removeDestination(self) self._reactor.callFromThread(self._reactor.stop) return deferToThreadPool( self._mainReactor, self._mainReactor.getThreadPool(), self._thread.join) def __call__(self, data): """ Write given bytes to the queue, to be written by the writer thread with a newline added. @param data: C{bytes} to write to disk. """ self._reactor.callFromThread(self._logFile.write, data + b'\n') def _writer(self): """ The function run by the writer thread. """ self._reactor.run(installSignalHandlers=False) self._logFile.close()
class ThreadedFileWriter(Service): """ An Eliot log destination that writes log messages as lines to a file, using a managed thread. Unfortunately Python's Queue is not reentrant (http://bugs.python.org/issue14976) and neither is RLock (http://bugs.python.org/issue13697). In order to queue items in a thread we therefore rely on the self-pipe trick, and the easiest way to do that is by running another reactor in the thread. @ivar _reactor: A private reactor running in a thread which will do the log writes. @ivar _thread: C{None}, or a L{threading.Thread} running the private reactor. """ name = u"Eliot Log Writer" def __init__(self, logFile, reactor): """ @param logFile: A C{file}-like object that is at the end of its existing contents (e.g. opened with append mode) and accepts bytes. @type logFile: C{file}, or any file-like object with C{write}, C{flush} and C{close} methods e.g. a L{twisted.python.logfile.LogFile} if you want log rotation. @param reactor: The main reactor. """ self._logFile = logFile self._reactor = Reactor() # Ick. See https://twistedmatrix.com/trac/ticket/6982 for real solution. self._reactor._registerAsIOThread = False self._mainReactor = reactor self._thread = None def startService(self): """ Start the writer thread. """ Service.startService(self) self._thread = threading.Thread(target=self._writer) self._thread.start() addDestination(self) def stopService(self): """ Stop the writer thread, wait for it to finish. """ Service.stopService(self) removeDestination(self) self._reactor.callFromThread(self._reactor.stop) return deferToThreadPool(self._mainReactor, self._mainReactor.getThreadPool(), self._thread.join) def __call__(self, data): """ Write given bytes to the queue, to be written by the writer thread with a newline added. @param data: C{bytes} to write to disk. """ self._reactor.callFromThread(self._logFile.write, data + b'\n') def _writer(self): """ The function run by the writer thread. """ self._reactor.run(installSignalHandlers=False) self._logFile.close()
class ThreadedWriter(Service): """ An non-blocking Eliot log destination that wraps a blocking destination, writing log messages to the latter in a managed thread. Unfortunately Python's Queue is not reentrant (http://bugs.python.org/issue14976) and neither is RLock (http://bugs.python.org/issue13697). In order to queue items in a thread we therefore rely on the self-pipe trick, and the easiest way to do that is by running another reactor in the thread. @ivar _reactor: A private reactor running in a thread which will do the log writes. @ivar _thread: C{None}, or a L{threading.Thread} running the private reactor. """ name = "Eliot Log Writer" def __init__(self, destination, reactor): """ @param destination: The underlying destination for log files. This will be called from a non-reactor thread. @param reactor: The main reactor. """ self._destination = destination self._reactor = Reactor() # Ick. See https://twistedmatrix.com/trac/ticket/6982 for real solution. self._reactor._registerAsIOThread = False self._mainReactor = reactor self._thread = None def startService(self): """ Start the writer thread. """ Service.startService(self) self._thread = threading.Thread(target=self._writer) self._thread.start() addDestination(self) def stopService(self): """ Stop the writer thread, wait for it to finish. """ Service.stopService(self) removeDestination(self) self._reactor.callFromThread(self._reactor.stop) return deferToThreadPool(self._mainReactor, self._mainReactor.getThreadPool(), self._thread.join) def __call__(self, data): """ Add the data to the queue, to be serialized to JSON and written by the writer thread with a newline added. @param data: C{bytes} to write to disk. """ self._reactor.callFromThread(self._destination, data) def _writer(self): """ The function run by the writer thread. """ self._reactor.run(installSignalHandlers=False)