NFVBENCH-25 Send run results to fluentd
[nfvbench.git] / nfvbench / fluentd.py
1 # Copyright 2017 Cisco Systems, Inc.  All rights reserved.
2 #
3 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
4 #    not use this file except in compliance with the License. You may obtain
5 #    a copy of the License at
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
9 #    Unless required by applicable law or agreed to in writing, software
10 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 #    License for the specific language governing permissions and limitations
13 #    under the License.
14
15 from datetime import datetime
16 from fluent import sender
17 import logging
18 import pytz
19
20
21 class FluentLogHandler(logging.Handler):
22     '''This is a minimalist log handler for use with Fluentd
23
24     Needs to be attached to a logger using the addHandler method.
25     It only picks up from every record:
26     - the formatted message (no timestamp and no level)
27     - the level name
28     - the runlogdate (to tie multiple run-related logs together)
29     The timestamp is retrieved by the fluentd library.
30     '''
31
32     def __init__(self, tag, fluentd_ip='127.0.0.1', fluentd_port=24224):
33         logging.Handler.__init__(self)
34         self.tag = tag
35         self.formatter = logging.Formatter('%(message)s')
36         self.sender = sender.FluentSender(self.tag, host=fluentd_ip, port=fluentd_port)
37         self.runlogdate = 0
38         self.__warning_counter = 0
39         self.__error_counter = 0
40
41     def start_new_run(self):
42         '''Delimitate a new run in the stream of records with a new timestamp
43         '''
44         self.runlogdate = self.__get_timestamp()
45         # reset counters
46         self.__warning_counter = 0
47         self.__error_counter = 0
48         # send start record
49         self.__send_start_record()
50
51     def emit(self, record):
52         data = {
53             "loglevel": record.levelname,
54             "message": self.formatter.format(record),
55             "@timestamp": self.__get_timestamp()
56         }
57         # if runlogdate is 0, it's a log from server (not an nfvbench run) so do not send runlogdate
58         if self.runlogdate != 0:
59             data["runlogdate"] = self.runlogdate
60
61         self.__update_stats(record.levelno)
62         self.sender.emit(None, data)
63
64     # this function is called by summarizer
65     def record_send(self, record):
66         self.sender.emit(None, record)
67
68     # send START record for each run
69     def __send_start_record(self):
70         data = {
71             "runlogdate": self.runlogdate,
72             "loglevel": "START",
73             "message": "NFVBENCH run is started",
74             "numloglevel": 0,
75             "numerrors": 0,
76             "numwarnings": 0,
77             "@timestamp": self.__get_timestamp()
78         }
79         self.sender.emit(None, data)
80
81     # send stats related to the current run and reset state for a new run
82     def send_run_summary(self, run_summary_required):
83         if run_summary_required or self.__get_highest_level() == logging.ERROR:
84             data = {
85                 "loglevel": "RUN_SUMMARY",
86                 "message": self.__get_highest_level_desc(),
87                 "numloglevel": self.__get_highest_level(),
88                 "numerrors": self.__error_counter,
89                 "numwarnings": self.__warning_counter,
90                 "@timestamp": self.__get_timestamp()
91             }
92             # if runlogdate is 0, it's a log from server (not an nfvbench run)
93             # so don't send runlogdate
94             if self.runlogdate != 0:
95                 data["runlogdate"] = self.runlogdate
96             self.sender.emit(None, data)
97
98     def __get_highest_level(self):
99         if self.__error_counter > 0:
100             return logging.ERROR
101         elif self.__warning_counter > 0:
102             return logging.WARNING
103         return logging.INFO
104
105     def __get_highest_level_desc(self):
106         highest_level = self.__get_highest_level()
107         if highest_level == logging.INFO:
108             return "GOOD RUN"
109         elif highest_level == logging.WARNING:
110             return "RUN WITH WARNINGS"
111         else:
112             return "RUN WITH ERRORS"
113
114     def __update_stats(self, levelno):
115         if levelno == logging.WARNING:
116             self.__warning_counter += 1
117         elif levelno == logging.ERROR:
118             self.__error_counter += 1
119
120     def __get_timestamp(self):
121         return datetime.utcnow().replace(tzinfo=pytz.utc).strftime(
122             "%Y-%m-%dT%H:%M:%S.%f%z")