Add Kibana visualizations examples for NDR result
[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 import logging
16
17 from datetime import datetime
18 from fluent import sender
19 import pytz
20
21
22 class FluentLogHandler(logging.Handler):
23     '''This is a minimalist log handler for use with Fluentd
24
25     Needs to be attached to a logger using the addHandler method.
26     It only picks up from every record:
27     - the formatted message (no timestamp and no level)
28     - the level name
29     - the runlogdate (to tie multiple run-related logs together)
30     The timestamp is retrieved by the fluentd library.
31     There will be only one instance of FluentLogHandler running.
32     '''
33
34     def __init__(self, fluentd_configs):
35         logging.Handler.__init__(self)
36         self.log_senders = []
37         self.result_senders = []
38         self.runlogdate = "1970-01-01T00:00:00.000000+0000"
39         self.formatter = logging.Formatter('%(message)s')
40         for fluentd_config in fluentd_configs:
41             if fluentd_config.logging_tag:
42                 self.log_senders.append(
43                     sender.FluentSender(fluentd_config.logging_tag, host=fluentd_config.ip,
44                                         port=fluentd_config.port))
45             if fluentd_config.result_tag:
46                 self.result_senders.append(
47                     sender.FluentSender(fluentd_config.result_tag, host=fluentd_config.ip,
48                                         port=fluentd_config.port))
49         self.__warning_counter = 0
50         self.__error_counter = 0
51
52     def start_new_run(self):
53         '''Delimitate a new run in the stream of records with a new timestamp
54         '''
55         # reset counters
56         self.__warning_counter = 0
57         self.__error_counter = 0
58         self.runlogdate = self.__get_timestamp()
59         # send start record
60         self.__send_start_record()
61
62     def emit(self, record):
63         data = {
64             "loglevel": record.levelname,
65             "message": self.formatter.format(record),
66             "@timestamp": self.__get_timestamp()
67         }
68         # if runlogdate is Jan 1st 1970, it's a log from server (not an nfvbench run)
69         # so do not send runlogdate
70         if self.runlogdate != "1970-01-01T00:00:00.000000+0000":
71             data["runlogdate"] = self.runlogdate
72
73         self.__update_stats(record.levelno)
74         for log_sender in self.log_senders:
75             log_sender.emit(None, data)
76
77     # this function is called by summarizer, and used for sending results
78     def record_send(self, record):
79         for result_sender in self.result_senders:
80             result_sender.emit(None, record)
81
82     # send START log record for each run
83     def __send_start_record(self):
84         data = {
85             "runlogdate": self.runlogdate,
86             "loglevel": "START",
87             "message": "NFVBENCH run is started",
88             "numloglevel": 0,
89             "numerrors": 0,
90             "numwarnings": 0,
91             "@timestamp": self.__get_timestamp()
92         }
93         for log_sender in self.log_senders:
94             log_sender.emit(None, data)
95
96     # send stats related to the current run and reset state for a new run
97     def send_run_summary(self, run_summary_required):
98         if run_summary_required or self.__get_highest_level() == logging.ERROR:
99             data = {
100                 "loglevel": "RUN_SUMMARY",
101                 "message": self.__get_highest_level_desc(),
102                 "numloglevel": self.__get_highest_level(),
103                 "numerrors": self.__error_counter,
104                 "numwarnings": self.__warning_counter,
105                 "@timestamp": self.__get_timestamp()
106             }
107             # if runlogdate is Jan 1st 1970, it's a log from server (not an nfvbench run)
108             # so don't send runlogdate
109             if self.runlogdate != "1970-01-01T00:00:00.000000+0000":
110                 data["runlogdate"] = self.runlogdate
111             for log_sender in self.log_senders:
112                 log_sender.emit(None, data)
113
114     def __get_highest_level(self):
115         if self.__error_counter > 0:
116             return logging.ERROR
117         if self.__warning_counter > 0:
118             return logging.WARNING
119         return logging.INFO
120
121     def __get_highest_level_desc(self):
122         highest_level = self.__get_highest_level()
123         if highest_level == logging.INFO:
124             return "GOOD RUN"
125         if highest_level == logging.WARNING:
126             return "RUN WITH WARNINGS"
127         return "RUN WITH ERRORS"
128
129     def __update_stats(self, levelno):
130         if levelno == logging.WARNING:
131             self.__warning_counter += 1
132         elif levelno == logging.ERROR:
133             self.__error_counter += 1
134
135     def __get_timestamp(self):
136         return datetime.utcnow().replace(tzinfo=pytz.utc).strftime(
137             "%Y-%m-%dT%H:%M:%S.%f%z")