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
15 from datetime import datetime
16 from fluent import sender
21 class FluentLogHandler(logging.Handler):
22 '''This is a minimalist log handler for use with Fluentd
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)
28 - the runlogdate (to tie multiple run-related logs together)
29 The timestamp is retrieved by the fluentd library.
32 def __init__(self, tag, fluentd_ip='127.0.0.1', fluentd_port=24224):
33 logging.Handler.__init__(self)
35 self.formatter = logging.Formatter('%(message)s')
36 self.sender = sender.FluentSender(self.tag, host=fluentd_ip, port=fluentd_port)
38 self.__warning_counter = 0
39 self.__error_counter = 0
41 def start_new_run(self):
42 '''Delimitate a new run in the stream of records with a new timestamp
44 self.runlogdate = self.__get_timestamp()
46 self.__warning_counter = 0
47 self.__error_counter = 0
49 self.__send_start_record()
51 def emit(self, record):
53 "loglevel": record.levelname,
54 "message": self.formatter.format(record),
55 "@timestamp": self.__get_timestamp()
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
61 self.__update_stats(record.levelno)
62 self.sender.emit(None, data)
64 # this function is called by summarizer
65 def record_send(self, record):
66 self.sender.emit(None, record)
68 # send START record for each run
69 def __send_start_record(self):
71 "runlogdate": self.runlogdate,
73 "message": "NFVBENCH run is started",
77 "@timestamp": self.__get_timestamp()
79 self.sender.emit(None, data)
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:
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()
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)
98 def __get_highest_level(self):
99 if self.__error_counter > 0:
101 elif self.__warning_counter > 0:
102 return logging.WARNING
105 def __get_highest_level_desc(self):
106 highest_level = self.__get_highest_level()
107 if highest_level == logging.INFO:
109 elif highest_level == logging.WARNING:
110 return "RUN WITH WARNINGS"
112 return "RUN WITH ERRORS"
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
120 def __get_timestamp(self):
121 return datetime.utcnow().replace(tzinfo=pytz.utc).strftime(
122 "%Y-%m-%dT%H:%M:%S.%f%z")