Skip to content

Commit ff90d66

Browse files
committed
refactor(CffuLogger): creates cffu logger instances with class instead of using static methods
- Replace static logger functions with instance-based methods. - Introduce per-class loggers using `CffuLogger#getLogger(Class<?>)`. - Update all callers to use instance-based logging for improved clarity and traceability.
1 parent 72c2af1 commit ff90d66

File tree

6 files changed

+109
-74
lines changed

6 files changed

+109
-74
lines changed

cffu-core/src/main/java/io/foldright/cffu2/ConcurrencyLimitExecutor.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.foldright.cffu2;
22

33
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4+
import io.foldright.cffu2.internal.CffuLogger;
45
import org.jetbrains.annotations.VisibleForTesting;
56

67
import javax.annotation.concurrent.GuardedBy;
@@ -13,8 +14,6 @@
1314

1415
import static io.foldright.cffu2.internal.CffuLogger.Level.ERROR;
1516
import static io.foldright.cffu2.internal.CffuLogger.Level.WARN;
16-
import static io.foldright.cffu2.internal.CffuLogger.log;
17-
import static io.foldright.cffu2.internal.CffuLogger.logException;
1817
import static java.lang.Thread.currentThread;
1918

2019

@@ -29,6 +28,7 @@
2928
@SuppressFBWarnings({"UL_UNRELEASED_LOCK", "AT_STALE_THREAD_WRITE_OF_PRIMITIVE",
3029
"AT_NONATOMIC_OPERATIONS_ON_SHARED_VARIABLE"})
3130
final class ConcurrencyLimitExecutor implements Executor {
31+
private static final CffuLogger logger = CffuLogger.getLogger(ConcurrencyLimitExecutor.class);
3232

3333
private final int maxConcurrency;
3434
private final Executor executor;
@@ -171,7 +171,7 @@ private void asyncWork() {
171171
} catch (Throwable e) { // sneaky checked exception
172172
// check for InterruptedEx from `task.run`, as other JVM languages may throw InterruptedEx
173173
if (e instanceof InterruptedException) interruptedDuringTask = true;
174-
logException(ERROR, "Exception while executing runnable " + task, e);
174+
logger.logException(ERROR, "Exception while executing runnable " + task, e);
175175
}
176176
}
177177
} finally {
@@ -191,16 +191,17 @@ private void asyncWork() {
191191
private void reportExceedWorkerCount() {
192192
// Check if worker count exceeds concurrency limit (should never happen)
193193
if (workerCount <= maxConcurrency) return;
194-
if (isPowerOfTwo(++exceedLimitTimes)) log(ERROR, exceedLimitTimes + " concurrency limit violation(s)"
194+
if (isPowerOfTwo(++exceedLimitTimes)) logger.log(ERROR, exceedLimitTimes + " concurrency limit violation(s)"
195195
+ " (current: " + workerCount + " > max: " + maxConcurrency + ") detected in " + this
196196
+ ". This should never happen - please report this issue to the cffu library!");
197197
}
198198

199199
@GuardedBy("lock")
200200
private void reportSyncRunning() {
201-
if (isPowerOfTwo(++syncRunTimes)) log(WARN, syncRunTimes + " synchronous execution(s) detected in " + this
202-
+ "; base executor runs task on caller thread (e.g. CallerRunsPolicy/DirectExecutor), which likely"
203-
+ " prevent reaching max concurrency (current: " + workerCount + ", max: " + maxConcurrency + ").");
201+
if (isPowerOfTwo(++syncRunTimes)) logger.log(WARN, syncRunTimes + " synchronous execution(s)"
202+
+ " detected in " + this + "; base executor runs task on caller thread"
203+
+ " (e.g. CallerRunsPolicy/DirectExecutor), which likely prevent reaching max concurrency"
204+
+ " (current: " + workerCount + ", max: " + maxConcurrency + ").");
204205
}
205206

206207
/**
@@ -226,7 +227,7 @@ public String toString() {
226227
@Override
227228
@SuppressWarnings("removal")
228229
protected void finalize() throws Throwable {
229-
if (!queue.isEmpty()) log(WARN, queue.size() + " queued task(s) remained"
230+
if (!queue.isEmpty()) logger.log(WARN, queue.size() + " queued task(s) remained"
230231
+ " when finalizing " + this + "; these tasks will be discarded!"
231232
+ " This indicates the base executor discarded tasks or shut down unexpectedly.");
232233
super.finalize();

cffu-core/src/main/java/io/foldright/cffu2/LLCF.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.foldright.cffu2;
22

33
import edu.umd.cs.findbugs.annotations.Nullable;
4+
import io.foldright.cffu2.internal.CffuLogger;
45
import io.foldright.cffu2.internal.CommonUtils;
56
import org.jetbrains.annotations.Contract;
67

@@ -17,7 +18,6 @@
1718
import static io.foldright.cffu2.CompletableFutureUtils.newIncompleteFuture;
1819
import static io.foldright.cffu2.CompletableFutureUtils.unwrapCfException;
1920
import static io.foldright.cffu2.internal.CffuLogger.Level.ERROR;
20-
import static io.foldright.cffu2.internal.CffuLogger.logUncaughtException;
2121
import static io.foldright.cffu2.internal.CommonUtils.containsInArray;
2222
import static io.foldright.cffu2.internal.CommonUtils.mapArray;
2323
import static java.lang.Thread.currentThread;
@@ -55,6 +55,13 @@ public final class LLCF {
5555
*/
5656
private static volatile int BLACK_HOLE = 0xCFF0;
5757

58+
// CAUTION: The initialization order of static fields matters. Do not place static fields
59+
// before their dependencies, as this will result in using uninitialized dependency values.
60+
//
61+
// Dependencies:
62+
// - IS_JAVA*_PLUS depends on BLACK_HOLE
63+
// - MIN_STAGE_CLASS depends on IS_JAVA9_PLUS
64+
5865
// `CompletableFuture.completedStage` have been the new method since java 9
5966
static final boolean IS_JAVA9_PLUS = methodExists(() -> CompletableFuture.completedStage(null));
6067
// `CompletableFuture.exceptionallyCompose` have been the new method since java 12
@@ -79,12 +86,7 @@ private static boolean methodExists(Supplier<?> methodCallCheck) {
7986
? CompletableFuture.completedStage(null).getClass()
8087
: null;
8188

82-
// CAUTION: The initialization order of static fields matters. Do not place static fields
83-
// before their dependencies, as this will result in using uninitialized dependency values.
84-
//
85-
// Dependencies:
86-
// - IS_JAVA*_PLUS depends on BLACK_HOLE
87-
// - MIN_STAGE_CLASS depends on IS_JAVA9_PLUS
89+
private static final CffuLogger logger = CffuLogger.getLogger(LLCF.class);
8890

8991
// endregion
9092
////////////////////////////////////////////////////////////////////////////////
@@ -271,7 +273,7 @@ private static <T> BiConsumer<T, Throwable> safePeekAction(BiConsumer<? super T,
271273
action.accept(v, ex);
272274
} catch (Throwable e1) {
273275
safeAddSuppressedEx(ex, e1);
274-
logUncaughtException(ERROR, where, e1);
276+
logger.logUncaughtException(ERROR, where, e1);
275277
}
276278
};
277279
}

cffu-core/src/main/java/io/foldright/cffu2/config/CffuConfiguration.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,39 @@
1111
*/
1212
public final class CffuConfiguration {
1313
/**
14-
* Sets the exception logging format for cffu operations.
14+
* Sets the exception logging format for cffu operations programmatically at runtime.
1515
* <p>
16-
* By default, (uncaught) exceptions are logged with their complete stack traces. The default logging behavior can be
17-
* configured through the system property {@code cffu.exception.log.format} at JVM startup with the following values:
18-
* <ul>
19-
* <li>{@code full}: Log the complete exception stack trace (default)</li>
20-
* <li>{@code short}: Log only the exception message</li>
21-
* <li>{@code none}: Suppress all exception logging</li>
22-
* </ul>
16+
* By default, uncaught exceptions are logged with their complete stack traces
17+
* (i.e. {@link ExceptionLogFormat#FULL}). The initial format can be configured via
18+
* the system property {@code cffu.exception.log.format} at JVM startup, with values:
19+
* {@code full}, {@code short}, or {@code none}.
2320
*
2421
* @see <a href="https://peps.python.org/pep-0020/">Errors should never pass silently. Unless explicitly silenced.</a>
22+
* @see ExceptionLogFormat
2523
*/
26-
public static void setExceptionLoggingFormat(ExceptionLoggingFormat format) {
27-
CffuLogger.setExceptionLoggingFormat(format);
24+
public static void setExceptionLogFormat(ExceptionLogFormat format) {
25+
CffuLogger.setExceptionLogFormat(format);
2826
}
2927

30-
public enum ExceptionLoggingFormat {FULL, SHORT, NONE}
28+
/**
29+
* the exception logging format for cffu operations.
30+
*
31+
* @see #setExceptionLogFormat
32+
*/
33+
public enum ExceptionLogFormat {
34+
/**
35+
* Log the complete exception stack trace (default)
36+
*/
37+
FULL,
38+
/**
39+
* Log only the exception message
40+
*/
41+
SHORT,
42+
/**
43+
* Suppress all exception logging
44+
*/
45+
NONE
46+
}
3147

3248
private CffuConfiguration() {}
3349
}

cffu-core/src/main/java/io/foldright/cffu2/eh/SwallowedExceptionHandleUtils.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import edu.umd.cs.findbugs.annotations.Nullable;
44
import io.foldright.cffu2.Cffu;
5+
import io.foldright.cffu2.internal.CffuLogger;
56
import io.foldright.cffu2.internal.CommonUtils;
67

78
import java.util.concurrent.CompletableFuture;
@@ -11,8 +12,6 @@
1112
import static io.foldright.cffu2.LLCF.*;
1213
import static io.foldright.cffu2.internal.CffuLogger.Level.ERROR;
1314
import static io.foldright.cffu2.internal.CffuLogger.Level.WARN;
14-
import static io.foldright.cffu2.internal.CffuLogger.logException;
15-
import static io.foldright.cffu2.internal.CffuLogger.logUncaughtException;
1615
import static io.foldright.cffu2.internal.CommonUtils.requireArrayAndEleNonNull;
1716
import static java.util.Objects.requireNonNull;
1817

@@ -29,6 +28,8 @@
2928
* @see <a href="https://peps.python.org/pep-0020/">Errors should never pass silently. Unless explicitly silenced.</a>
3029
*/
3130
public final class SwallowedExceptionHandleUtils {
31+
private static final CffuLogger logger = CffuLogger.getLogger(SwallowedExceptionHandleUtils.class);
32+
3233
/**
3334
* Handles all exceptions from multiple input {@code CompletionStage}s as swallowed exceptions,
3435
* using {@link #cffuSwallowedExceptionHandler()} and calling back it with {@code null} attachment.
@@ -143,7 +144,7 @@ public static ExceptionHandler cffuSwallowedExceptionHandler() {
143144

144145
private static final ExceptionHandler CFFU_SWALLOWED_EX_HANDLER = exInfo -> {
145146
String msg = "Swallowed exception of cf" + (exInfo.index + 1) + " at " + exInfo.where;
146-
logException(WARN, msg, exInfo.exception);
147+
logger.logException(WARN, msg, exInfo.exception);
147148
};
148149

149150
/**
@@ -169,7 +170,7 @@ private static void safeHandle(ExceptionInfo info, ExceptionHandler handler) {
169170
handler.handle(info);
170171
} catch (Throwable ex) {
171172
safeAddSuppressedEx(info.exception, ex);
172-
logUncaughtException(ERROR, "exceptionHandler(" + handler.getClass() + ")", ex);
173+
logger.logUncaughtException(ERROR, "exceptionHandler(" + handler.getClass() + ")", ex);
173174
}
174175
}
175176

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,92 @@
11
package io.foldright.cffu2.internal;
22

33
import edu.umd.cs.findbugs.annotations.Nullable;
4-
import io.foldright.cffu2.config.CffuConfiguration.ExceptionLoggingFormat;
4+
import io.foldright.cffu2.config.CffuConfiguration.ExceptionLogFormat;
55
import org.jetbrains.annotations.ApiStatus;
66
import org.jetbrains.annotations.VisibleForTesting;
77
import org.slf4j.spi.LocationAwareLogger;
88

99

1010
/**
11-
* <strong>Internal</strong> exception logging utility for the cffu library.
11+
* <strong>Internal</strong> logger for the cffu library.
1212
*
1313
* @author HuHao (995483610 at qq dot com)
1414
* @author Jerry Lee (oldratlee at gmail dot com)
15-
* @see io.foldright.cffu2.config.CffuConfiguration#setExceptionLoggingFormat
15+
* @see io.foldright.cffu2.config.CffuConfiguration#setExceptionLogFormat
1616
* @see <a href="https://peps.python.org/pep-0020/">Errors should never pass silently. Unless explicitly silenced.</a>
1717
*/
1818
@ApiStatus.Internal
1919
public final class CffuLogger {
2020
private static final String FQCN = CffuLogger.class.getName();
21-
private static final String CFFU_PACKAGE_NAME = FQCN.replaceFirst("(\\.[^.]*){2}$", "");
2221

23-
@VisibleForTesting
24-
static volatile ExceptionLoggingFormat exceptionLoggingFormat = initExceptionLoggingFormat();
22+
private final LoggerAdapter logger;
2523

26-
private static final LoggerAdapter logger = getLogger();
24+
private CffuLogger(LoggerAdapter logger) {this.logger = logger;}
2725

28-
public static void logException(Level level, String msg, Throwable ex) {
26+
public void logException(Level level, String msg, Throwable ex) {
2927
log0(level, msg, ex);
3028
}
3129

32-
public static void logUncaughtException(Level level, String where, Throwable ex) {
30+
public void logUncaughtException(Level level, String where, Throwable ex) {
3331
log0(level, "Uncaught exception occurred at " + where, ex);
3432
}
3533

36-
public static void log(Level level, String msg) {
34+
public void log(Level level, String msg) {
3735
log0(level, msg, null);
3836
}
3937

38+
@VisibleForTesting
39+
static volatile ExceptionLogFormat exceptionLogFormat = initExceptionLogFormat();
40+
41+
public static void setExceptionLogFormat(ExceptionLogFormat format) {exceptionLogFormat = format;}
42+
4043
@SuppressWarnings("StatementWithEmptyBody")
41-
private static void log0(Level level, String msg, @Nullable Throwable ex) {
42-
final ExceptionLoggingFormat format = exceptionLoggingFormat;
43-
if (format == ExceptionLoggingFormat.NONE) {
44+
private void log0(Level level, String msg, @Nullable Throwable ex) {
45+
// read volatile field once into local var for consistent use within this method
46+
final ExceptionLogFormat format = exceptionLogFormat;
47+
if (format == ExceptionLogFormat.NONE) {
4448
// pass silently when explicitly silenced.
45-
} else if (format == ExceptionLoggingFormat.SHORT) {
46-
if (ex != null) msg = msg + ", exception: " + ex;
47-
logger.log(level, msg + ", " + ex, null);
49+
} else if (format == ExceptionLogFormat.SHORT) {
50+
if (ex != null) msg += ", exception: " + ex;
51+
logger.log(level, msg, null);
4852
} else {
4953
logger.log(level, msg, ex);
5054
}
5155
}
5256

53-
public static void setExceptionLoggingFormat(ExceptionLoggingFormat format) {
54-
exceptionLoggingFormat = format;
55-
}
56-
57-
private static ExceptionLoggingFormat initExceptionLoggingFormat() {
57+
private static ExceptionLogFormat initExceptionLogFormat() {
5858
final String fullFormat = "full";
5959
final String shortFormat = "short";
6060
final String noneFormat = "none";
6161

6262
final String format = System.getProperty("cffu.exception.log.format", fullFormat);
6363
if (noneFormat.equalsIgnoreCase(format)) {
64-
return ExceptionLoggingFormat.NONE;
64+
return ExceptionLogFormat.NONE;
6565
} else if (shortFormat.equalsIgnoreCase(format)) {
66-
return ExceptionLoggingFormat.SHORT;
66+
return ExceptionLogFormat.SHORT;
6767
} else {
68-
return ExceptionLoggingFormat.FULL;
68+
return ExceptionLogFormat.FULL;
6969
}
7070
}
7171

7272
public enum Level {ERROR, WARN}
7373

74+
public static CffuLogger getLogger(Class<?> clazz) {
75+
return new CffuLogger(getLoggerAdapter(clazz));
76+
}
77+
78+
private static volatile boolean slf4jUnavailable = false;
79+
7480
/**
7581
* Returns a logger adapter that uses {@code SLF4J} if available, otherwise uses {@link java.util.logging}.
7682
*/
77-
private static LoggerAdapter getLogger() {
83+
private static LoggerAdapter getLoggerAdapter(Class<?> clazz) {
84+
if (slf4jUnavailable) return new JulLoggerAdapter(clazz);
7885
try {
79-
return new Slf4jLoggerAdapter();
86+
return new Slf4jLoggerAdapter(clazz);
8087
} catch (NoClassDefFoundError e) {
81-
return new JulLoggerAdapter();
88+
slf4jUnavailable = true;
89+
return new JulLoggerAdapter(clazz);
8290
}
8391
}
8492

@@ -87,7 +95,9 @@ private interface LoggerAdapter {
8795
}
8896

8997
private static final class Slf4jLoggerAdapter implements LoggerAdapter {
90-
private final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CFFU_PACKAGE_NAME);
98+
private final org.slf4j.Logger logger;
99+
100+
Slf4jLoggerAdapter(Class<?> clazz) {logger = org.slf4j.LoggerFactory.getLogger(clazz);}
91101

92102
@Override
93103
public void log(Level level, String msg, @Nullable Throwable ex) {
@@ -102,13 +112,16 @@ public void log(Level level, String msg, @Nullable Throwable ex) {
102112
}
103113

104114
private static final class JulLoggerAdapter implements LoggerAdapter {
105-
private final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(CFFU_PACKAGE_NAME);
115+
private final java.util.logging.Logger logger;
116+
117+
JulLoggerAdapter(Class<?> clazz) {logger = java.util.logging.Logger.getLogger(clazz.getName());}
106118

107119
@Override
108120
public void log(Level level, String msg, @Nullable Throwable ex) {
109-
logger.log(level == Level.ERROR ? java.util.logging.Level.SEVERE : java.util.logging.Level.WARNING, msg, ex);
121+
java.util.logging.Level lvl = level == Level.ERROR
122+
? java.util.logging.Level.SEVERE
123+
: java.util.logging.Level.WARNING;
124+
logger.log(lvl, msg, ex);
110125
}
111126
}
112-
113-
private CffuLogger() {}
114127
}

0 commit comments

Comments
 (0)