Advertisement
Guest User

Untitled

a guest
Jun 20th, 2017
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.08 KB | None | 0 0
  1. # python playercounter.py /path/to/server.log
  2.  
  3. DB_USER = ""
  4. DB_PASS = ""
  5. DB_NAME = ""
  6. DB_HOST = ""
  7.  
  8. import datetime
  9. import MySQLdb
  10. import os.path
  11. import os
  12. import sys
  13. import re
  14.  
  15. from collections import defaultdict
  16. from stat import ST_SIZE
  17. from time import sleep, time
  18.  
  19. # DB INFORMATION
  20. db = MySQLdb.connect(user=DB_USER, passwd=DB_PASS, db=DB_NAME, host=DB_HOST)
  21.  
  22. class Tail(object):
  23.     """The Tail monitor object."""
  24.    
  25.     def __init__(self, path, only_new = False,
  26.                  min_sleep = 1,
  27.                  sleep_interval = 1,
  28.                  max_sleep = 60):
  29.         """Initialize a tail monitor.
  30.             path: filename to open
  31.             only_new: By default, the tail monitor will start reading from
  32.               the beginning of the file when first opened. Set only_new to
  33.               True to have it skip to the end when it first opens, so that
  34.               you only get the new additions that arrive after you start
  35.               monitoring.
  36.             min_sleep: Shortest interval in seconds to sleep when waiting
  37.               for more input to arrive. Defaults to 1.0 second.
  38.             sleep_interval: The tail monitor will dynamically recompute an
  39.               appropriate sleep interval based on a sliding window of data
  40.               arrival rate. You can set sleep_interval here to seed it
  41.               initially if the default of 1.0 second doesn't work for you
  42.               and you don't want to wait for it to converge.
  43.             max_sleep: Maximum interval in seconds to sleep when waiting
  44.               for more input to arrive. Also, if this many seconds have
  45.               elapsed without getting any new data, the tail monitor will
  46.               check to see if the log got truncated (rotated) and will
  47.               quietly reopen itself if this was the case. Defaults to 60.0
  48.               seconds.
  49.        """
  50.  
  51.         # remember path to file in case I need to reopen
  52.         self.path = os.path.abspath(path)
  53.         self.f = open(self.path,"r")
  54.         self.min_sleep = min_sleep * 1.0
  55.         self.sleep_interval = sleep_interval * 1.0
  56.         self.max_sleep = max_sleep * 1.0
  57.         if only_new:
  58.             # seek to current end of file
  59.             file_len = os.stat(path)[ST_SIZE]
  60.             self.f.seek(file_len)
  61.         self.pos = self.f.tell()        # where am I in the file?
  62.         self.last_read = time()         # when did I last get some data?
  63.         self.queue = []                 # queue of lines that are ready
  64.         self.window = []                # sliding window for dynamically
  65.                                         # adjusting the sleep_interval
  66.  
  67.     def _recompute_rate(self, n, start, stop):
  68.         """Internal function for recomputing the sleep interval. I get
  69.        called with a number of lines that appeared between the start and
  70.        stop times; this will get added to a sliding window, and I will
  71.        recompute the average interarrival rate over the last window.
  72.        """
  73.         self.window.append((n, start, stop))
  74.         purge_idx = -1                  # index of the highest old record
  75.         tot_n = 0                       # total arrivals in the window
  76.         tot_start = stop                # earliest time in the window
  77.         tot_stop = start                # latest time in the window
  78.         for i, record in enumerate(self.window):
  79.             (i_n, i_start, i_stop) = record
  80.             if i_stop < start - self.max_sleep:
  81.                 # window size is based on self.max_sleep; this record has
  82.                 # fallen out of the window
  83.                 purge_idx = i
  84.             else:
  85.                 tot_n += i_n
  86.                 if i_start < tot_start: tot_start = i_start
  87.                 if i_stop > tot_stop: tot_stop = i_stop
  88.         if purge_idx >= 0:
  89.             # clean the old records out of the window (slide the window)
  90.             self.window = self.window[purge_idx+1:]
  91.         if tot_n > 0:
  92.             # recompute; stay within bounds
  93.             self.sleep_interval = (tot_stop - tot_start) / tot_n
  94.             if self.sleep_interval > self.max_sleep:
  95.                 self.sleep_interval = self.max_sleep
  96.             if self.sleep_interval < self.min_sleep:
  97.                 self.sleep_interval = self.min_sleep
  98.  
  99.     def _fill_cache(self):
  100.         """Internal method for grabbing as much data out of the file as is
  101.        available and caching it for future calls to nextline(). Returns
  102.        the number of lines just read.
  103.        """
  104.         old_len = len(self.queue)
  105.         line = self.f.readline()
  106.         while line != "":
  107.             self.queue.append(line)
  108.             line = self.f.readline()
  109.         # how many did we just get?
  110.         num_read = len(self.queue) - old_len
  111.         if num_read > 0:
  112.             self.pos = self.f.tell()
  113.             now = time()
  114.             self._recompute_rate(num_read, self.last_read, now)
  115.             self.last_read = now
  116.         return num_read
  117.  
  118.     def _dequeue(self):
  119.         """Internal method; returns the first available line out of the
  120.        cache, if any."""
  121.         if len(self.queue) > 0:
  122.             line = self.queue[0]
  123.             self.queue = self.queue[1:]
  124.             return line
  125.         else:
  126.             return None
  127.  
  128.     def _reset(self):
  129.         """Internal method; reopen the internal file handle (probably
  130.        because the log file got rotated/truncated)."""
  131.         self.f.close()
  132.         self.f = open(self.path, "r")
  133.         self.pos = self.f.tell()
  134.         self.last_read = time()
  135.  
  136.     def nextline(self):
  137.         """Return the next line from the file. Blocks if there are no lines
  138.        immediately available."""
  139.  
  140.         # see if we have any lines cached from the last file read
  141.         line = self._dequeue()
  142.         if line:
  143.             return line
  144.        
  145.         # ok, we are out of cache; let's get some lines from the file
  146.         if self._fill_cache() > 0:
  147.             # got some
  148.             return self._dequeue()
  149.  
  150.         # hmm, still no input available
  151.         while True:
  152.             sleep(self.sleep_interval)
  153.             if self._fill_cache() > 0:
  154.                 return self._dequeue()
  155.             now = time()
  156.             if (now - self.last_read > self.max_sleep):
  157.                 # maybe the log got rotated out from under us?
  158.                 if os.stat(self.path)[ST_SIZE] < self.pos:
  159.                     # file got truncated and/or re-created
  160.                     self._reset()
  161.                     if self._fill_cache() > 0:
  162.                         return self._dequeue()
  163.  
  164.     def close(self):
  165.         """Close the tail monitor, discarding any remaining input."""
  166.         self.f.close()
  167.         self.f = None
  168.         self.queue = []
  169.         self.window = []
  170.  
  171.     def __iter__(self):
  172.         """Iterator interface, so you can do:
  173.  
  174.        for line in filetail.Tail('log.txt'):
  175.            # do stuff
  176.            pass
  177.        """
  178.         return self
  179.  
  180.     def next(self):
  181.         """Kick the iterator interface. Used under the covers to support:
  182.  
  183.        for line in filetail.Tail('log.txt'):
  184.            # do stuff
  185.            pass
  186.        """
  187.         return self.nextline()
  188.  
  189. class LogParser(object):
  190.     def __init__(self, logfile, server_id=1):
  191.         self.visitor_stack = {}
  192.         self.logfile = logfile
  193.         self.server_id = server_id
  194.    
  195.     def handle_disconnect(self, player, date):
  196.         visitor_id = self.visitor_stack.pop(player, None)
  197.         if visitor_id:
  198.             print "%s (%s) has disconnected" % (player, visitor_id)
  199.             cursor = db.cursor()
  200.             cursor.execute("update mc_visitor set date_left = %s where id = %s", [date, visitor_id])
  201.             cursor.close()
  202.        
  203.     def handle_connect(self, player, date):
  204.         self.handle_disconnect(player, date)
  205.         cursor = db.cursor()
  206.         cursor.execute('insert ignore into mc_visitor (name, date_joined, server_id) values(%s, %s, %s)', [player, date, self.server_id])
  207.         visitor_id = db.insert_id()
  208.         if not visitor_id:
  209.             cursor.execute('select id from mc_visitor where name = %s and server_id = %s and date_joined = %s', [player, self.server_id, date])
  210.             visitor_id = cursor.fetchone()[0]
  211.         cursor.close()
  212.         self.visitor_stack[player] = visitor_id
  213.         print "%s (%s) has connected" % (player, visitor_id)
  214.  
  215.     def begin(self):
  216.         for line in Tail(self.logfile):
  217.             line = line.strip()
  218.             try:
  219.                 broken = line.split(' ')
  220.                 tokens = broken[3:]
  221.             except IndexError:
  222.                 continue
  223.  
  224.             if not tokens:
  225.                 continue
  226.            
  227.             date = ' '.join(broken[0:2])
  228.             try:
  229.                 date = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S')
  230.             except Exception, exc:
  231.                 print exc
  232.                 continue
  233.  
  234.             if tokens[0].startswith('<') or tokens[0].startswith('/') or tokens[0].startswith('*'):
  235.                 continue
  236.            
  237.             player = None
  238.             if 'Starting minecraft server version' in line:
  239.                 for player in self.visitor_stack.keys():
  240.                     self.handle_disconnect(player, date)
  241.                 self.visitor_stack = {}
  242.             elif ' lost connection' in line:
  243.                 player = tokens[0]
  244.                 self.handle_disconnect(player, date)
  245.             elif line.endswith(' logged in'):
  246.                 player = tokens[0]
  247.                
  248.                 # this shouldnt happen
  249.                 self.handle_connect(player, date)
  250.             elif ' Banning ' in line or ' Kicking ' in line:
  251.                 player = tokens[-1]
  252.                 self.handle_disconnect(player, date)
  253.  
  254. def main(logfile):
  255.     LogParser(logfile).begin()
  256.  
  257. if __name__ == '__main__':
  258.     LogPath = sys.argv[1]
  259.     pattern = re.compile (r"^server_[0-9]{10}\.log$" )
  260.     files = os.listdir(LogPath)
  261.     files = [f for f in files if re.search(pattern, f, re.I)]
  262.     files.sort()
  263.     LastEntry = len(files)-1
  264.     LastLog = files[LastEntry]
  265.     main(LogPath+LastLog)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement