From e8f563deebde0fdff12892ebb0c12c3b13ea22c2 Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Mon, 24 Apr 2023 04:36:02 -0400 Subject: [PATCH] #1529 Adds logging suppression to Broadcaster to limit total logged error messages. (#1530) Co-authored-by: Dennis Sheirer --- .../dsheirer/log/LoggingSuppressor.java | 139 ++++++++++++++++++ .../github/dsheirer/sample/Broadcaster.java | 29 +++- 2 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 src/main/java/io/github/dsheirer/log/LoggingSuppressor.java diff --git a/src/main/java/io/github/dsheirer/log/LoggingSuppressor.java b/src/main/java/io/github/dsheirer/log/LoggingSuppressor.java new file mode 100644 index 000000000..87c9ee652 --- /dev/null +++ b/src/main/java/io/github/dsheirer/log/LoggingSuppressor.java @@ -0,0 +1,139 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.log; + +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; + +/** + * Logging suppressor that squelches log messages after they have been logged and continue to occur. + */ +public class LoggingSuppressor +{ + private Map mSuppressionCountMap = new HashMap<>(); + private Logger mLogger; + + /** + * Constructs an instance + * @param logger to receive suppressed log messages. + */ + public LoggingSuppressor(Logger logger) + { + mLogger = logger; + } + + /** + * Logs the information message. + * @param key for suppression. + * @param maxCount of the logged instance. + * @param message to log. + */ + public void info(String key, int maxCount, String message) + { + if(canLog(key, maxCount)) + { + mLogger.info(message + getTag(key, maxCount)); + } + } + + /** + * Logs the information message. + * @param key for suppression. + * @param maxCount of the logged instance. + * @param message to log. + * @param t stack trace to include. + */ + public void info(String key, int maxCount, String message, Throwable t) + { + if(canLog(key, maxCount)) + { + mLogger.info(message + getTag(key, maxCount), t); + } + } + + /** + * Logs the error message. + * @param key for suppression. + * @param maxCount of the logged instance. + * @param message to log. + */ + public void error(String key, int maxCount, String message) + { + if(canLog(key, maxCount)) + { + mLogger.error(message + getTag(key, maxCount)); + } + } + + /** + * Logs the error message. + * @param key for suppression. + * @param maxCount of the logged instance. + * @param message to log. + * @param t stack trace to include. + */ + public void error(String key, int maxCount, String message, Throwable t) + { + if(canLog(key, maxCount)) + { + mLogger.error(message + getTag(key, maxCount), t); + } + } + + /** + * Creates a logging tag that logs the suppression statistics. + * @param key for retrieving current count. + * @param maxCount to log. + * @return logging tag + */ + private String getTag(String key, int maxCount) + { + int count = mSuppressionCountMap.get(key); + return " [Log Suppress " + count + "/" + maxCount + "]"; + } + + /** + * Indicates if the operation can be logged without exceeding the max logging count specified by the user. + * @param key for tracking by count + * @param maxCount of the key to log + * @return true if can log or false. + */ + private boolean canLog(String key, int maxCount) + { + if(mSuppressionCountMap.containsKey(key)) + { + int count = mSuppressionCountMap.get(key); + + if(count >= maxCount) + { + return false; + } + + mSuppressionCountMap.put(key, count + 1); + } + else + { + mSuppressionCountMap.put(key, 1); + } + + return true; + } +} diff --git a/src/main/java/io/github/dsheirer/sample/Broadcaster.java b/src/main/java/io/github/dsheirer/sample/Broadcaster.java index 3ba9e0f82..21386aa7d 100644 --- a/src/main/java/io/github/dsheirer/sample/Broadcaster.java +++ b/src/main/java/io/github/dsheirer/sample/Broadcaster.java @@ -18,10 +18,10 @@ */ package io.github.dsheirer.sample; +import io.github.dsheirer.log.LoggingSuppressor; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** @@ -29,11 +29,15 @@ */ public class Broadcaster implements Listener { - private final static Logger mLog = LoggerFactory.getLogger(Broadcaster.class); + private static LoggingSuppressor sLoggingSuppressor; private List> mListeners = new CopyOnWriteArrayList<>(); + /** + * Constructs an instance + */ public Broadcaster() { + sLoggingSuppressor = new LoggingSuppressor(LoggerFactory.getLogger(Broadcaster.class)); } /** @@ -117,16 +121,25 @@ public void clear() */ public void broadcast(T t) { - try + for(Listener listener: mListeners) { - for(Listener listener: mListeners) + try { listener.receive(t); } - } - catch(Exception e) - { - mLog.error("Error while broadcasting [" + t.getClass() + "] to listeners"); + catch(Exception e) + { + if(t != null) + { + sLoggingSuppressor.error(t.getClass().toGenericString(), 5, + "Error while broadcasting [" + t.getClass() + "] to listeners", e); + } + else + { + sLoggingSuppressor.error("null broadcast object", 5, "Can't broadcast null " + + "object to listener [" + listener.getClass() + "]", e); + } + } } } }