Update to py38
[nfvbench.git] / nfvbench / fluentd.py
index 683d4ce..535d640 100644 (file)
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import logging
+
 from datetime import datetime
 from fluent import sender
-import logging
+import pytz
+
 
 class FluentLogHandler(logging.Handler):
     '''This is a minimalist log handler for use with Fluentd
@@ -25,23 +28,110 @@ class FluentLogHandler(logging.Handler):
     - the level name
     - the runlogdate (to tie multiple run-related logs together)
     The timestamp is retrieved by the fluentd library.
+    There will be only one instance of FluentLogHandler running.
     '''
-    def __init__(self, tag, fluentd_ip='127.0.0.1', fluentd_port=24224):
+
+    def __init__(self, fluentd_configs):
         logging.Handler.__init__(self)
-        self.tag = tag
+        self.log_senders = []
+        self.result_senders = []
+        self.runlogdate = "1970-01-01T00:00:00.000000+0000"
         self.formatter = logging.Formatter('%(message)s')
-        self.sender = sender.FluentSender(self.tag, port=fluentd_port)
-        self.start_new_run()
+        for fluentd_config in fluentd_configs:
+            if fluentd_config.logging_tag:
+                self.log_senders.append(
+                    sender.FluentSender(fluentd_config.logging_tag, host=fluentd_config.ip,
+                                        port=fluentd_config.port))
+            if fluentd_config.result_tag:
+                self.result_senders.append(
+                    sender.FluentSender(fluentd_config.result_tag, host=fluentd_config.ip,
+                                        port=fluentd_config.port))
+        self.__warning_counter = 0
+        self.__error_counter = 0
 
     def start_new_run(self):
         '''Delimitate a new run in the stream of records with a new timestamp
         '''
-        self.runlogdate = str(datetime.now())
+        # reset counters
+        self.__warning_counter = 0
+        self.__error_counter = 0
+        self.runlogdate = self.__get_timestamp()
+        # send start record
+        self.__send_start_record()
 
     def emit(self, record):
         data = {
-            "runlogdate": self.runlogdate,
             "loglevel": record.levelname,
-            "message": self.formatter.format(record)
+            "message": self.formatter.format(record),
+            "@timestamp": self.__get_timestamp()
         }
-        self.sender.emit(None, data)
+        # if runlogdate is Jan 1st 1970, it's a log from server (not an nfvbench run)
+        # so do not send runlogdate
+        if self.runlogdate != "1970-01-01T00:00:00.000000+0000":
+            data["runlogdate"] = self.runlogdate
+
+        self.__update_stats(record.levelno)
+        for log_sender in self.log_senders:
+            log_sender.emit(None, data)
+
+    # this function is called by summarizer, and used for sending results
+    def record_send(self, record):
+        for result_sender in self.result_senders:
+            result_sender.emit(None, record)
+
+    # send START log record for each run
+    def __send_start_record(self):
+        data = {
+            "runlogdate": self.runlogdate,
+            "loglevel": "START",
+            "message": "NFVBENCH run is started",
+            "numloglevel": 0,
+            "numerrors": 0,
+            "numwarnings": 0,
+            "@timestamp": self.__get_timestamp()
+        }
+        for log_sender in self.log_senders:
+            log_sender.emit(None, data)
+
+    # send stats related to the current run and reset state for a new run
+    def send_run_summary(self, run_summary_required):
+        if run_summary_required or self.__get_highest_level() == logging.ERROR:
+            data = {
+                "loglevel": "RUN_SUMMARY",
+                "message": self.__get_highest_level_desc(),
+                "numloglevel": self.__get_highest_level(),
+                "numerrors": self.__error_counter,
+                "numwarnings": self.__warning_counter,
+                "@timestamp": self.__get_timestamp()
+            }
+            # if runlogdate is Jan 1st 1970, it's a log from server (not an nfvbench run)
+            # so don't send runlogdate
+            if self.runlogdate != "1970-01-01T00:00:00.000000+0000":
+                data["runlogdate"] = self.runlogdate
+            for log_sender in self.log_senders:
+                log_sender.emit(None, data)
+
+    def __get_highest_level(self):
+        if self.__error_counter > 0:
+            return logging.ERROR
+        if self.__warning_counter > 0:
+            return logging.WARNING
+        return logging.INFO
+
+    def __get_highest_level_desc(self):
+        highest_level = self.__get_highest_level()
+        if highest_level == logging.INFO:
+            return "GOOD RUN"
+        if highest_level == logging.WARNING:
+            return "RUN WITH WARNINGS"
+        return "RUN WITH ERRORS"
+
+    def __update_stats(self, levelno):
+        if levelno == logging.WARNING:
+            self.__warning_counter += 1
+        elif levelno == logging.ERROR:
+            self.__error_counter += 1
+
+    def __get_timestamp(self):
+        return datetime.utcnow().replace(tzinfo=pytz.utc).strftime(
+            "%Y-%m-%dT%H:%M:%S.%f%z")