class TestLogHandler(unittest.TestCase): """Test the LogHandler class""" def setUp(self): """Initialization of the tests""" # Create temporary log file for testing the methods self.logPath = "tmp.log" self.generatingRate = 60 self.entryGenerator = EntryGenerator(self.logPath, self.generatingRate) now = datetime.now() # Add a 10 hours old entry self.entryGenerator.write_entry(now - timedelta(hours=10)) # Add two entries to the current time self.entryGenerator.write_entry(now) self.entryGenerator.write_entry(now) # Setup the LogHandler object prior testing self.refreshPeriod = 2 self.alertThreshold = 20 self.monitorDuration = 10 self.logHandler = LogHandler(self.logPath, self.refreshPeriod, self.alertThreshold, self.monitorDuration) # Disable logHandler console output for the tests self.logHandler.printStatus = False def test_add_entry(self): """Tests adding entries to the LogHandler""" print("********************************") print("test_add_entry()") unparsedEntry = self.entryGenerator.generate_entry(datetime.now()) logEntry = LogEntry(unparsedEntry) self.logHandler.add_entry(logEntry) # Check that all the attributes of the entry have been processed self.assertEqual(self.logHandler.log[0], logEntry) self.assertEqual(self.logHandler.hits, 1) self.assertEqual(self.logHandler.size, logEntry.size) self.assertEqual(self.logHandler.sections, {logEntry.section: 1}) self.assertEqual(self.logHandler.ips, {logEntry.ip: 1}) self.assertEqual(self.logHandler.methods, {logEntry.method: 1}) self.assertEqual(self.logHandler.codes, {logEntry.code: 1}) # Put an entry that is not formatted # to check if it's correctly dropped logEntry = LogEntry("This is not a formatted entry\n") self.logHandler.add_entry(logEntry) self.assertEqual(len(self.logHandler.log), 1) self.assertEqual(self.logHandler.hits, 1) def test_delete_entry(self): """Tests deleting entries from the LogHandler""" print("********************************") print("test_delete_entry()") # Add 2 entries and delete the oldest unparsedEntry = self.entryGenerator.generate_entry(datetime.now()) logEntry1 = LogEntry(unparsedEntry) unparsedEntry = self.entryGenerator.generate_entry(datetime.now()) logEntry2 = LogEntry(unparsedEntry) self.logHandler.add_entry(logEntry1) self.logHandler.add_entry(logEntry2) self.logHandler.delete_entry() # logEntry1 should be deleted (oldest) # Check that only logEntry2 is left self.assertEqual(self.logHandler.log[0], logEntry2) self.assertEqual(self.logHandler.hits, 1) self.assertEqual(self.logHandler.size, logEntry2.size) self.assertEqual(self.logHandler.sections, {logEntry2.section: 1}) self.assertEqual(self.logHandler.ips, {logEntry2.ip: 1}) self.assertEqual(self.logHandler.methods, {logEntry2.method: 1}) self.assertEqual(self.logHandler.codes, {logEntry2.code: 1}) def test_read(self): """Test that the LogHandler correctly reads the log and creates a corresponding list of LogEntry objects""" print("********************************") print("test_read()") # Add a non formatted line to the log to check if it's dropped self.entryGenerator.write("this is not a formatted entry\n") self.logHandler.read() # Check that only the two most recent entries have been processed self.assertEqual(len(self.logHandler.log), 2) self.assertEqual(self.logHandler.hits, 2) # Check that all entries are of type LogEntry for entry in self.logHandler.log: self.assertIsInstance(entry, LogEntry) def test_several_reads(self): """Test behaviour of LogHandler when reading several times in a row""" print("********************************") print("test_several_reads()") self.logHandler.read() # Check that only the two most recententries have been processed self.assertEqual(len(self.logHandler.log), 2) # Add a new entry self.entryGenerator.write_entry(datetime.now()) self.logHandler.read() # Check that logHandler has 3 entries in total self.assertEqual(len(self.logHandler.log), 3) def test_run(self): """Test the main monitoring loop of the LogHandler""" print("********************************") print("test_run()") # Start the thread (checks the log every refreshPeriod) self.logHandler.start() # Wait a bit so that logHandler has time to read the log once sleep(0.1 * self.refreshPeriod) updateTime1 = self.logHandler.lastReadTime # Let's wait one and a half refreshPeriod for another read to happen sleep(self.refreshPeriod * 1.5) updateTime2 = self.logHandler.lastReadTime # Time between the 2 reads delta = (updateTime2 - updateTime1).total_seconds() self.logHandler.stop() self.logHandler.join() # Check delta between two log reads is within a 10% error margin # from the specified refreshPeriod self.assertTrue( abs((delta - self.refreshPeriod) / self.refreshPeriod) < 0.1) def test_drop_old_entries(self): """Test the removal of entries older than the monitored period""" print("********************************") print("test_drop_old_entries()") # Create an old entry object in the log oldEntryStr = self.entryGenerator.generate_entry(datetime.now() - timedelta(hours=10)) oldEntry = LogEntry(oldEntryStr) # Create a recent entry newEntryString = self.entryGenerator.generate_entry(datetime.now()) newEntry = LogEntry(newEntryString) # Add them manually to the LogHandler self.logHandler.add_entry(oldEntry) self.logHandler.add_entry(newEntry) self.assertEqual(len(self.logHandler.log), 2) self.logHandler.drop_old_entries() # Only one of the entries should be left self.assertEqual(len(self.logHandler.log), 1) self.assertEqual(self.logHandler.hits, 1) def test_alert(self): """Test alert triggering""" print("********************************") print("test_alert()") self.logHandler.refreshPeriod = 2 self.logHandler.start() self.assertEqual(self.logHandler.alertStatus, False) # Add twice as much entries than the threshold to trigger the alert now = datetime.now() entryCount = int(2 * self.logHandler.alertThreshold * self.logHandler.monitorDuration / 60) for i in range(0, entryCount): self.entryGenerator.write_entry(now) # Wait for the LogHandler to read the log sleep(1.5 * self.logHandler.refreshPeriod) self.logHandler.stop() self.logHandler.join() self.assertTrue(self.logHandler.alertStatus) def test_end_alert(self): """Test the alert ending went traffic went back to normal""" print("********************************") print("test_end_alert()") # Set time frame of monitoring to 1 second to test faster self.logHandler.monitorDuration = 2 self.logHandler.refreshPeriod = 1 self.logHandler.start() self.assertEqual(self.logHandler.alertStatus, False) # Add twice as much entries than the threshold to trigger the alert now = datetime.now() entryCount = int(2 * self.logHandler.alertThreshold * self.logHandler.monitorDuration / 60) for i in range(0, entryCount): self.entryGenerator.write_entry(now) # Wait for the LogHandler to read the log sleep(self.refreshPeriod) self.assertTrue(self.logHandler.alertStatus) # Wait for the LogHandler to remove the entries sleep(1.5 * self.logHandler.monitorDuration) self.logHandler.stop() self.logHandler.join() self.assertFalse(self.logHandler.alertStatus) def test_summary(self): """Test the processing of information contained in the entries""" print("********************************") print("test_summary()") # Write some predefined entries to the log file self.logHandler.monitorDuration = 2 now = datetime.now() # Truncate current datetime to remove microseconds # (for the test to succeed) now = datetime(now.year, now.month, now.day, now.hour, now.minute, now.second) # Disposition required to satisfy PEP8 entries = ( '127.0.0.1 - - [%s +1000] "GET /icons/blank.gif HTTP/1.1" \ 200 100\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '289.8.42.1 - - [%s +1000] "POST /index.html HTTP/1.1" \ 200 1000\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '127.0.0.1 - - [%s +1000] "GET /icons/blank.gif HTTP/1.1" \ 200 900\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '289.8.42.1 - - [%s +1000] "GET /css/display.css HTTP/1.1" \ 403 4000\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '127.0.0.1 - - [%s +1000] "GET /index.php HTTP/1.1" \ 404 1000\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '289.8.42.1 - - [%s +1000] "POST /icons/blank.gif HTTP/1.1" \ 200 9000\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '127.0.0.1 - - [%s +1000] "GET /icons/blank.gif HTTP/1.1" \ 403 4000\n' % now.strftime("%d/%b/%Y:%H:%M:%S")) self.entryGenerator.clear_log() self.entryGenerator.write(entries) self.logHandler.read() # Check that summary information are correct self.assertEqual(self.logHandler.hits, 7) self.assertEqual(self.logHandler.size, 20000) self.assertEqual(self.logHandler.sections, { "icons": 4, "root": 2, "css": 1 }) self.assertEqual(self.logHandler.ips, { "127.0.0.1": 4, "289.8.42.1": 3 }) self.assertEqual(self.logHandler.methods, {"GET": 5, "POST": 2}) self.assertEqual(self.logHandler.codes, {"200": 4, "403": 2, "404": 1})
# Run the HTTP monitor with parameters from parameters.cfg from log_handler import LogHandler import configparser config = configparser.ConfigParser() config.read("parameters.cfg") logPath = str(config.get("Monitor", "logPath")) refreshPeriod = float(config.get("Monitor", "refreshPeriod")) treshold = float(config.get("Monitor", "treshold")) monitorDuration = float(config.get("Monitor", "monitorDuration")) logHandler = LogHandler(logPath, refreshPeriod, treshold, monitorDuration) logHandler.start() # Wait for the logHandler to finish to end the program logHandler.join()
class TestLogHandler(unittest.TestCase): """Test the LogHandler class""" def setUp(self): """Initialization of the tests""" # Create temporary log file for testing the methods self.logPath = "tmp.log" self.generatingRate = 60 self.entryGenerator = EntryGenerator(self.logPath, self.generatingRate) now = datetime.now() # Add a 10 hours old entry self.entryGenerator.write_entry(now - timedelta(hours=10)) # Add two entries to the current time self.entryGenerator.write_entry(now) self.entryGenerator.write_entry(now) # Setup the LogHandler object prior testing self.refreshPeriod = 2 self.alertThreshold = 20 self.monitorDuration = 10 self.logHandler = LogHandler(self.logPath, self.refreshPeriod, self.alertThreshold, self.monitorDuration) # Disable logHandler console output for the tests self.logHandler.printStatus = False def test_add_entry(self): """Tests adding entries to the LogHandler""" print("********************************") print("test_add_entry()") unparsedEntry = self.entryGenerator.generate_entry(datetime.now()) logEntry = LogEntry(unparsedEntry) self.logHandler.add_entry(logEntry) # Check that all the attributes of the entry have been processed self.assertEqual(self.logHandler.log[0], logEntry) self.assertEqual(self.logHandler.hits, 1) self.assertEqual(self.logHandler.size, logEntry.size) self.assertEqual(self.logHandler.sections, {logEntry.section: 1}) self.assertEqual(self.logHandler.ips, {logEntry.ip: 1}) self.assertEqual(self.logHandler.methods, {logEntry.method: 1}) self.assertEqual(self.logHandler.codes, {logEntry.code: 1}) # Put an entry that is not formatted # to check if it's correctly dropped logEntry = LogEntry("This is not a formatted entry\n") self.logHandler.add_entry(logEntry) self.assertEqual(len(self.logHandler.log), 1) self.assertEqual(self.logHandler.hits, 1) def test_delete_entry(self): """Tests deleting entries from the LogHandler""" print("********************************") print("test_delete_entry()") # Add 2 entries and delete the oldest unparsedEntry = self.entryGenerator.generate_entry(datetime.now()) logEntry1 = LogEntry(unparsedEntry) unparsedEntry = self.entryGenerator.generate_entry(datetime.now()) logEntry2 = LogEntry(unparsedEntry) self.logHandler.add_entry(logEntry1) self.logHandler.add_entry(logEntry2) self.logHandler.delete_entry() # logEntry1 should be deleted (oldest) # Check that only logEntry2 is left self.assertEqual(self.logHandler.log[0], logEntry2) self.assertEqual(self.logHandler.hits, 1) self.assertEqual(self.logHandler.size, logEntry2.size) self.assertEqual(self.logHandler.sections, {logEntry2.section: 1}) self.assertEqual(self.logHandler.ips, {logEntry2.ip: 1}) self.assertEqual(self.logHandler.methods, {logEntry2.method: 1}) self.assertEqual(self.logHandler.codes, {logEntry2.code: 1}) def test_read(self): """Test that the LogHandler correctly reads the log and creates a corresponding list of LogEntry objects""" print("********************************") print("test_read()") # Add a non formatted line to the log to check if it's dropped self.entryGenerator.write("this is not a formatted entry\n") self.logHandler.read() # Check that only the two most recent entries have been processed self.assertEqual(len(self.logHandler.log), 2) self.assertEqual(self.logHandler.hits, 2) # Check that all entries are of type LogEntry for entry in self.logHandler.log: self.assertIsInstance(entry, LogEntry) def test_several_reads(self): """Test behaviour of LogHandler when reading several times in a row""" print("********************************") print("test_several_reads()") self.logHandler.read() # Check that only the two most recententries have been processed self.assertEqual(len(self.logHandler.log), 2) # Add a new entry self.entryGenerator.write_entry(datetime.now()) self.logHandler.read() # Check that logHandler has 3 entries in total self.assertEqual(len(self.logHandler.log), 3) def test_run(self): """Test the main monitoring loop of the LogHandler""" print("********************************") print("test_run()") # Start the thread (checks the log every refreshPeriod) self.logHandler.start() # Wait a bit so that logHandler has time to read the log once sleep(0.1*self.refreshPeriod) updateTime1 = self.logHandler.lastReadTime # Let's wait one and a half refreshPeriod for another read to happen sleep(self.refreshPeriod*1.5) updateTime2 = self.logHandler.lastReadTime # Time between the 2 reads delta = (updateTime2 - updateTime1).total_seconds() self.logHandler.stop() self.logHandler.join() # Check delta between two log reads is within a 10% error margin # from the specified refreshPeriod self.assertTrue(abs((delta-self.refreshPeriod) / self.refreshPeriod) < 0.1) def test_drop_old_entries(self): """Test the removal of entries older than the monitored period""" print("********************************") print("test_drop_old_entries()") # Create an old entry object in the log oldEntryStr = self.entryGenerator.generate_entry(datetime.now() - timedelta(hours=10)) oldEntry = LogEntry(oldEntryStr) # Create a recent entry newEntryString = self.entryGenerator.generate_entry(datetime.now()) newEntry = LogEntry(newEntryString) # Add them manually to the LogHandler self.logHandler.add_entry(oldEntry) self.logHandler.add_entry(newEntry) self.assertEqual(len(self.logHandler.log), 2) self.logHandler.drop_old_entries() # Only one of the entries should be left self.assertEqual(len(self.logHandler.log), 1) self.assertEqual(self.logHandler.hits, 1) def test_alert(self): """Test alert triggering""" print("********************************") print("test_alert()") self.logHandler.refreshPeriod = 2 self.logHandler.start() self.assertEqual(self.logHandler.alertStatus, False) # Add twice as much entries than the threshold to trigger the alert now = datetime.now() entryCount = int(2 * self.logHandler.alertThreshold * self.logHandler.monitorDuration / 60) for i in range(0, entryCount): self.entryGenerator.write_entry(now) # Wait for the LogHandler to read the log sleep(1.5*self.logHandler.refreshPeriod) self.logHandler.stop() self.logHandler.join() self.assertTrue(self.logHandler.alertStatus) def test_end_alert(self): """Test the alert ending went traffic went back to normal""" print("********************************") print("test_end_alert()") # Set time frame of monitoring to 1 second to test faster self.logHandler.monitorDuration = 2 self.logHandler.refreshPeriod = 1 self.logHandler.start() self.assertEqual(self.logHandler.alertStatus, False) # Add twice as much entries than the threshold to trigger the alert now = datetime.now() entryCount = int(2 * self.logHandler.alertThreshold * self.logHandler.monitorDuration / 60) for i in range(0, entryCount): self.entryGenerator.write_entry(now) # Wait for the LogHandler to read the log sleep(self.refreshPeriod) self.assertTrue(self.logHandler.alertStatus) # Wait for the LogHandler to remove the entries sleep(1.5*self.logHandler.monitorDuration) self.logHandler.stop() self.logHandler.join() self.assertFalse(self.logHandler.alertStatus) def test_summary(self): """Test the processing of information contained in the entries""" print("********************************") print("test_summary()") # Write some predefined entries to the log file self.logHandler.monitorDuration = 2 now = datetime.now() # Truncate current datetime to remove microseconds # (for the test to succeed) now = datetime(now.year, now.month, now.day, now.hour, now.minute, now.second) # Disposition required to satisfy PEP8 entries = ('127.0.0.1 - - [%s +1000] "GET /icons/blank.gif HTTP/1.1" \ 200 100\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '289.8.42.1 - - [%s +1000] "POST /index.html HTTP/1.1" \ 200 1000\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '127.0.0.1 - - [%s +1000] "GET /icons/blank.gif HTTP/1.1" \ 200 900\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '289.8.42.1 - - [%s +1000] "GET /css/display.css HTTP/1.1" \ 403 4000\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '127.0.0.1 - - [%s +1000] "GET /index.php HTTP/1.1" \ 404 1000\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '289.8.42.1 - - [%s +1000] "POST /icons/blank.gif HTTP/1.1" \ 200 9000\n' % now.strftime("%d/%b/%Y:%H:%M:%S") + '127.0.0.1 - - [%s +1000] "GET /icons/blank.gif HTTP/1.1" \ 403 4000\n' % now.strftime("%d/%b/%Y:%H:%M:%S")) self.entryGenerator.clear_log() self.entryGenerator.write(entries) self.logHandler.read() # Check that summary information are correct self.assertEqual(self.logHandler.hits, 7) self.assertEqual(self.logHandler.size, 20000) self.assertEqual(self.logHandler.sections, {"icons": 4, "root": 2, "css": 1}) self.assertEqual(self.logHandler.ips, {"127.0.0.1": 4, "289.8.42.1": 3}) self.assertEqual(self.logHandler.methods, {"GET": 5, "POST": 2}) self.assertEqual(self.logHandler.codes, {"200": 4, "403": 2, "404": 1})