Skip to content

Commit c46f416

Browse files
committed
Add configurable agent logging backend
Signed-off-by: Arnab Nandy <arnab_nandy7@yahoo.com>
1 parent 06d82ef commit c46f416

14 files changed

Lines changed: 434 additions & 144 deletions

File tree

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright (C) The Prometheus jmx_exporter Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.prometheus.jmx.logger;
18+
19+
import java.util.logging.Formatter;
20+
import java.util.logging.Handler;
21+
import java.util.logging.SimpleFormatter;
22+
23+
/** Logging backend that delegates to Java Util Logging (JUL). */
24+
final class JulLoggerBackend implements LoggerBackend {
25+
26+
private static final String ROOT_LOGGER = "";
27+
28+
private final java.util.logging.Logger logger;
29+
30+
static {
31+
configureSimpleFormatters(java.util.logging.Logger.getLogger(ROOT_LOGGER));
32+
}
33+
34+
/**
35+
* Constructs a JUL backend for the specified logger name.
36+
*
37+
* @param loggerName logger name
38+
*/
39+
JulLoggerBackend(String loggerName) {
40+
logger = java.util.logging.Logger.getLogger(loggerName);
41+
configureSimpleFormatters(logger);
42+
}
43+
44+
private static void configureSimpleFormatters(java.util.logging.Logger logger) {
45+
for (Handler handler : logger.getHandlers()) {
46+
Formatter formatter = handler.getFormatter();
47+
if (formatter != null && formatter.getClass().getName().endsWith(SimpleFormatter.class.getName())) {
48+
handler.setFormatter(new LoggerFormatter());
49+
}
50+
}
51+
}
52+
53+
@Override
54+
public boolean isEnabled(Level level) {
55+
return logger.isLoggable(toJulLevel(level));
56+
}
57+
58+
@Override
59+
public void log(Level level, String message) {
60+
logger.log(toJulLevel(level), message);
61+
}
62+
63+
static java.util.logging.Level toJulLevel(Level level) {
64+
switch (level) {
65+
case TRACE:
66+
return java.util.logging.Level.FINEST;
67+
case INFO:
68+
return java.util.logging.Level.INFO;
69+
case WARN:
70+
return java.util.logging.Level.WARNING;
71+
case ERROR:
72+
return java.util.logging.Level.SEVERE;
73+
default:
74+
throw new IllegalArgumentException("Unknown logging level: " + level);
75+
}
76+
}
77+
}

collector/src/main/java/io/prometheus/jmx/logger/Level.java

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,38 +28,23 @@ public enum Level {
2828
* Trace level logging. This is the most verbose level and is used for detailed debugging
2929
* information.
3030
*/
31-
TRACE(java.util.logging.Level.FINEST),
31+
TRACE,
3232

3333
/**
3434
* Info level logging. This level is used for informational messages that highlight the progress
3535
* of the application.
3636
*/
37-
INFO(java.util.logging.Level.INFO),
37+
INFO,
3838

3939
/**
4040
* Warn level logging. This level is used for potentially harmful situations that should be
4141
* looked at.
4242
*/
43-
WARN(java.util.logging.Level.WARNING),
43+
WARN,
4444

4545
/**
4646
* Error level logging. This level is used for error events that might still allow the
4747
* application to continue running.
4848
*/
49-
ERROR(java.util.logging.Level.SEVERE);
50-
51-
private final java.util.logging.Level julLevel;
52-
53-
Level(java.util.logging.Level julLevel) {
54-
this.julLevel = julLevel;
55-
}
56-
57-
/**
58-
* Returns the corresponding {@link java.util.logging.Level} for this level.
59-
*
60-
* @return the java.util.logging.Level
61-
*/
62-
public java.util.logging.Level julLevel() {
63-
return julLevel;
64-
}
49+
ERROR
6550
}

collector/src/main/java/io/prometheus/jmx/logger/Logger.java

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,17 @@
2020

2121
import java.time.LocalDateTime;
2222
import java.time.format.DateTimeFormatter;
23-
import java.util.logging.Formatter;
24-
import java.util.logging.Handler;
25-
import java.util.logging.SimpleFormatter;
2623

2724
/**
2825
* Logger wrapper providing simple logging with TRACE, INFO, WARN, and ERROR levels.
2926
*
30-
* <p>This class wraps {@link java.util.logging.Logger} and provides:
27+
* <p>This class delegates to the configured logging backend and provides:
3128
*
3229
* <ul>
3330
* <li>Simple logging interface with format strings
31+
* <li>Native logging that does not initialize Java Util Logging (JUL)
32+
* <li>Optional JUL integration
3433
* <li>Developer debug mode for additional console output
35-
* <li>Custom formatter for consistent log message format
3634
* </ul>
3735
*
3836
* <p>Developer debug mode can be enabled via:
@@ -42,15 +40,15 @@
4240
* <li>System property: {@code -Djmx.prometheus.exporter.developer.debug=true}
4341
* </ul>
4442
*
45-
* <p>Thread-safety: This class is thread-safe. The underlying logger is thread-safe, and
46-
* date formatting uses a thread-safe {@link DateTimeFormatter}.
43+
* <p>Thread-safety: This class is thread-safe. Logging backends are responsible for serializing
44+
* writes where required, and date formatting uses a thread-safe {@link DateTimeFormatter}.
4745
*/
4846
public class Logger {
4947

5048
/**
51-
* The underlying Java util logger.
49+
* The configured logging backend.
5250
*/
53-
private final java.util.logging.Logger LOGGER;
51+
private final LoggerBackend backend;
5452

5553
/**
5654
* Cached logger name for developer debug output.
@@ -60,15 +58,15 @@ public class Logger {
6058
/**
6159
* Flag indicating if developer debug mode is enabled.
6260
*
63-
* <p>When enabled, logs are also written to stdout in addition to the normal logging
64-
* destination. Evaluated once at class load time.
61+
* <p>When enabled, logs are also written to stdout in addition to the configured logging
62+
* backend. Evaluated once at class load time.
6563
*/
6664
private static volatile boolean DEVELOPER_DEBUG =
6765
"true".equals(System.getenv("JMX_PROMETHEUS_EXPORTER_DEVELOPER_DEBUG"))
6866
|| "true".equals(System.getProperty("jmx.prometheus.exporter.developer.debug"));
6967

7068
/**
71-
* Thread-safe date formatter for log timestamps.
69+
* Thread-safe date formatter for developer debug timestamps.
7270
*/
7371
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
7472

@@ -78,16 +76,8 @@ public class Logger {
7876
* @param clazz the class for which to create a logger, must not be {@code null}
7977
*/
8078
Logger(Class<?> clazz) {
81-
LOGGER = java.util.logging.Logger.getLogger(clazz.getName());
82-
loggerName = LOGGER.getName();
83-
84-
// Override the default formatter for the logger if it is SimpleFormatter
85-
for (Handler handler : LOGGER.getHandlers()) {
86-
Formatter formatter = handler.getFormatter();
87-
if (null != formatter && formatter.getClass().getName().endsWith(SimpleFormatter.class.getName())) {
88-
handler.setFormatter(new LoggerFormatter());
89-
}
90-
}
79+
loggerName = clazz.getName();
80+
backend = LoggerFactory.createBackend(loggerName);
9181
}
9282

9383
/**
@@ -96,7 +86,7 @@ public class Logger {
9686
* @return {@code true} if TRACE logging is enabled, {@code false} otherwise
9787
*/
9888
public boolean isTraceEnabled() {
99-
return LOGGER.isLoggable(Level.TRACE.julLevel());
89+
return backend.isEnabled(Level.TRACE);
10090
}
10191

10292
/**
@@ -105,7 +95,7 @@ public boolean isTraceEnabled() {
10595
* @return {@code true} if INFO logging is enabled, {@code false} otherwise
10696
*/
10797
public boolean isInfoEnabled() {
108-
return LOGGER.isLoggable(Level.INFO.julLevel());
98+
return backend.isEnabled(Level.INFO);
10999
}
110100

111101
/**
@@ -114,7 +104,7 @@ public boolean isInfoEnabled() {
114104
* @return {@code true} if WARN logging is enabled, {@code false} otherwise
115105
*/
116106
public boolean isWarnEnabled() {
117-
return LOGGER.isLoggable(Level.WARN.julLevel());
107+
return backend.isEnabled(Level.WARN);
118108
}
119109

120110
/**
@@ -123,7 +113,7 @@ public boolean isWarnEnabled() {
123113
* @return {@code true} if ERROR logging is enabled, {@code false} otherwise
124114
*/
125115
public boolean isErrorEnabled() {
126-
return LOGGER.isLoggable(Level.ERROR.julLevel());
116+
return backend.isEnabled(Level.ERROR);
127117
}
128118

129119
/**
@@ -237,15 +227,14 @@ public void error(String format, Object... objects) {
237227
*
238228
* <p>Skips {@link String#format} since the message is already a plain string.
239229
*
240-
* @param level the level
230+
* @param level the level
241231
* @param message the pre-formatted message
242232
*/
243233
private void log(Level level, String message) {
244-
java.util.logging.Level julLevel = level.julLevel();
245-
boolean loggable = LOGGER.isLoggable(julLevel);
234+
boolean loggable = backend.isEnabled(level);
246235
boolean debug = DEVELOPER_DEBUG;
247236
if (loggable) {
248-
LOGGER.log(julLevel, message);
237+
backend.log(level, message);
249238
}
250239
if (debug) {
251240
developerDebug(level, message);
@@ -255,18 +244,17 @@ private void log(Level level, String message) {
255244
/**
256245
* Logs a formatted message at the given level.
257246
*
258-
* @param level the level
259-
* @param format the format string
247+
* @param level the level
248+
* @param format the format string
260249
* @param objects the objects to format
261250
*/
262251
private void log(Level level, String format, Object... objects) {
263-
java.util.logging.Level julLevel = level.julLevel();
264-
boolean loggable = LOGGER.isLoggable(julLevel);
252+
boolean loggable = backend.isEnabled(level);
265253
boolean debug = DEVELOPER_DEBUG;
266254
if (loggable || debug) {
267255
String message = format(format, objects);
268256
if (loggable) {
269-
LOGGER.log(julLevel, message);
257+
backend.log(level, message);
270258
}
271259
if (debug) {
272260
developerDebug(level, message);
@@ -275,7 +263,7 @@ private void log(Level level, String format, Object... objects) {
275263
}
276264

277265
/**
278-
* Writes a log message to stdout with timestamp, thread, level, and logger name.
266+
* Writes a developer debug message to stdout with timestamp, thread, level, and logger name.
279267
*/
280268
private void developerDebug(Level level, String message) {
281269
String timestamp = DATE_TIME_FORMATTER.format(LocalDateTime.now());
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (C) The Prometheus jmx_exporter Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.prometheus.jmx.logger;
18+
19+
/** Internal logging backend used by the Logger facade. */
20+
interface LoggerBackend {
21+
22+
/**
23+
* Returns whether a logging level is enabled.
24+
*
25+
* @param level logging level
26+
* @return {@code true} when the level is enabled
27+
*/
28+
boolean isEnabled(Level level);
29+
30+
/**
31+
* Logs a pre-formatted message.
32+
*
33+
* @param level logging level
34+
* @param message message to log
35+
*/
36+
void log(Level level, String message);
37+
}

0 commit comments

Comments
 (0)