Skip to content

Commit 9ac10fc

Browse files
Add logging abstraction with SLF4J backend
1 parent 29672b6 commit 9ac10fc

File tree

6 files changed

+225
-0
lines changed

6 files changed

+225
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.databricks.sdk.core.logging;
2+
3+
/**
4+
* Logging contract used throughout the SDK.
5+
*
6+
* <p>Extend this class to provide a custom logging implementation, then register it via a custom
7+
* {@link LoggerFactory} subclass and {@link LoggerFactory#setDefault}.
8+
*/
9+
public abstract class Logger {
10+
11+
public abstract boolean isDebugEnabled();
12+
13+
public abstract void debug(String msg);
14+
15+
public abstract void debug(String format, Object... args);
16+
17+
public abstract void info(String msg);
18+
19+
public abstract void info(String format, Object... args);
20+
21+
public abstract void warn(String msg);
22+
23+
public abstract void warn(String format, Object... args);
24+
25+
public abstract void error(String msg);
26+
27+
public abstract void error(String format, Object... args);
28+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.databricks.sdk.core.logging;
2+
3+
/**
4+
* Creates and configures {@link Logger} instances for the SDK.
5+
*
6+
* <p>By default, logging goes through SLF4J. Users can override the backend programmatically
7+
* before creating any SDK client:
8+
*
9+
* <pre>{@code
10+
* LoggerFactory.setDefault(myCustomFactory);
11+
* WorkspaceClient ws = new WorkspaceClient();
12+
* }</pre>
13+
*
14+
* <p>Extend this class to provide a fully custom logging backend.
15+
*/
16+
public abstract class LoggerFactory {
17+
18+
private static volatile LoggerFactory defaultFactory = Slf4jLoggerFactory.INSTANCE;
19+
20+
/** Returns a logger for the given class, using the current default factory. */
21+
public static Logger getLogger(Class<?> type) {
22+
return getDefault().newInstance(type);
23+
}
24+
25+
/** Returns a logger with the given name, using the current default factory. */
26+
public static Logger getLogger(String name) {
27+
return getDefault().newInstance(name);
28+
}
29+
30+
/**
31+
* Overrides the logging backend used by the SDK.
32+
*
33+
* <p>Must be called before creating any SDK client or calling {@link #getLogger}. Loggers
34+
* already obtained will not be affected by subsequent calls.
35+
*/
36+
public static void setDefault(LoggerFactory factory) {
37+
if (factory == null) {
38+
throw new IllegalArgumentException("LoggerFactory must not be null");
39+
}
40+
defaultFactory = factory;
41+
}
42+
43+
static LoggerFactory getDefault() {
44+
return defaultFactory;
45+
}
46+
47+
/** Creates a new logger for the given class. */
48+
protected abstract Logger newInstance(Class<?> type);
49+
50+
/** Creates a new logger with the given name. */
51+
protected abstract Logger newInstance(String name);
52+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.databricks.sdk.core.logging;
2+
3+
/** Delegates all logging calls to an SLF4J {@code Logger}. */
4+
class Slf4jLogger extends Logger {
5+
6+
private final org.slf4j.Logger delegate;
7+
8+
private Slf4jLogger(org.slf4j.Logger delegate) {
9+
this.delegate = delegate;
10+
}
11+
12+
static Logger create(Class<?> type) {
13+
return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(type));
14+
}
15+
16+
static Logger create(String name) {
17+
return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(name));
18+
}
19+
20+
@Override
21+
public boolean isDebugEnabled() {
22+
return delegate.isDebugEnabled();
23+
}
24+
25+
@Override
26+
public void debug(String msg) {
27+
delegate.debug(msg);
28+
}
29+
30+
@Override
31+
public void debug(String format, Object... args) {
32+
delegate.debug(format, args);
33+
}
34+
35+
@Override
36+
public void info(String msg) {
37+
delegate.info(msg);
38+
}
39+
40+
@Override
41+
public void info(String format, Object... args) {
42+
delegate.info(format, args);
43+
}
44+
45+
@Override
46+
public void warn(String msg) {
47+
delegate.warn(msg);
48+
}
49+
50+
@Override
51+
public void warn(String format, Object... args) {
52+
delegate.warn(format, args);
53+
}
54+
55+
@Override
56+
public void error(String msg) {
57+
delegate.error(msg);
58+
}
59+
60+
@Override
61+
public void error(String format, Object... args) {
62+
delegate.error(format, args);
63+
}
64+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.databricks.sdk.core.logging;
2+
3+
/** A {@link LoggerFactory} backed by SLF4J. This is the default. */
4+
public class Slf4jLoggerFactory extends LoggerFactory {
5+
6+
public static final Slf4jLoggerFactory INSTANCE = new Slf4jLoggerFactory();
7+
8+
@Override
9+
protected Logger newInstance(Class<?> type) {
10+
return Slf4jLogger.create(type);
11+
}
12+
13+
@Override
14+
protected Logger newInstance(String name) {
15+
return Slf4jLogger.create(name);
16+
}
17+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.databricks.sdk.core.logging;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import org.junit.jupiter.api.AfterEach;
6+
import org.junit.jupiter.api.Test;
7+
8+
public class LoggerFactoryTest {
9+
10+
@AfterEach
11+
void resetFactory() {
12+
LoggerFactory.setDefault(Slf4jLoggerFactory.INSTANCE);
13+
}
14+
15+
@Test
16+
void defaultFactoryIsSLF4J() {
17+
Logger logger = LoggerFactory.getLogger(LoggerFactoryTest.class);
18+
assertNotNull(logger);
19+
logger.info("LoggerFactory defaultFactoryIsSLF4J test message");
20+
}
21+
22+
@Test
23+
void setDefaultRejectsNull() {
24+
assertThrows(IllegalArgumentException.class, () -> LoggerFactory.setDefault(null));
25+
}
26+
27+
@Test
28+
void getLoggerByNameWorks() {
29+
Logger logger = LoggerFactory.getLogger("com.example.Test");
30+
assertNotNull(logger);
31+
logger.info("getLoggerByNameWorks test message");
32+
}
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.databricks.sdk.core.logging;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
public class Slf4jLoggerTest {
8+
9+
@Test
10+
void createReturnsSlf4jLogger() {
11+
Logger logger = Slf4jLogger.create(Slf4jLoggerTest.class);
12+
assertNotNull(logger);
13+
assertTrue(logger instanceof Slf4jLogger);
14+
}
15+
16+
@Test
17+
void slf4jLoggerOperations() {
18+
Logger logger = Slf4jLogger.create(Slf4jLoggerTest.class);
19+
logger.debug("debug");
20+
logger.debug("debug {}", "arg");
21+
logger.info("info");
22+
logger.info("info {}", "arg");
23+
logger.warn("warn");
24+
logger.warn("warn {}", "arg");
25+
logger.error("error");
26+
logger.error("error {}", "arg");
27+
logger.error("error {} failed", "op", new RuntimeException("cause"));
28+
logger.isDebugEnabled();
29+
}
30+
31+
}

0 commit comments

Comments
 (0)