From 1bf5af5d7844a3fbecf4f7be088627e2f54acf94 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 25 Jul 2025 05:03:49 -0400 Subject: [PATCH] fix: logging, add: redis --- .../net/streamline/platform/BasePlugin.java | 2 +- dependencies.gradle | 5 +- .../main/java/singularity/Singularity.java | 8 +- .../configs/given/GivenConfigs.java | 13 +++ .../configs/given/RedisConfigHandler.java | 53 ++++++++++++ .../singularity/listeners/CosmicListener.java | 2 +- .../singularity/logging/CosmicLogHandler.java | 18 ---- .../logging/CosmicLogbackAppender.java | 15 ---- .../singularity/logging/LogCollector.java | 30 ++++--- .../logging/timers/LogPopTimer.java | 2 + .../java/singularity/redis/RedisClient.java | 85 +++++++++++++++++++ .../java/singularity/redis/RedisHandler.java | 45 ++++++++++ .../java/singularity/redis/RedisListener.java | 43 ++++++++++ .../java/singularity/redis/RedisMessage.java | 19 +++++ .../src/main/resources/redis-config.yml | 13 +++ .../net/streamline/platform/BasePlugin.java | 2 +- 16 files changed, 299 insertions(+), 56 deletions(-) create mode 100644 singularity-api/src/main/java/singularity/configs/given/RedisConfigHandler.java create mode 100644 singularity-api/src/main/java/singularity/redis/RedisClient.java create mode 100644 singularity-api/src/main/java/singularity/redis/RedisHandler.java create mode 100644 singularity-api/src/main/java/singularity/redis/RedisListener.java create mode 100644 singularity-api/src/main/java/singularity/redis/RedisMessage.java create mode 100644 singularity-api/src/main/resources/redis-config.yml diff --git a/bungee/src/main/java/net/streamline/platform/BasePlugin.java b/bungee/src/main/java/net/streamline/platform/BasePlugin.java index fdb86fe6..e7da062b 100644 --- a/bungee/src/main/java/net/streamline/platform/BasePlugin.java +++ b/bungee/src/main/java/net/streamline/platform/BasePlugin.java @@ -353,7 +353,7 @@ public static ConcurrentSkipListMap getPlayersByUUID() { @Override public Logger getLoggerLogger() { - return getLogger(); + return getProxy().getLogger(); } @Override diff --git a/dependencies.gradle b/dependencies.gradle index a9fe0c8f..cb49c036 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -5,6 +5,7 @@ ext { "org.pf4j:pf4j:3.10.0", "commons-codec:commons-codec:1.5", "ch.qos.logback:logback-classic:1.5.6", + 'redis.clients:jedis:6.0.0', ] SHADOW = [ "com.github.ben-manes.caffeine:caffeine:3.1.8", @@ -12,11 +13,13 @@ ext { "org.pf4j:pf4j:3.10.0", "commons-codec:commons-codec:1.5", "ch.qos.logback:logback-classic:1.5.6", + 'redis.clients:jedis:6.0.0', ] ANNO = [ "com.github.ben-manes.caffeine:caffeine:3.1.8", "com.github.Server-Utilities:TheBase:master-SNAPSHOT", - "org.pf4j:pf4j:3.10.0" + "org.pf4j:pf4j:3.10.0", + 'redis.clients:jedis:6.0.0', ] COMP_ONLY = [ ] diff --git a/singularity-api/src/main/java/singularity/Singularity.java b/singularity-api/src/main/java/singularity/Singularity.java index f08f8b32..4463db84 100644 --- a/singularity-api/src/main/java/singularity/Singularity.java +++ b/singularity-api/src/main/java/singularity/Singularity.java @@ -274,13 +274,7 @@ public Singularity(String identifier, S platform, U userManager, M messenger, IC public void setupLogger() { if (getPlatform().hasLoggerLogger()) { java.util.logging.Logger rootLogger = getPlatform().getLoggerLogger(); - while (rootLogger.getParent() != null) { - rootLogger = rootLogger.getParent(); - } - // Remove existing handlers to avoid duplicates (optional) -// for (java.util.logging.Handler handler : rootLogger.getHandlers()) { -// rootLogger.removeHandler(handler); -// } + // Add the custom handler CosmicLogHandler handler = new CosmicLogHandler(); rootLogger.addHandler(handler); diff --git a/singularity-api/src/main/java/singularity/configs/given/GivenConfigs.java b/singularity-api/src/main/java/singularity/configs/given/GivenConfigs.java index ad714122..4b9cfc91 100644 --- a/singularity-api/src/main/java/singularity/configs/given/GivenConfigs.java +++ b/singularity-api/src/main/java/singularity/configs/given/GivenConfigs.java @@ -8,6 +8,7 @@ import singularity.database.ConnectorSet; import singularity.database.CoreDBOperator; import singularity.database.servers.SavedServer; +import singularity.redis.RedisClient; import singularity.utils.UserUtils; import java.io.File; @@ -23,6 +24,8 @@ public class GivenConfigs { private static DatabaseConfigHandler databaseConfig; @Getter @Setter private static ServerConfigHandler serverConfig; + @Getter @Setter + private static RedisConfigHandler redisConfig; @Getter @Setter private static File punishmentFolder; @@ -30,12 +33,16 @@ public class GivenConfigs { @Getter @Setter private static CoreDBOperator mainDatabase; + @Getter @Setter + private static RedisClient redisClient; + public static void init() { setMainConfig(new MainConfigHandler()); setMainMessages(new MainMessagesHandler()); setWhitelistConfig(new WhitelistConfig()); setDatabaseConfig(new DatabaseConfigHandler()); setServerConfig(new ServerConfigHandler()); + setRedisConfig(new RedisConfigHandler()); try { ConnectorSet connectorSet = getDatabaseConfig().getConnectorSet(); @@ -46,6 +53,12 @@ public static void init() { } catch (Exception e) { e.printStackTrace(); } + + try { + setRedisClient(new RedisClient()); + } catch (Exception e) { + e.printStackTrace(); + } } public static void ensureFolders() { diff --git a/singularity-api/src/main/java/singularity/configs/given/RedisConfigHandler.java b/singularity-api/src/main/java/singularity/configs/given/RedisConfigHandler.java new file mode 100644 index 00000000..e8367be5 --- /dev/null +++ b/singularity-api/src/main/java/singularity/configs/given/RedisConfigHandler.java @@ -0,0 +1,53 @@ +package singularity.configs.given; + +import gg.drak.thebase.storage.resources.flat.simple.SimpleConfiguration; +import lombok.Getter; +import lombok.Setter; +import singularity.Singularity; + +@Getter @Setter +public class RedisConfigHandler extends SimpleConfiguration { + public RedisConfigHandler() { + super("redis-config.yml", Singularity.getInstance(), true); + } + + @Override + public void init() { + getHost(); + getPort(); + getUsername(); + getPassword(); + } + + // DATABASE + + public String getHost() { + reloadResource(); + + return getResource().getOrSetDefault("host", "localhost"); + } + + public int getPort() { + reloadResource(); + + return getResource().getOrSetDefault("port", 6379); + } + + public String getUsername() { + reloadResource(); + + return getResource().getOrSetDefault("username", "default"); + } + + public String getPassword() { + reloadResource(); + + return getResource().getOrSetDefault("password", "password"); + } + + public boolean isEnabled() { + reloadResource(); + + return getResource().getOrSetDefault("enabled", false); + } +} diff --git a/singularity-api/src/main/java/singularity/listeners/CosmicListener.java b/singularity-api/src/main/java/singularity/listeners/CosmicListener.java index 7561b6e8..cd8d5839 100644 --- a/singularity-api/src/main/java/singularity/listeners/CosmicListener.java +++ b/singularity-api/src/main/java/singularity/listeners/CosmicListener.java @@ -6,6 +6,6 @@ public class CosmicListener implements BaseEventListener { public CosmicListener() { - BaseEventHandler.bake(this, Singularity.getBaseModule()); + BaseEventHandler.bake(this, Singularity.getInstance()); } } diff --git a/singularity-api/src/main/java/singularity/logging/CosmicLogHandler.java b/singularity-api/src/main/java/singularity/logging/CosmicLogHandler.java index 543c9db6..65fc2ee2 100644 --- a/singularity-api/src/main/java/singularity/logging/CosmicLogHandler.java +++ b/singularity-api/src/main/java/singularity/logging/CosmicLogHandler.java @@ -2,19 +2,14 @@ import singularity.events.server.ServerLogTextEvent; -import java.io.ByteArrayOutputStream; import java.util.logging.LogRecord; import java.util.logging.StreamHandler; import java.util.logging.Level; public class CosmicLogHandler extends StreamHandler { - private final ByteArrayOutputStream baos; - public CosmicLogHandler() { // Set output to System.out by default super.setOutputStream(System.out); - - this.baos = new ByteArrayOutputStream(); } @Override @@ -44,18 +39,5 @@ public synchronized void publish(LogRecord record) { if (event.isCancelled()) { return; } - - // Capture the log message - String formattedMessage = getFormatter().format(record); - baos.write(formattedMessage.getBytes(), 0, formattedMessage.length()); - - // Pass to parent handler for actual logging - super.publish(record); - super.flush(); // Ensure immediate output - } - - // Method to get captured logs - public String getCapturedLogs() { - return baos.toString(); } } diff --git a/singularity-api/src/main/java/singularity/logging/CosmicLogbackAppender.java b/singularity-api/src/main/java/singularity/logging/CosmicLogbackAppender.java index 32c22c34..f869e585 100644 --- a/singularity-api/src/main/java/singularity/logging/CosmicLogbackAppender.java +++ b/singularity-api/src/main/java/singularity/logging/CosmicLogbackAppender.java @@ -5,11 +5,7 @@ import ch.qos.logback.core.AppenderBase; import singularity.events.server.ServerLogTextEvent; -import java.io.ByteArrayOutputStream; - public class CosmicLogbackAppender extends AppenderBase { - private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - @Override protected void append(ILoggingEvent eventObject) { Level level = eventObject.getLevel(); @@ -37,16 +33,5 @@ protected void append(ILoggingEvent eventObject) { if (event.isCancelled()) { return; } - - // Capture the log message - String message = eventObject.getFormattedMessage() + System.lineSeparator(); - baos.write(message.getBytes(), 0, message.length()); - - // Optionally forward to console - System.out.print(message); - } - - public String getCapturedLogs() { - return baos.toString(); } } \ No newline at end of file diff --git a/singularity-api/src/main/java/singularity/logging/LogCollector.java b/singularity-api/src/main/java/singularity/logging/LogCollector.java index 74bf17da..c342fd92 100644 --- a/singularity-api/src/main/java/singularity/logging/LogCollector.java +++ b/singularity-api/src/main/java/singularity/logging/LogCollector.java @@ -9,15 +9,22 @@ import singularity.logging.timers.LogPopTimer; import singularity.utils.MessageUtils; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.atomic.AtomicInteger; public class LogCollector extends CosmicListener { @Getter @Setter - private static ConcurrentLinkedQueue logQueue = new ConcurrentLinkedQueue<>(); + private static ConcurrentSkipListMap logQueue = new ConcurrentSkipListMap<>(); + + @Getter @Setter + private static AtomicInteger idCounter = new AtomicInteger(0); + + public static int getNextId() { + return idCounter.getAndIncrement(); + } public static void addLog(ServerLogTextEvent event) { - logQueue.add(event); + getLogQueue().put(getNextId(), event); } /** @@ -25,19 +32,18 @@ public static void addLog(ServerLogTextEvent event) { * @return A map of log entries where the key is the log ID and the value is the log message. */ public static ConcurrentSkipListMap getLogs() { - ConcurrentSkipListMap logs = new ConcurrentSkipListMap<>(); - int id = 0; - - while (! logQueue.isEmpty()) { - ServerLogTextEvent event = logQueue.poll(); - if (event != null) { - logs.put(id++, event); - } - } + ConcurrentSkipListMap logs = new ConcurrentSkipListMap<>(getLogQueue()); + + resetQueue(); return logs; } + public static void resetQueue() { + clearLogs(); + setIdCounter(new AtomicInteger(0)); + } + public static void clearLogs() { logQueue.clear(); } diff --git a/singularity-api/src/main/java/singularity/logging/timers/LogPopTimer.java b/singularity-api/src/main/java/singularity/logging/timers/LogPopTimer.java index bc03f427..b7b117d8 100644 --- a/singularity-api/src/main/java/singularity/logging/timers/LogPopTimer.java +++ b/singularity-api/src/main/java/singularity/logging/timers/LogPopTimer.java @@ -6,6 +6,8 @@ public class LogPopTimer extends AsyncTask { public LogPopTimer() { super(LogPopTimer::runTask, 0, 20 * 5); // Runs every 5 seconds + queue(); + start(); } public static void runTask(AsyncTask task) { diff --git a/singularity-api/src/main/java/singularity/redis/RedisClient.java b/singularity-api/src/main/java/singularity/redis/RedisClient.java new file mode 100644 index 00000000..879136d3 --- /dev/null +++ b/singularity-api/src/main/java/singularity/redis/RedisClient.java @@ -0,0 +1,85 @@ +package singularity.redis; + +import lombok.Getter; +import lombok.Setter; +import redis.clients.jedis.Jedis; +import singularity.configs.given.GivenConfigs; +import singularity.configs.given.RedisConfigHandler; +import singularity.utils.MessageUtils; + +import java.util.Optional; +import java.util.function.Consumer; + +public class RedisClient { + public static RedisConfigHandler getConfig() { + return GivenConfigs.getRedisConfig(); + } + + @Getter @Setter + private static Jedis jedisClient; + + public static String getHost() { + return getConfig().getHost(); + } + + public static int getPort() { + return getConfig().getPort(); + } + + public static String getUsername() { + return getConfig().getUsername(); + } + + public static String getPassword() { + return getConfig().getPassword(); + } + + public static boolean isEnabled() { + return getConfig().isEnabled(); + } + + public static Jedis getJedis() { + if (! isEnabled()) return null; + + if (getJedisClient() != null) { + return getJedisClient(); + } + + Jedis jedis = new Jedis(getHost(), getPort()); + String auth = jedis.auth(getUsername(), getPassword()); + if (auth != null) { + if (auth.equals("OK")) { + MessageUtils.logInfo("Redis authenticated successfully."); + } else { + MessageUtils.logInfo("Redis authentication failed: " + auth); + } + } + + String pingResponse = jedis.ping(); + MessageUtils.logInfo("Redis ping response: " + pingResponse); + + setJedisClient(jedis); + + return getJedisClient(); + } + + public static void withJedis(Consumer consumer) { + withJedis(consumer, true); + } + + public static void withJedis(Consumer consumer, boolean silent) { + if (isEnabled()) Optional.ofNullable(getJedis()).ifPresentOrElse(consumer, () -> { + if (! silent) { + MessageUtils.logWarning("Redis client is not initialized. Please check your Redis configuration."); + } + }); + } + + public static RedisClient getInstance() { + return GivenConfigs.getRedisClient(); + } + + public static void sendMessage(RedisMessage message) { + withJedis(j -> j.publish(message.getChannel(), message.getMessage())); + } +} diff --git a/singularity-api/src/main/java/singularity/redis/RedisHandler.java b/singularity-api/src/main/java/singularity/redis/RedisHandler.java new file mode 100644 index 00000000..bc01fcaf --- /dev/null +++ b/singularity-api/src/main/java/singularity/redis/RedisHandler.java @@ -0,0 +1,45 @@ +package singularity.redis; + +import lombok.Getter; +import lombok.Setter; + +import java.util.Optional; +import java.util.concurrent.ConcurrentSkipListSet; + +public class RedisHandler { + @Getter @Setter + private static ConcurrentSkipListSet listeners = new ConcurrentSkipListSet<>(); + + public static void load(RedisListener listener) { + unload(listener); + + listeners.add(listener); + } + + public static void unload(RedisListener listener) { + unload(listener.getIdentifier()); + } + + public static void unload(String identifier) { + listeners.removeIf(listener -> listener.getIdentifier().equals(identifier)); + } + + public static Optional get(String identifier) { + return listeners.stream() + .filter(listener -> listener.getIdentifier().equals(identifier)) + .findFirst(); + } + + public static boolean isLoaded(String identifier) { + return listeners.stream() + .anyMatch(listener -> listener.getIdentifier().equals(identifier)); + } + + public static boolean isLoaded(RedisListener listener) { + return isLoaded(listener.getIdentifier()); + } + + public static RedisListener getOrNull(String identifier) { + return get(identifier).orElse(null); + } +} diff --git a/singularity-api/src/main/java/singularity/redis/RedisListener.java b/singularity-api/src/main/java/singularity/redis/RedisListener.java new file mode 100644 index 00000000..7ea27c6e --- /dev/null +++ b/singularity-api/src/main/java/singularity/redis/RedisListener.java @@ -0,0 +1,43 @@ +package singularity.redis; + +import gg.drak.thebase.objects.Identifiable; +import lombok.Getter; +import lombok.Setter; +import redis.clients.jedis.JedisPubSub; +import singularity.utils.MessageUtils; + +@Getter @Setter +public abstract class RedisListener extends JedisPubSub implements Identifiable { + private String identifier; + + public RedisListener(String identifier) { + this.identifier = identifier; + + registerAndLoad(); + + MessageUtils.logInfo("&cRedisListener &fregistered: &d" + identifier); + } + + public void load() { + RedisHandler.load(this); + } + + public void unload() { + RedisHandler.unload(this); + } + + public boolean isLoaded() { + return RedisHandler.isLoaded(this); + } + + public void register() { + RedisClient.withJedis(j -> j.subscribe(this, getChannelsArray())); + } + + public void registerAndLoad() { + register(); + load(); + } + + abstract String[] getChannelsArray(); +} diff --git a/singularity-api/src/main/java/singularity/redis/RedisMessage.java b/singularity-api/src/main/java/singularity/redis/RedisMessage.java new file mode 100644 index 00000000..dc70e85d --- /dev/null +++ b/singularity-api/src/main/java/singularity/redis/RedisMessage.java @@ -0,0 +1,19 @@ +package singularity.redis; + +import lombok.Getter; +import lombok.Setter; + +@Getter @Setter +public class RedisMessage { + private String channel; + private String message; + + public RedisMessage(String channel, String message) { + this.channel = channel; + this.message = message; + } + + public void send() { + RedisClient.sendMessage(this); + } +} diff --git a/singularity-api/src/main/resources/redis-config.yml b/singularity-api/src/main/resources/redis-config.yml new file mode 100644 index 00000000..4cbaa38e --- /dev/null +++ b/singularity-api/src/main/resources/redis-config.yml @@ -0,0 +1,13 @@ +# Redis Configuration. +# Host - Hostname of the Redis server. +host: localhost +# Port - Port number of the Redis server. +port: 6379 +# Username - Redis username. +username: default +# Password - Redis password. +password: password +# Whether to use Redis or not. +# true = use Redis. +# false = do not use Redis. +enabled: false \ No newline at end of file diff --git a/spigot/src/main/java/net/streamline/platform/BasePlugin.java b/spigot/src/main/java/net/streamline/platform/BasePlugin.java index 54182c79..27014edc 100644 --- a/spigot/src/main/java/net/streamline/platform/BasePlugin.java +++ b/spigot/src/main/java/net/streamline/platform/BasePlugin.java @@ -528,7 +528,7 @@ public static Command getBukkitCommand(String name) { @Override public Logger getLoggerLogger() { - return getLogger(); + return Bukkit.getLogger(); } @Override