Skip to content
Closed
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
1 change: 0 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@
</execution>
</executions>
</plugin>

<!-- Configure the jar plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
12 changes: 6 additions & 6 deletions sdk/core/azure-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@
<version>2.2</version> <!-- {x-version-update;org.hamcrest:hamcrest-library;external_dependency} -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version> <!-- {x-version-update;org.slf4j:slf4j-simple;external_dependency} -->
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.slf4j</groupId>-->
<!-- <artifactId>slf4j-simple</artifactId>-->
<!-- <version>1.7.28</version> &lt;!&ndash; {x-version-update;org.slf4j:slf4j-simple;external_dependency} &ndash;&gt;-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

import com.azure.core.util.Configuration;
import com.azure.core.util.CoreUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.NOPLogger;

import static com.azure.core.implementation.LoggingUtil.getEnvironmentLoggingLevel;

Expand All @@ -36,6 +36,7 @@
*/
public class ClientLogger {
private final Logger logger;
private final java.util.logging.Logger defaultLogger;

/**
* Retrieves a logger for the passed class using the {@link LoggerFactory}.
Expand All @@ -50,9 +51,13 @@ public ClientLogger(Class<?> clazz) {
* Retrieves a logger for the passed class name using the {@link LoggerFactory}.
*
* @param className Class name creating the logger.
* @throws RuntimeException it is an error.
*/
public ClientLogger(String className) {
logger = LoggerFactory.getLogger(className);
System.setProperty("java.util.logging.config.file",
ClientLogger.class.getClassLoader().getResource("logging.properties").getPath());
defaultLogger = java.util.logging.Logger.getLogger(className);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't overwrite a system property. Use another means to configure the logger. Also, don't create a default logger unless necessary - you're paying the price here to create two loggers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can initial it into log method. Try to figure out if we can set the logLevel during runtime.

}

/**
Expand Down Expand Up @@ -131,10 +136,9 @@ public void error(String format, Object... args) {
* @param args Arguments for the message, if an exception is being logged last argument is the throwable.
*/
private void log(LogLevel logLevel, String format, Object... args) {
LogLevel environmentLoggingLevel = getEnvironmentLoggingLevel();

if (canLogAtLevel(logLevel, environmentLoggingLevel)) {
performLogging(logLevel, environmentLoggingLevel, false, format, args);
LogLevel rootLogLevel = loadSystemLogLevel();
if (canLogAtLevel(logLevel, rootLogLevel)) {
performLogging(logLevel, rootLogLevel, false, format, args);
}
}

Expand Down Expand Up @@ -162,34 +166,23 @@ public RuntimeException logExceptionAsError(RuntimeException runtimeException) {

private RuntimeException logException(RuntimeException runtimeException, LogLevel logLevel) {
Objects.requireNonNull(runtimeException, "'runtimeException' cannot be null.");
LogLevel rootLogLevel = loadSystemLogLevel();

LogLevel environmentLoggingLevel = getEnvironmentLoggingLevel();

if (canLogAtLevel(logLevel, environmentLoggingLevel)) {
performLogging(logLevel, environmentLoggingLevel, true, runtimeException.getMessage(), runtimeException);
if (canLogAtLevel(logLevel, rootLogLevel)) {
performLogging(logLevel, rootLogLevel, true, runtimeException.getMessage(), runtimeException);
}

return runtimeException;
}

/**
* Determines if the environment and logger support logging at the given log level.
*
* @param logLevel The {@link LogLevel} being validated as supported.
* @return Flag indicating if the environment and logger support logging at the given log level.
*/
public boolean canLogAtLevel(LogLevel logLevel) {
return canLogAtLevel(logLevel, getEnvironmentLoggingLevel());
}

/*
* Performs the logging.
*
* @param format formattable message.
* @param args Arguments for the message, if an exception is being logged last argument is the throwable.
*/
private void performLogging(LogLevel logLevel, LogLevel environmentLogLevel, boolean isExceptionLogging,
String format, Object... args) {
private void performLogging(LogLevel logLevel, LogLevel rootLogLevel,
boolean isExceptionLogging, String format, Object... args) {
// If the logging level is less granular than verbose remove the potential throwable from the args.
String throwableMessage = "";
if (doesArgsHaveThrowable(args)) {
Expand All @@ -207,11 +200,55 @@ private void performLogging(LogLevel logLevel, LogLevel environmentLogLevel, boo
* Environment is logging at a level higher than verbose, strip out the throwable as it would log its
* stack trace which is only expected when logging at a verbose level.
*/
if (environmentLogLevel.getLogLevel() > LogLevel.VERBOSE.getLogLevel()) {
if (rootLogLevel.getLogLevel() > LogLevel.VERBOSE.getLogLevel()) {
args = removeThrowable(args);
}
}
boolean isFromEnv = isLogFromEnv();
printInPlace(isFromEnv, throwableMessage, logLevel, format, args);

}

private void printInPlace(boolean isFromEnv, String throwableMessage, LogLevel logLevel, String format,
Object... args) {
if (isFromEnv) {
printFromDefaultLogger(throwableMessage, logLevel, format, args);
} else {
printFromLogger(throwableMessage, logLevel, format, args);
}
}

private void printFromDefaultLogger(String throwableMessage, LogLevel logLevel, String format, Object... args) {
switch (logLevel) {
case VERBOSE:
defaultLogger.log(java.util.logging.Level.FINE, format, args);
break;
case INFORMATIONAL:
defaultLogger.log(java.util.logging.Level.INFO, format, args);
break;
case WARNING:
if (!CoreUtils.isNullOrEmpty(throwableMessage)) {
format += System.lineSeparator() + throwableMessage;
}
defaultLogger.log(java.util.logging.Level.WARNING, format, args);
break;
case ERROR:
if (!CoreUtils.isNullOrEmpty(throwableMessage)) {
format += System.lineSeparator() + throwableMessage;
}
defaultLogger.log(java.util.logging.Level.SEVERE, format, args);
break;
default:
// Don't do anything, this state shouldn't be possible.
break;
}
}

private boolean isLogFromEnv() {
return logger instanceof NOPLogger;
}

private void printFromLogger(String throwableMessage, LogLevel logLevel, String format, Object... args) {
switch (logLevel) {
case VERBOSE:
logger.debug(format, args);
Expand All @@ -237,37 +274,44 @@ private void performLogging(LogLevel logLevel, LogLevel environmentLogLevel, boo
}
}

/*
* Determines if the environment and logger support logging at the given log level.
/**
* Determines if the app or environment logger support logging at the given log level.
*
* @param logLevel Logging level for the log message.
* @param environmentLoggingLevel Logging level the environment is set to support.
* @return Flag indicating if the environment and logger are configured to support logging at the given log level.
*/
private boolean canLogAtLevel(LogLevel logLevel, LogLevel environmentLoggingLevel) {
// Do not log if logLevel is null is not set.
if (logLevel == null) {
return false;
}
public boolean canLogAtLevel(LogLevel logLevel) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LogLevel is in an implementation package and will need to be moved to public package.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR is for reviewing the code flow, not finished yet.
Will merge the changes in PR: #6886.
Also, will fix the tests after merging.

LogLevel rootLogLevel = loadSystemLogLevel();
return canLogAtLevel(logLevel, rootLogLevel);
}

// Attempting to log at a level not supported by the environment.
if (logLevel.getLogLevel() < environmentLoggingLevel.getLogLevel()) {
return false;
}
private boolean canLogAtLevel(LogLevel logLevel, LogLevel allowedLogLevel) {
// Attempting to log at a level not supported by the SLF4J configuration or env variable.
return logLevel.getLogLevel() >= allowedLogLevel.getLogLevel();
}

// Determine if the logger configuration supports logging at the level.
switch (logLevel) {
case VERBOSE:
return logger.isDebugEnabled();
case INFORMATIONAL:
return logger.isInfoEnabled();
case WARNING:
return logger.isWarnEnabled();
case ERROR:
return logger.isErrorEnabled();
default:
return false;
/**
* Checking the system log with the preference order of slf4j and environment variable.
*
* @return The log level.
*/
private LogLevel loadSystemLogLevel() {
if (isLogFromEnv()) {
return getEnvironmentLoggingLevel();
}
if (logger.isDebugEnabled()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the binder still be checked? That way if an SLF4J binding exists but all logging levels are disabled we won't default into the environment configuration and instead wouldn't log anything as expected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my testing, if there is some other logging binding to SLF4J, logger.isDebugEnabled() will reflect the log level set by user. So if log4j set log level to info, then the clientLogger logger.canLogAtLevel(LogLevel.INFO) returns true.

Also, Binder check API is inside of slf4j-simple pkg which asks to introduce slf4j impl library to src code. This is not ideal as you comment above.

return LogLevel.VERBOSE;
}
if (logger.isInfoEnabled()) {
return LogLevel.INFORMATIONAL;
}
if (logger.isWarnEnabled()) {
return LogLevel.WARNING;
}
if (logger.isErrorEnabled()) {
return LogLevel.ERROR;
}
return LogLevel.NOT_SET;
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import java.util.Locale;

/**
* Enum which represent logging levels used in Azure SDKs.
* Enum which represent logging levels used in Azure SDKs.
*/
public enum LogLevel {
/**
Expand Down Expand Up @@ -65,7 +65,7 @@ public int getLogLevel() {
* Converts the passed log level string to the corresponding {@link LogLevel}.
*
* @param logLevelVal The log level value which needs to convert
* @return The LogLevel Enum if pass in the valid string.
* @return The LogLevel Enum if pass in the valid string.
* The valid strings for {@link LogLevel} are:
* <ul>
* <li>VERBOSE: "verbose", "debug"</li>
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/azure-core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
requires transitive org.reactivestreams;

requires transitive org.slf4j;

requires transitive java.logging;
// public API surface area
exports com.azure.core.annotation;
exports com.azure.core.credential;
Expand Down
3 changes: 3 additions & 0 deletions sdk/core/azure-core/src/main/resources/logging.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
.level = ALL
java.util.logging.ConsoleHandler.level = FINER
Loading