Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

/**
* Simplified REST API client with retries, JSON POJO SerDe through Jackson and exception POJO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import com.databricks.sdk.support.InternalApi;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

@InternalApi
public class AzureCliCredentialsProvider implements CredentialsProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

@InternalApi
public class CliTokenSource implements TokenSource {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
import org.apache.commons.configuration2.INIConfiguration;
import org.apache.commons.configuration2.SubnodeConfiguration;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

@InternalApi
public class ConfigLoader {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.charset.StandardCharsets;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

@InternalApi
public class DatabricksCliCredentialsProvider implements CredentialsProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
import java.time.Duration;
import java.util.*;
import org.apache.http.HttpMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

public class DatabricksConfig {
private static final Logger LOG = LoggerFactory.getLogger(DatabricksConfig.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import com.google.common.base.Strings;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

/**
* The DefaultCredentialsProvider is the primary authentication handler for the Databricks SDK. It
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

@InternalApi
public class GoogleCredentialsCredentialsProvider implements CredentialsProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import com.google.auth.oauth2.ImpersonatedCredentials;
import java.io.IOException;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

@InternalApi
public class GoogleIdCredentialsProvider implements CredentialsProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

/**
* A CredentialsProvider that uses the API token from the command context to authenticate.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

@InternalApi
public class CommonsHttpClient implements HttpClient {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import com.databricks.sdk.support.InternalApi;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.databricks.sdk.core.logging.Logger;
import com.databricks.sdk.core.logging.LoggerFactory;

@InternalApi
abstract class AbstractErrorMapper {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package com.databricks.sdk.core.logging;

import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.LogRecord;

/** Delegates logging calls to a {@code java.util.logging.Logger}, translating SLF4J conventions. */
class JulLogger extends Logger {

private static final String LOGGING_PACKAGE = "com.databricks.sdk.core.logging.";

private final java.util.logging.Logger delegate;

private JulLogger(java.util.logging.Logger delegate) {
this.delegate = delegate;
}

static Logger create(Class<?> type) {
return create(type.getName());
}

static Logger create(String name) {
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger(name);
return new JulLogger(julLogger);
}

@Override
public boolean isDebugEnabled() {
return delegate.isLoggable(Level.FINE);
}

@Override
public void debug(String msg) {
log(Level.FINE, msg, null);
}

@Override
public void debug(String format, Object... args) {
log(Level.FINE, format, args);
}

@Override
public void info(String msg) {
log(Level.INFO, msg, null);
}

@Override
public void info(String format, Object... args) {
log(Level.INFO, format, args);
}

@Override
public void warn(String msg) {
log(Level.WARNING, msg, null);
}

@Override
public void warn(String format, Object... args) {
log(Level.WARNING, format, args);
}

@Override
public void error(String msg) {
log(Level.SEVERE, msg, null);
}

@Override
public void error(String format, Object... args) {
log(Level.SEVERE, format, args);
}

private void log(Level level, String format, Object[] args) {
if (!delegate.isLoggable(level)) {
return;
}
Throwable thrown = (args != null) ? extractThrowable(format, args) : null;
String message = (args != null) ? formatMessage(format, args) : format;
LogRecord record = new LogRecord(level, message);
record.setLoggerName(delegate.getName());
if (thrown != null) {
record.setThrown(thrown);
}
inferCaller(record);
delegate.log(record);
}

/**
* Sets the source class and method on a {@link LogRecord} by walking the call stack to find the
* first frame outside this logging package.
*
* <p>JUL normally infers caller information automatically by scanning the stack for the first
* frame after its own {@code java.util.logging.Logger} methods. Because {@code JulLogger} wraps
* the JUL logger, that automatic inference stops at {@code JulLogger} or its helper methods
* instead of reaching the actual SDK class that initiated the log call. Without this correction,
* every log record would be attributed to {@code JulLogger}, making JUL output useless for
* identifying the real call site.
*/
private static void inferCaller(LogRecord record) {
StackTraceElement[] stack = new Throwable().getStackTrace();
for (StackTraceElement frame : stack) {
if (!frame.getClassName().startsWith(LOGGING_PACKAGE)) {
record.setSourceClassName(frame.getClassName());
record.setSourceMethodName(frame.getMethodName());
return;
}
}
}

/**
* Replaces SLF4J-style {@code {}} placeholders with argument values, matching the semantics of
* SLF4J's {@code MessageFormatter.arrayFormat}:
*
* <ul>
* <li>A trailing {@link Throwable} is unconditionally excluded from formatting.
* <li>A backslash before {@code {}} escapes it as a literal {@code {}}.
* <li>Array arguments are rendered with {@link Arrays#deepToString}.
* <li>A {@code null} format string returns {@code null}.
* </ul>
*/
static String formatMessage(String format, Object[] args) {
if (format == null) {
return null;
}
if (args == null || args.length == 0) {
return format;
}
int usableArgs = args.length;
if (args[usableArgs - 1] instanceof Throwable) {
usableArgs--;
}
StringBuilder sb = new StringBuilder(format.length() + 32);
int argIdx = 0;
int i = 0;
while (i < format.length()) {
if (i + 1 < format.length() && format.charAt(i) == '{' && format.charAt(i + 1) == '}') {
if (i > 0 && format.charAt(i - 1) == '\\') {
sb.setLength(sb.length() - 1);
sb.append("{}");
} else if (argIdx < usableArgs) {
sb.append(renderArg(args[argIdx++]));
} else {
sb.append("{}");
}
i += 2;
} else {
sb.append(format.charAt(i));
i++;
}
}
return sb.toString();
}

private static String renderArg(Object arg) {
if (arg == null) {
return "null";
}
if (arg instanceof Object[]) {
return Arrays.deepToString((Object[]) arg);
}
if (arg.getClass().isArray()) {
return primitiveArrayToString(arg);
}
return arg.toString();
}

private static String primitiveArrayToString(Object array) {
if (array instanceof boolean[]) return Arrays.toString((boolean[]) array);
if (array instanceof byte[]) return Arrays.toString((byte[]) array);
if (array instanceof char[]) return Arrays.toString((char[]) array);
if (array instanceof short[]) return Arrays.toString((short[]) array);
if (array instanceof int[]) return Arrays.toString((int[]) array);
if (array instanceof long[]) return Arrays.toString((long[]) array);
if (array instanceof float[]) return Arrays.toString((float[]) array);
if (array instanceof double[]) return Arrays.toString((double[]) array);
return Arrays.deepToString(new Object[] {array});
}

/**
* Returns the last argument if it is a {@link Throwable}, unconditionally. This matches SLF4J's
* {@code NormalizedParameters.getThrowableCandidate}, which always extracts a trailing Throwable
* regardless of how many {@code {}} placeholders the format string contains.
*/
static Throwable extractThrowable(String format, Object[] args) {
if (args == null || args.length == 0) {
return null;
}
Object last = args[args.length - 1];
if (last instanceof Throwable) {
return (Throwable) last;
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.databricks.sdk.core.logging;

/**
* A {@link LoggerFactory} backed by {@code java.util.logging}. Always available on any JRE.
*
* <p>Use this when SLF4J is not desirable:
*
* <pre>{@code
* LoggerFactory.setDefault(JulLoggerFactory.INSTANCE);
* }</pre>
*/
public class JulLoggerFactory extends LoggerFactory {

public static final JulLoggerFactory INSTANCE = new JulLoggerFactory();

@Override
protected Logger newInstance(Class<?> type) {
return JulLogger.create(type);
}

@Override
protected Logger newInstance(String name) {
return JulLogger.create(name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.databricks.sdk.core.logging;

/**
* Logging contract used throughout the SDK.
*
* <p>Extend this class to provide a custom logging implementation, then register it via a custom
* {@link LoggerFactory} subclass and {@link LoggerFactory#setDefault}.
*/
public abstract class Logger {

public abstract boolean isDebugEnabled();

public abstract void debug(String msg);

public abstract void debug(String format, Object... args);

public abstract void info(String msg);

public abstract void info(String format, Object... args);

public abstract void warn(String msg);

public abstract void warn(String format, Object... args);

public abstract void error(String msg);

public abstract void error(String format, Object... args);
}
Loading