from __future__ import absolute_import

import logging
import time
from tornado import ioloop
from django.conf import settings

orig_poll_impl = ioloop._poll

# A hack to keep track of how much time we spend working, versus sleeping in
# the event loop.
#
# Creating a new event loop instance with a custom impl object fails (events
# don't get processed), so instead we modify the ioloop module variable holding
# the default poll implementation.  We need to do this before any Tornado code
# runs that might instantiate the default event loop.

class InstrumentedPoll(object):
    def __init__(self):
        self._underlying = orig_poll_impl()
        self._times = []
        self._last_print = 0

    # Python won't let us subclass e.g. select.epoll, so instead
    # we proxy every method.  __getattr__ handles anything we
    # don't define elsewhere.
    def __getattr__(self, name):
        return getattr(self._underlying, name)

    # Call the underlying poll method, and report timing data.
    def poll(self, timeout):
        # Avoid accumulating a bunch of insignificant data points
        # from short timeouts.
        if timeout < 1e-3:
            return self._underlying.poll(timeout)

        # Record start and end times for the underlying poll
        t0 = time.time()
        result = self._underlying.poll(timeout)
        t1 = time.time()

        # Log this datapoint and restrict our log to the past minute
        self._times.append((t0, t1))
        while self._times and self._times[0][0] < t1 - 60:
            self._times.pop(0)

        # Report (at most once every 5s) the percentage of time spent
        # outside poll
        if self._times and t1 - self._last_print >= 5:
            total = t1 - self._times[0][0]
            in_poll = sum(b-a for a,b in self._times)
            if total > 0:
                percent_busy = 100 * (1 - in_poll/total)
                if settings.DEPLOYED or percent_busy > 20:
                    logging.info('Tornado %5.1f%% busy over the past %4.1f seconds'
                        % (percent_busy, total))
                    self._last_print = t1

        return result

def instrument_tornado_ioloop():
    ioloop._poll = InstrumentedPoll

@@ -60,16 +60,30 @@
 rt time%0A
+import select%0A
 from tor
@@ -135,16 +135,43 @@
 ttings%0A%0A
+try:%0A    # Tornado 2.4%0A    
 orig_pol
@@ -191,16 +191,644 @@
 op._poll
+%0A    def instrument_tornado_ioloop():%0A        ioloop._poll = InstrumentedPoll%0Aexcept:%0A    # Tornado 3%0A    from tornado.ioloop import IOLoop, PollIOLoop%0A    # There isn't a good way to get at what the underlying poll implementation%0A    # will be without actually constructing an IOLoop, so we just assume it will%0A    # be epoll.%0A    orig_poll_impl = select.epoll%0A    class InstrumentedPollIOLoop(PollIOLoop):%0A        def initialize(self, **kwargs):%0A            super(InstrumentedPollIOLoop, self).initialize(impl=InstrumentedPoll(), **kwargs)%0A%0A    def instrument_tornado_ioloop():%0A        IOLoop.configure(InstrumentedPollIOLoop)
 %0A%0A# A ha
@@ -2815,74 +2815,4 @@
 ult%0A
-%0Adef instrument_tornado_ioloop():%0A    ioloop._poll = InstrumentedPoll%0A

Support Tornado 3