Skip to content

Commit 4cbb457

Browse files
committed
Internal logging support
Add a logging abstraction over foreign logger backends. The internal logger supports JUL and SLF4J (Logback) loggers in this commit. Parametrized messages are supported via MessageFormat instead of relying on external loggers support. Closes: #194
1 parent b53e0ba commit 4cbb457

9 files changed

+486
-0
lines changed

pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
<junit.jupiter.version>5.4.2</junit.jupiter.version>
1111
<snakeyml.version>1.23</snakeyml.version>
1212
<mockito.version>1.10.19</mockito.version>
13+
<!-- logger dependency versions -->
14+
<sfl4j.version>1.7.26</sfl4j.version>
1315
</properties>
1416
<name>Tarantool Connector for Java</name>
1517
<url>https://github.com/tarantool/tarantool-java</url>
@@ -189,6 +191,12 @@
189191
<version>${snakeyml.version}</version>
190192
<scope>test</scope>
191193
</dependency>
194+
<dependency>
195+
<groupId>org.slf4j</groupId>
196+
<artifactId>slf4j-api</artifactId>
197+
<version>${sfl4j.version}</version>
198+
<scope>provided</scope>
199+
</dependency>
192200
</dependencies>
193201

194202
<parent>
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package org.tarantool.logging;
2+
3+
abstract class BaseLogger implements Logger {
4+
5+
public abstract void log(Level level, String text, Throwable error, Object... parameters);
6+
7+
public abstract boolean isLoggable(Level level);
8+
9+
@Override
10+
public void debug(String message) {
11+
log(Level.DEBUG, message, null);
12+
}
13+
14+
@Override
15+
public void debug(String format, Object... params) {
16+
log(Level.DEBUG, format, null, params);
17+
}
18+
19+
@Override
20+
public void debug(String message, Throwable throwable) {
21+
log(Level.DEBUG, message, throwable);
22+
}
23+
24+
@Override
25+
public void error(String message) {
26+
log(Level.ERROR, message, null);
27+
}
28+
29+
@Override
30+
public void error(String format, Object... params) {
31+
log(Level.ERROR, format, null, params);
32+
}
33+
34+
@Override
35+
public void error(String message, Throwable throwable) {
36+
log(Level.ERROR, message, throwable);
37+
}
38+
39+
@Override
40+
public void info(String message) {
41+
log(Level.INFO, message, null);
42+
}
43+
44+
@Override
45+
public void info(String format, Object... params) {
46+
log(Level.INFO, format, null, params);
47+
}
48+
49+
@Override
50+
public void info(String message, Throwable throwable) {
51+
log(Level.INFO, message, throwable);
52+
}
53+
54+
@Override
55+
public void trace(String message) {
56+
log(Level.TRACE, message, null);
57+
}
58+
59+
@Override
60+
public void trace(String format, Object... params) {
61+
log(Level.TRACE, format, null, params);
62+
}
63+
64+
@Override
65+
public void trace(String message, Throwable throwable) {
66+
log(Level.TRACE, message, throwable);
67+
}
68+
69+
@Override
70+
public void warn(String message) {
71+
log(Level.WARN, message, null);
72+
}
73+
74+
@Override
75+
public void warn(String format, Object... params) {
76+
log(Level.WARN, format, null, params);
77+
}
78+
79+
@Override
80+
public void warn(String message, Throwable throwable) {
81+
log(Level.WARN, message, throwable);
82+
}
83+
84+
@Override
85+
public boolean isDebugEnabled() {
86+
return isLoggable(Level.DEBUG);
87+
}
88+
89+
@Override
90+
public boolean isErrorEnabled() {
91+
return isLoggable(Level.ERROR);
92+
}
93+
94+
@Override
95+
public boolean isInfoEnabled() {
96+
return isLoggable(Level.INFO);
97+
}
98+
99+
@Override
100+
public boolean isTraceEnabled() {
101+
return isLoggable(Level.TRACE);
102+
}
103+
104+
@Override
105+
public boolean isWarnEnabled() {
106+
return isLoggable(Level.WARN);
107+
}
108+
109+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.tarantool.logging;
2+
3+
import java.util.logging.LogRecord;
4+
5+
/**
6+
* Adapter to JUL logger backend.
7+
* <p>
8+
* This class is not a part of public API.
9+
*
10+
* @see java.util.logging
11+
*/
12+
class JdkLogger extends BaseLogger {
13+
14+
private final java.util.logging.Logger delegate;
15+
16+
public JdkLogger(String loggerName) {
17+
this.delegate = java.util.logging.Logger.getLogger(loggerName);
18+
}
19+
20+
@Override
21+
public String getName() {
22+
return delegate.getName();
23+
}
24+
25+
@Override
26+
public void log(Level level, String text, Throwable error, Object... parameters) {
27+
if (!isLoggable(level)) {
28+
return;
29+
}
30+
31+
java.util.logging.Level julLevel = translate(level);
32+
LogRecord record = new LogRecord(julLevel, text);
33+
record.setLoggerName(delegate.getName());
34+
record.setResourceBundle(delegate.getResourceBundle());
35+
record.setResourceBundleName(delegate.getResourceBundleName());
36+
if (error != null) {
37+
record.setThrown(error);
38+
}
39+
record.setParameters(parameters);
40+
delegate.log(record);
41+
}
42+
43+
@Override
44+
public boolean isLoggable(Level level) {
45+
return delegate.isLoggable(translate(level));
46+
}
47+
48+
private java.util.logging.Level translate(Level level) {
49+
switch (level) {
50+
case ERROR:
51+
return java.util.logging.Level.SEVERE;
52+
case WARN:
53+
return java.util.logging.Level.WARNING;
54+
case INFO:
55+
return java.util.logging.Level.INFO;
56+
case DEBUG:
57+
return java.util.logging.Level.FINE;
58+
case TRACE:
59+
return java.util.logging.Level.FINEST;
60+
default:
61+
return java.util.logging.Level.ALL;
62+
}
63+
}
64+
65+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.tarantool.logging;
2+
3+
/**
4+
* Delegates a logger creation to JUL logger adapter.
5+
* <p>
6+
* This class is not a part of public API.
7+
*/
8+
public class JdkLoggerProvider implements LoggerProvider {
9+
10+
@Override
11+
public Logger getLogger(String name) {
12+
return new JdkLogger(name);
13+
}
14+
15+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.tarantool.logging;
2+
3+
/**
4+
* Minimal logger contract to perform internal
5+
* logging.
6+
* <p>
7+
* This class is not a part of public API.
8+
*/
9+
public interface Logger {
10+
11+
enum Level {
12+
ERROR,
13+
WARN,
14+
INFO,
15+
DEBUG,
16+
TRACE
17+
}
18+
19+
String getName();
20+
21+
boolean isErrorEnabled();
22+
23+
void error(final String message);
24+
25+
void error(final String format, final Object... params);
26+
27+
void error(final String message, Throwable throwable);
28+
29+
boolean isWarnEnabled();
30+
31+
void warn(final String message);
32+
33+
void warn(final String format, final Object... params);
34+
35+
void warn(final String message, Throwable throwable);
36+
37+
boolean isInfoEnabled();
38+
39+
void info(final String message);
40+
41+
void info(final String format, final Object... params);
42+
43+
void info(final String message, Throwable throwable);
44+
45+
boolean isDebugEnabled();
46+
47+
void debug(final String message);
48+
49+
void debug(final String format, final Object... params);
50+
51+
void debug(final String message, Throwable throwable);
52+
53+
boolean isTraceEnabled();
54+
55+
void trace(final String message);
56+
57+
void trace(final String format, final Object... params);
58+
59+
void trace(final String message, Throwable throwable);
60+
61+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package org.tarantool.logging;
2+
3+
import java.util.Iterator;
4+
import java.util.ServiceLoader;
5+
6+
/**
7+
* Basic service to load appropriate {@link LoggerProvider}
8+
* and obtain new loggers using loaded provider.
9+
* <p>
10+
* This class should be used as a start point to obtain the logger:
11+
* <pre> {@code
12+
* static final Logger LOGGER = LoggerFactory.getLogger(MyClass.java);
13+
* } </pre>
14+
* and then it can be used as a standard logger:
15+
* <pre> {@code
16+
* LOGGER.info("Service initialized");
17+
* } </pre>
18+
* <p>
19+
* There are four major attempts to load the logger provider:
20+
* <li>
21+
* Use a runtime system property {@literal org.tarantool.logging.provider}.
22+
* Possible values are 'slf4j' or 'jdk'. (for instance,
23+
* {@literal -Dorg.tarantool.logging.provider=slf4j}).
24+
* </li>
25+
* <li>
26+
* Use SPI mechanism to find the proper {@link LoggerProvider}.
27+
* </li>
28+
* <li>
29+
* Check the classpath in attempt to discover one of supported logger
30+
* backends.
31+
* </li>
32+
* <li>
33+
* Use JUL implementation if none of above attempts are successful.
34+
* </li>
35+
* <p>
36+
* This class is not a part of public API.
37+
*/
38+
public class LoggerFactory {
39+
40+
private static final String LOGGING_PROVIDER_KEY = "org.tarantool.logging.provider";
41+
private static final String LOGGING_PROVIDER_JDK = "jdk";
42+
private static final String LOGGING_PROVIDER_SLF4J = "slf4j";
43+
44+
private static LoggerProvider loggerProvider = loadLoggerProvider();
45+
46+
private static LoggerProvider loadLoggerProvider() {
47+
// use a runtime property to determine a logger provider name
48+
try {
49+
String providerName = System.getProperty(LOGGING_PROVIDER_KEY);
50+
if (providerName != null) {
51+
if (providerName.equalsIgnoreCase(LOGGING_PROVIDER_JDK)) {
52+
return tryLoadJdkProvider();
53+
} else if (providerName.equalsIgnoreCase(LOGGING_PROVIDER_SLF4J)) {
54+
return tryLoadSlf4jProvider();
55+
}
56+
}
57+
} catch (Throwable ignored) {
58+
// no-op
59+
}
60+
61+
// use SPI to pick a proper logger provider
62+
try {
63+
ServiceLoader<LoggerProvider> serviceLoader = ServiceLoader.load(LoggerProvider.class);
64+
Iterator<LoggerProvider> iterator = serviceLoader.iterator();
65+
while (loggerProvider == null && iterator.hasNext()) {
66+
try {
67+
return iterator.next();
68+
} catch (Throwable ignored) {
69+
// no-op
70+
}
71+
}
72+
} catch (Throwable ignored) {
73+
// no-op
74+
}
75+
76+
// check slf4j-logback existence in the class path
77+
try {
78+
Class.forName("ch.qos.logback.classic.Logger", false, LoggerFactory.class.getClassLoader());
79+
return tryLoadSlf4jProvider();
80+
} catch (Throwable ignored) {
81+
// no-op
82+
}
83+
84+
// use JUL logger as a default logger
85+
return tryLoadJdkProvider();
86+
}
87+
88+
89+
private static LoggerProvider tryLoadJdkProvider() {
90+
return new JdkLoggerProvider();
91+
}
92+
93+
private static LoggerProvider tryLoadSlf4jProvider() {
94+
return new Slf4jLoggerProvider();
95+
}
96+
97+
private LoggerFactory() {
98+
}
99+
100+
/**
101+
* Gets a logger provided by this factory.
102+
*
103+
* @param name logger name
104+
*
105+
* @return obtained logger
106+
*/
107+
public static Logger getLogger(String name) {
108+
return loggerProvider.getLogger(name);
109+
}
110+
111+
/**
112+
* Gets a logger provided by this factory.
113+
*
114+
* @param type target class
115+
*
116+
* @return obtained logger
117+
*/
118+
public static Logger getLogger(Class<?> type) {
119+
return loggerProvider.getLogger(type.getName());
120+
}
121+
122+
}

0 commit comments

Comments
 (0)