1# Copyright (C) 2017 Open Information Security Foundation 2# Copyright (c) 2016 Jason Ish 3# 4# You can copy, redistribute or modify this Program under the terms of 5# the GNU General Public License version 2 as published by the Free 6# Software Foundation. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# version 2 along with this program; if not, write to the Free Software 15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 16# 02110-1301, USA. 17 18import sys 19import os 20import logging 21import time 22 23# A list of secrets that will be replaced in the log output. 24secrets = {} 25 26 27def add_secret(secret, replacement): 28 """Register a secret to be masked. The secret will be replaced with: 29 <replacement> 30 """ 31 secrets[str(secret)] = str(replacement) 32 33 34class SuriColourLogHandler(logging.StreamHandler): 35 """An alternative stream log handler that logs with Suricata inspired 36 log colours.""" 37 38 GREEN = "\x1b[32m" 39 BLUE = "\x1b[34m" 40 REDB = "\x1b[1;31m" 41 YELLOW = "\x1b[33m" 42 RED = "\x1b[31m" 43 YELLOWB = "\x1b[1;33m" 44 ORANGE = "\x1b[38;5;208m" 45 RESET = "\x1b[0m" 46 47 def formatTime(self, record): 48 lt = time.localtime(record.created) 49 t = "%d/%d/%d -- %02d:%02d:%02d" % (lt.tm_mday, 50 lt.tm_mon, 51 lt.tm_year, 52 lt.tm_hour, 53 lt.tm_min, 54 lt.tm_sec) 55 return "%s" % (t) 56 57 def emit(self, record): 58 59 if record.levelname == "ERROR": 60 level_prefix = self.REDB 61 message_prefix = self.REDB 62 elif record.levelname == "WARNING": 63 level_prefix = self.ORANGE 64 message_prefix = self.ORANGE 65 else: 66 level_prefix = self.YELLOW 67 message_prefix = "" 68 69 if os.isatty(self.stream.fileno()): 70 self.stream.write("%s%s%s - <%s%s%s> -- %s%s%s\n" % ( 71 self.GREEN, 72 self.formatTime(record), 73 self.RESET, 74 level_prefix, 75 record.levelname.title(), 76 self.RESET, 77 message_prefix, 78 self.mask_secrets(record.getMessage()), 79 self.RESET)) 80 else: 81 self.stream.write("%s - <%s> -- %s\n" % ( 82 self.formatTime(record), 83 record.levelname.title(), 84 self.mask_secrets(record.getMessage()))) 85 86 def mask_secrets(self, msg): 87 for secret in secrets: 88 msg = msg.replace(secret, "<%s>" % secrets[secret]) 89 return msg 90 91 92class LessThanFilter(logging.Filter): 93 def __init__(self, exclusive_maximum, name=""): 94 super(LessThanFilter, self).__init__(name) 95 self.max_level = exclusive_maximum 96 97 def filter(self, record): 98 return 1 if record.levelno < self.max_level else 0 99 100 101def configure_logging(): 102 if os.fstat(sys.stdout.fileno()) == os.fstat(sys.stderr.fileno()): 103 filter_stdout = True 104 else: 105 filter_stdout = False 106 logger = logging.getLogger() 107 logger.setLevel(logging.NOTSET) 108 logging_handler_out = SuriColourLogHandler(sys.stdout) 109 logging_handler_out.setLevel(logging.DEBUG) 110 if filter_stdout: 111 logging_handler_out.addFilter(LessThanFilter(logging.WARNING)) 112 logger.addHandler(logging_handler_out) 113 logging_handler_err = SuriColourLogHandler(sys.stderr) 114 logging_handler_err.setLevel(logging.WARNING) 115 logger.addHandler(logging_handler_err) 116