1 # Copyright 2017 Cisco Systems, Inc. All rights reserved.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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
17 from datetime import datetime
18 from fluent import sender
22 class FluentLogHandler(logging.Handler):
23 '''This is a minimalist log handler for use with Fluentd
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)
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.
34 def __init__(self, fluentd_configs):
35 logging.Handler.__init__(self)
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
52 def start_new_run(self):
53 '''Delimitate a new run in the stream of records with a new timestamp
56 self.__warning_counter = 0
57 self.__error_counter = 0
58 self.runlogdate = self.__get_timestamp()
60 self.__send_start_record()
62 def emit(self, record):
64 "loglevel": record.levelname,
65 "message": self.formatter.format(record),
66 "@timestamp": self.__get_timestamp()
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
73 self.__update_stats(record.levelno)
74 for log_sender in self.log_senders:
75 log_sender.emit(None, data)
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)
82 # send START log record for each run
83 def __send_start_record(self):
85 "runlogdate": self.runlogdate,
87 "message": "NFVBENCH run is started",
91 "@timestamp": self.__get_timestamp()
93 for log_sender in self.log_senders:
94 log_sender.emit(None, data)
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:
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()
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)
114 def __get_highest_level(self):
115 if self.__error_counter > 0:
117 if self.__warning_counter > 0:
118 return logging.WARNING
121 def __get_highest_level_desc(self):
122 highest_level = self.__get_highest_level()
123 if highest_level == logging.INFO:
125 if highest_level == logging.WARNING:
126 return "RUN WITH WARNINGS"
127 return "RUN WITH ERRORS"
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
135 def __get_timestamp(self):
136 return datetime.utcnow().replace(tzinfo=pytz.utc).strftime(
137 "%Y-%m-%dT%H:%M:%S.%f%z")