mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Add new "Collecting Logger", that can be activated to "collect" logs (e.g., in an internal list), for later access (e.g., by a test, to assert if things were logged).
This commit is contained in:
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.logging;
|
||||
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A log message, which can be "collected" by the QCollectingLogger.
|
||||
*******************************************************************************/
|
||||
public class CollectedLogMessage
|
||||
{
|
||||
private Level level;
|
||||
private String message;
|
||||
private Throwable exception;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public CollectedLogMessage()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for message
|
||||
*******************************************************************************/
|
||||
public String getMessage()
|
||||
{
|
||||
return (this.message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for message
|
||||
*******************************************************************************/
|
||||
public void setMessage(String message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for message
|
||||
*******************************************************************************/
|
||||
public CollectedLogMessage withMessage(String message)
|
||||
{
|
||||
this.message = message;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for exception
|
||||
*******************************************************************************/
|
||||
public Throwable getException()
|
||||
{
|
||||
return (this.exception);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for exception
|
||||
*******************************************************************************/
|
||||
public void setException(Throwable exception)
|
||||
{
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for exception
|
||||
*******************************************************************************/
|
||||
public CollectedLogMessage withException(Throwable exception)
|
||||
{
|
||||
this.exception = exception;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for level
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Level getLevel()
|
||||
{
|
||||
return level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for level
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setLevel(Level level)
|
||||
{
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for level
|
||||
**
|
||||
*******************************************************************************/
|
||||
public CollectedLogMessage withLevel(Level level)
|
||||
{
|
||||
this.level = level;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public JSONObject getMessageAsJSONObject() throws JSONException
|
||||
{
|
||||
return (new JSONObject(getMessage()));
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.logging;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.Marker;
|
||||
import org.apache.logging.log4j.message.Message;
|
||||
import org.apache.logging.log4j.message.ObjectMessage;
|
||||
import org.apache.logging.log4j.message.SimpleMessageFactory;
|
||||
import org.apache.logging.log4j.simple.SimpleLogger;
|
||||
import org.apache.logging.log4j.util.PropertiesUtil;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QQQ log4j implementation, used within a QLogger, to "collect" log messages
|
||||
** in an internal list - the idea being - for tests, to assert that logs happened.
|
||||
*******************************************************************************/
|
||||
public class QCollectingLogger extends SimpleLogger
|
||||
{
|
||||
private List<CollectedLogMessage> collectedMessages = new ArrayList<>();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// just in case one of these gets activated, and left on, put a limit on how many messages we'll collect //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
private int capacity = 100;
|
||||
|
||||
private Logger logger;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QCollectingLogger(Logger logger)
|
||||
{
|
||||
super(logger.getName(), logger.getLevel(), false, false, true, false, "", new SimpleMessageFactory(), new PropertiesUtil(new Properties()), System.out);
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void logMessage(String fqcn, Level level, Marker marker, Message message, Throwable throwable)
|
||||
{
|
||||
////////////////////////////////////////////
|
||||
// add this log message to our collection //
|
||||
////////////////////////////////////////////
|
||||
collectedMessages.add(new CollectedLogMessage()
|
||||
.withLevel(level)
|
||||
.withMessage(message.getFormattedMessage())
|
||||
.withException(throwable));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if we've gone over our capacity, remove the 1st entry until we're back at capacity //
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
while(collectedMessages.size() > capacity)
|
||||
{
|
||||
collectedMessages.remove(0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// update the message that we log to indicate that we collected it. //
|
||||
// if it looks like JSON, insert as a name:value pair; else text. //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
String formattedMessage = message.getFormattedMessage();
|
||||
String updatedMessage;
|
||||
if(formattedMessage.startsWith("{"))
|
||||
{
|
||||
updatedMessage = """
|
||||
{"collected":true,""" + formattedMessage.substring(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
updatedMessage = "[Collected] " + formattedMessage;
|
||||
}
|
||||
ObjectMessage myMessage = new ObjectMessage(updatedMessage);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// log the message with the original log4j logger, with our slightly updated message //
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
logger.logMessage(level, marker, fqcn, null, myMessage, throwable);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for logger
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setLogger(Logger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for collectedMessages
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<CollectedLogMessage> getCollectedMessages()
|
||||
{
|
||||
return collectedMessages;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void clear()
|
||||
{
|
||||
this.collectedMessages.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for capacity
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCapacity(int capacity)
|
||||
{
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
}
|
@ -119,6 +119,34 @@ public class QLogger
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QCollectingLogger activateCollectingLoggerForClass(Class<?> c)
|
||||
{
|
||||
Logger loggerFromLogManager = LogManager.getLogger(c);
|
||||
QCollectingLogger collectingLogger = new QCollectingLogger(loggerFromLogManager);
|
||||
|
||||
QLogger qLogger = getLogger(c);
|
||||
qLogger.setLogger(collectingLogger);
|
||||
|
||||
return collectingLogger;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void deactivateCollectingLoggerForClass(Class<?> c)
|
||||
{
|
||||
Logger loggerFromLogManager = LogManager.getLogger(c);
|
||||
QLogger qLogger = getLogger(c);
|
||||
qLogger.setLogger(loggerFromLogManager);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -518,7 +546,7 @@ public class QLogger
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String makeJsonString(String message, Throwable t, List<LogPair> logPairList)
|
||||
protected String makeJsonString(String message, Throwable t, List<LogPair> logPairList)
|
||||
{
|
||||
if(logPairList == null)
|
||||
{
|
||||
@ -620,4 +648,15 @@ public class QLogger
|
||||
exceptionList.get(0).setHasLoggedLevel(level);
|
||||
return (level);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for logger
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void setLogger(Logger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.logging;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for QCollectingLogger
|
||||
*******************************************************************************/
|
||||
class QCollectingLoggerTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test()
|
||||
{
|
||||
ClassThatLogsThings classThatLogsThings = new ClassThatLogsThings();
|
||||
classThatLogsThings.logAnInfo("1");
|
||||
|
||||
QCollectingLogger collectingLogger = QLogger.activateCollectingLoggerForClass(ClassThatLogsThings.class);
|
||||
classThatLogsThings.logAnInfo("2");
|
||||
classThatLogsThings.logAWarn("3");
|
||||
QLogger.deactivateCollectingLoggerForClass(ClassThatLogsThings.class);
|
||||
|
||||
classThatLogsThings.logAWarn("4");
|
||||
|
||||
assertEquals(2, collectingLogger.getCollectedMessages().size());
|
||||
|
||||
assertThat(collectingLogger.getCollectedMessages().get(0).getMessage()).contains("""
|
||||
"message":"2",""");
|
||||
assertEquals("2", collectingLogger.getCollectedMessages().get(0).getMessageAsJSONObject().getString("message"));
|
||||
assertEquals(Level.INFO, collectingLogger.getCollectedMessages().get(0).getLevel());
|
||||
|
||||
assertThat(collectingLogger.getCollectedMessages().get(1).getMessage()).contains("""
|
||||
"message":"3",""");
|
||||
assertEquals(Level.WARN, collectingLogger.getCollectedMessages().get(1).getLevel());
|
||||
assertEquals("3", collectingLogger.getCollectedMessages().get(1).getMessageAsJSONObject().getString("message"));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class ClassThatLogsThings
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(ClassThatLogsThings.class);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void logAnInfo(String message)
|
||||
{
|
||||
LOG.info(message);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void logAWarn(String message)
|
||||
{
|
||||
LOG.warn(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user